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
-
Amazon guide on setting up DynamoDbLocal: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html
-
Stackoverflow post with some more info on setting up DynamoDbLocal: https://stackoverflow.com/questions/26901613/easier-dynamodb-local-testing
-
Amazon guide on using the DynamoDbEnhanced client: https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/examples-dynamodb-enhanced.html