It can be frustrating when AWS puts out statements like “Amazon DynamoDB local now supports the AWS SDK for Java 2.x”, but provides no information on how to use it, or what that even means. This article will show how to use DynamoDb Local with the new DynamoDbEnhanced clients in the AWS SDK for Java 2.x.

The DynamoDB enhanced client is a high-level library that is part of the AWS SDK for Java 2.x. It offers a straightforward way to map client-side classes to DynamoDB tables. If you’re familiar with DynamoDbMapper from v1 of the SDK, this is basically the same thing, but in v2.

With DynamoDbLocal, you can develop and test applications without accessing the DynamoDB web service. Instead, the database is self-contained on your computer. Having this local version helps you save on throughput, data storage, and data transfer fees. In addition, you don’t need an internet connection while you develop your application.

DynamoDbLocal can be run as a server in a separate program, or embedded in the java program you’re writing. This guide will focus on the embedded case, as it is more suitable for unit testing.

If you already have DynamoDbLocal working, and you just want to know how to make it work with an @DynamoDbBean annotated class, this is the relevant code.

DynamoDbClient ddb = DynamoDBEmbedded.create().dynamoDbClient();
DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()
        .dynamoDbClient(ddb)
        .build();
DynamoDbTable<Customer> customerTable = enhancedClient.table("Customer", TableSchema.fromBean(Customer.class));
customerTable.createTable();
// Now, pass the table into the class you want to test with dependency inversion.

Setting up DynamoDbLocal

Add DynamoDbLocal to your pom.xml. You’ll also need a build plugin to handle the sqlite4java dependencies.

<!--Dependency:-->
<dependencies>
    <dependency>
       <groupId>com.amazonaws</groupId>
       <artifactId>DynamoDBLocal</artifactId>
       <version>[1.12,2.0)</version>
    </dependency>
</dependencies>
<!--Custom repository:-->
<repositories>
    <repository>
       <id>dynamodb-local-oregon</id>
       <name>DynamoDB Local Release Repository</name>
       <url>https://s3-us-west-2.amazonaws.com/dynamodb-local/release</url>
    </repository>
</repositories>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <version>2.10</version>
            <executions>
                <execution>
                    <id>copy</id>
                    <phase>test-compile</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                    <configuration>
                        <includeScope>test</includeScope>
                        <includeTypes>so,dll,dylib</includeTypes>
                        <outputDirectory>${project.basedir}/native-libs</outputDirectory>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Before doing DynamoDBEmbedded.create() , you’ll need to set the following system property. I recommend setting it statically in your test class.

static {
  System.setProperty("sqlite4java.library.path", "native-libs");
}

If this is not set before you create your embedded client, you’ll get the following sqlite4java error

WARNING: [sqlite] cannot open DB[1]: com.almworks.sqlite4java.SQLiteException: [-91] cannot load library: java.lang.UnsatisfiedLinkError: no sqlite4java-win32-x64-1.0.392 in java.library.path
...
com.almworks.sqlite4java.SQLiteException: [-91] cannot load library: java.lang.UnsatisfiedLinkError: no sqlite4java-win32-x64-1.0.392 in java.library.path
  ...

Using DynamoDB Local

Here’s a Customer class which is a @DynamoDbBean

@Data
@DynamoDbBean
public class Customer {
    private String id;
    private String name;
    private String email;
    private Instant regDate;

    @DynamoDbPartitionKey
    public String getId() {
        return this.id;
    }

    @DynamoDbSortKey
    public String getEmail() {
        return this.email;
    }

    @Override
    public String toString() {
        return "Customer [id=" + id + ", name=" + name + ", email=" + email + ", regDate=" + regDate + "]";
    }
}

To test this class, you’ll need to create a DynamoDbClient from the Embedded instance, and pass that client into the DynamoDbEnhancedClient builder.

public class CustomerTest {
    static DynamoDbTable<Customer> customerTable;

    static {
        System.setProperty("sqlite4java.library.path", "native-libs");
    }

    @BeforeAll
    public static void setup() {
        DynamoDbClient ddb = DynamoDBEmbedded.create().dynamoDbClient();
        DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()
                .dynamoDbClient(ddb)
                .build();
        customerTable = enhancedClient.table("Customer", TableSchema.fromBean(Customer.class));
        // Create the table
        customerTable.createTable();
    }

    @Test
    void smokeTest() {
        // Create a customer
        var customer = new Customer();
        customer.setId("testId");
        customer.setName("testName");
        customer.setEmail("testEmail");
        customer.setRegDate(Instant.now());

        // Insert them into the database
        customerTable.putItem(customer);
        // Verify that it has been inserted
        var key = Key.builder().partitionValue("testId").sortValue("testEmail").build();
        var foundCustomer = customerTable.getItem(key);
        assertEquals(customer, foundCustomer);
    }
}

From this point, you can pass this DynamoDbTable<Customer> into any class you like to test, as long as you’re using dependency inversion.

Appendix