March 19, 2024

The ContactSunny Blog

Tech from one dev to another

Integrate AWS DynamoDB with Spring Boot

5 min read
dynamodb without text

Here is another POC to add to the growing list of POCs on my Github profile. Today, we’ll see how to integrate AWS DynamoDB with a Spring Boot application. This is going to be super simple, thanks to the AWS Java SDK and the Spring Data DynamoDB package. Let’s get started then.


Dependencies

First, as usual, we need to create a Spring Boot project, the dependencies of which look like:

<dependencies>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-dynamodb</artifactId>
<version>1.11.573</version>
</dependency>
<dependency>
<groupId>com.github.derjust</groupId>
<artifactId>spring-data-dynamodb</artifactId>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>2.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j</artifactId>
<version>2.11.2</version>
</dependency>

</dependencies>

Some of the packages here are used only for logging. So you don’t have to worry about why they’re there.


Configuration

There are two pieces of configuration we need to take care of for the integration to work.

1. AWS Credentials configuration

For this, we just create a Bean which returns a BasicAWSCredentials object, which accepts the access key and secret key as constructor parameter:

@Bean
private AWSCredentials amazonAWSCredentials() {
return new BasicAWSCredentials(awsAccessKey, awsSecretKey);
}

There are other ways in which we can create or provide the AWS SDK with the credentials. But for a POC, this will do just fine.

2. DynamoDB configuration

We again create a Bean for this, which returns an object of class AmazonDynamoDB. We need the DynamoDB endpoint for this. Amazon provides a list of endpoints for each region available. You can get the list here. So depending on which region you’ve created your DB in, you can just copy-paste the endpoint from that page into your properties or YAML file in the project. The code snippet for this configuration looks something like this:

@Bean
public AmazonDynamoDB amazonDynamoDB() {
AmazonDynamoDB amazonDynamoDB
= new AmazonDynamoDBClient(amazonAWSCredentials());

if (!StringUtils.isEmpty(dynamoDbEndpoint)) {
amazonDynamoDB.setEndpoint(dynamoDbEndpoint);
}

return amazonDynamoDB;
}

Make sure that the class in which you defined these beans is annotated with the @Configuration annotation. Also, to enable DynamoDB, you need to use the @EnableDynamoDBRepositories annotation, and pass the base package for the repositories that you’re going to create. The class looks like this:

@Configuration
@EnableDynamoDBRepositories
(basePackages = "com.contactsunny.poc.DynamoDBSpringBootPOC.repositories")
public class DynamoDBConfig {

@Value("${amazon.dynamodb.endpoint}")
private String dynamoDbEndpoint;

@Value("${amazon.aws.accessKey}")
private String awsAccessKey;

@Value("${amazon.aws.secretKey}")
private String awsSecretKey;

@Bean
public AmazonDynamoDB amazonDynamoDB() {
AmazonDynamoDB amazonDynamoDB
= new AmazonDynamoDBClient(amazonAWSCredentials());

if (!StringUtils.isEmpty(dynamoDbEndpoint)) {
amazonDynamoDB.setEndpoint(dynamoDbEndpoint);
}

return amazonDynamoDB;
}

@Bean
private AWSCredentials amazonAWSCredentials() {
return new BasicAWSCredentials(awsAccessKey, awsSecretKey);
}
}

Models (Entities or POJOs)

Once we’re done with the configuration, we have to start defining our models, or entity classes. This is similar to what you’d do with MongoDB models or MySQL models. The only difference, you have different annotations. So, to mark a POJO class as a data model or a DynomoDB entity, you’ll use the @DynamoDBTable annotation, and pass the table name as a parameter, something like this:

@DynamoDBTable(tableName = "awsServices")

In most other DB integrations, you’d annotate the declaration of a variable as attributes. But with DynamoDB, you have to annotate the getter methods to declare a class property as a DynamoDB attribute. So for this particular model, the class looks like this:

@DynamoDBTable(tableName = "awsServices")
public class AwsService {

private String id;
private String serviceName;
private String serviceDescription;
private String serviceHomePageUrl;

@DynamoDBHashKey
@DynamoDBAutoGeneratedKey
public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

@DynamoDBAttribute
public String getServiceName() {
return serviceName;
}

public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}

@DynamoDBAttribute
public String getServiceDescription() {
return serviceDescription;
}

public void setServiceDescription(String serviceDescription) {
this.serviceDescription = serviceDescription;
}

@DynamoDBAttribute
public String getServiceHomePageUrl() {
return serviceHomePageUrl;
}

public void setServiceHomePageUrl(String serviceHomePageUrl) {
this.serviceHomePageUrl = serviceHomePageUrl;
}
}

As you can see, if you miss the @DynamoDBAutoGeneratedKey annotation for the ID attribute, the system will not create an ID for you, you’ll be responsible for maintaining unique IDs for your objects.


Repositories

This is similar to any other Spring Data repository that you’ve created in the past. I’ll not explain anything here and just give you the code snippet.

@EnableScan
public interface AwsServiceRepository extends CrudRepository<AwsService, String> {
}

Here, AwsService is the model or entity class that we created earlier.


Read and Write to the DB

Now comes the fun part. To start reading data from and writing data to your DynamoDB, you need to get an instance of the DB first. For this, we need to @Autowire the AmazonDynamoDB Bean that we created in our configuration class, and the AwsServiceRepository that we created. After this, we create an object of the class DynamoDBMapper, which will help us create tables on the fly using a CreateTableRequest instance. Let’s see how it’s done:

private DynamoDBMapper dynamoDBMapper;

@Autowired
private AmazonDynamoDB amazonDynamoDB;

@Autowired
private AwsServiceRepository awsServiceRepository;
.
.
.
dynamoDBMapper = new DynamoDBMapper(amazonDynamoDB);

CreateTableRequest tableRequest = dynamoDBMapper
        .generateCreateTableRequest(AwsService.class);

tableRequest.setProvisionedThroughput(
        new ProvisionedThroughput(1L, 1L));

TableUtils.createTableIfNotExists(amazonDynamoDB, tableRequest);

Once the table is created, we can use the repository instance we have autowired to read and write to the DB:

AwsService awsService = new AwsService();

awsService.setServiceName("AWS DynamoDB");
awsService.setServiceHomePageUrl("https://aws.amazon.com/dynamodb/?nc2=h_m1");

awsService = awsServiceRepository.save(awsService);

logger.info("Saved AwsService object: " + new Gson().toJson(awsService));

String awsServiceId = awsService.getId();

logger.info("AWS Service ID: " + awsServiceId);

Optional<AwsService> awsServiceQueried = awsServiceRepository.findById(awsServiceId);

if (awsServiceQueried.get() != null) {
logger.info("Queried object: " + new Gson().toJson(awsServiceQueried.get()));
}

Iterable<AwsService> awsServices = awsServiceRepository.findAll();

for (AwsService awsServiceObject : awsServices) {
logger.info("List object: " + new Gson().toJson(awsServiceObject));
}

In the code snippet above, we are:

  1. writing an object to the DB, and logging it to make sure we have the ID auto generated.
  2. fetching that ID into a String variable, and querying the DB for that object using the ID. We’re logging that as well.
  3. fetching all the items in the DB, looping through them, and logging each item to the console.

It’s as simple as that. You can browse your DB from the AWS console as well.

The complete POC is available on my Github profile as a project. Do let me know if you any doubt or if you can improve this POC in any way.


And if you like what you see here, or on my Medium blog, and would like to see more of such helpful technical posts in the future, consider supporting me on Patreon and Github.

Become a Patron!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.