Skip to main content

ddb-spring-data-1

We will describe two ways for accessing DynamoDB from Spring applications:

DynamoDB Local Spring Data

Description

Using DynamoDB module of Spring Data

The Spring Data module for DynamoDB is a community module for accessing AWS DynamoDB with familiar Spring Data constructs of data objects and repository interfaces.

Run Docker & DynamoDBLocal

DynamoDB Admin

Create table & items (e.g.)

{
"AttributeDefinitions": [
{
"AttributeName": "id",
"AttributeType": "N"
}
],
"TableName": "Music",
"KeySchema": [
{
"AttributeName": "id",
"KeyType": "HASH"
}
],
"TableStatus": "ACTIVE",
"CreationDateTime": "2024-09-15T09:36:39.613Z",
"ProvisionedThroughput": {
"LastIncreaseDateTime": "1970-01-01T00:00:00.000Z",
"LastDecreaseDateTime": "1970-01-01T00:00:00.000Z",
"NumberOfDecreasesToday": 0,
"ReadCapacityUnits": 3,
"WriteCapacityUnits": 3
},
"TableSizeBytes": 95,
"ItemCount": 2,
"TableArn": "arn:aws:dynamodb:ddblocal:000000000000:table/Music",
"DeletionProtectionEnabled": false
}
ddb-spring-data-01.png

Project Structure

.
├── pom.xml
├── README.md
└── src
├── main
│   ├── java
│   │   └── com
│   │   └── jreact
│   │   └── dynamodb
│   │   ├── Application.java
│   │   ├── DynamoDbConfig.java
│   │   ├── MusicController.java
│   │   ├── MusicEntity.java
│   │   ├── MusicRepository.java
│   │   └── MusicService.java
│   └── resources
│   └── application.properties
└── test
└── java
└── com
└── jreact
└── dynamodb
└── MusicServiceTest.java

application.properties

dynamodb.endpoint=http://localhost:8000
region=us-east-1

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<groupId>com.jreact</groupId>
<artifactId>ddb-spring-data-1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ddb-spring-data-1</name>
<description>ddb-spring-data-1</description>

<properties>
<java.version>17</java.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<lombok.version>1.18.34</lombok.version>
<aws.java.sdk.version>2.28.0</aws.java.sdk.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
<version>${aws.java.sdk.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>dynamodb</artifactId>
</dependency>

<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>dynamodb-enhanced</artifactId>
</dependency>

<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>DynamoDBLocal</artifactId>
<version>2.5.1</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.6.0</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.build.directory}/native-libs</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin><!-- attach IT tests to verify phase -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.1.2</version>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>integration-test</goal>
</goals>
</execution>
</executions>
<configuration>
<systemPropertyVariables>
<sqlite4java.library.path>${project.build.directory}/native-libs</sqlite4java.library.path>
</systemPropertyVariables>
<argLine>
--add-opens java.base/java.time=ALL-UNNAMED
</argLine>
</configuration>
</plugin>

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>3.4.0</version>
<executions>
<execution>
<id>add-integration-test-source</id>
<phase>generate-test-sources</phase>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>src/integration-test/java</source>
</sources>
</configuration>
</execution>
<execution>
<id>add-integration-test-resource</id>
<phase>generate-test-resources</phase>
<goals>
<goal>add-test-resource</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>src/integration-test/resources</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

</project>

DynamoDbConfig.java

package com.jreact.dynamodb;

import java.net.URI;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.extensions.VersionedRecordExtension;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;

@Configuration
@NoArgsConstructor
@Slf4j
public class DynamoDbConfig {
@Value("${dynamodb.endpoint:}")
private String dynamodbEndpoint;

@Value("${region:}")
private String region;

@Bean
public DynamoDbClient getDynamoDbClient() {
var builder = DynamoDbClient
.builder()
.credentialsProvider(DefaultCredentialsProvider.create());

if (dynamodbEndpoint != null && !dynamodbEndpoint.isBlank()) {
builder.region(Region.of(region))
.endpointOverride(URI.create(dynamodbEndpoint));
log.info("ddb region: " + region);
log.info("ddb endpoint: " + dynamodbEndpoint);
}
return builder.build();
}

@Bean
public DynamoDbEnhancedClient getDynamoDbEnhancedClient(DynamoDbClient ddbc) {
return DynamoDbEnhancedClient
.builder()
.extensions(VersionedRecordExtension.builder().build())
.dynamoDbClient(ddbc)
.build();
}

@Bean
public DynamoDbTable<MusicEntity> getMusicLocaleTable(DynamoDbEnhancedClient dbClient) {
return dbClient.table(MusicEntity.TABLE_NAME, TableSchema.fromBean(MusicEntity.class));
}
}

MusicEntity.java

package com.jreact.dynamodb;

import lombok.Data;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;

@DynamoDbBean
@Data
public class MusicEntity {
public static final String TABLE_NAME = "Music";

private String musicCode;
private String musicDisplayName;

@DynamoDbPartitionKey
public String getMusicCode() {
return musicCode;
}
}

Running and Testing

mvn clean package
mvn spring-boot:run

Output (Browser):

localhost-browse-01.png

Source Code

https://github.com/ZbCiok/zjc-examples/tree/main/aws/aws/dynamodb/ddb-spring-data-1