Building high-performance and scalable applications often requires a powerful database solution.
If you're working with a Java Spring project and considering Amazon's robust NoSQL service, DynamoDB, this article is for you! We'll guide you through a seamless integration process using the Spring Cloud AWS.
By following this tutorial, you'll gain the knowledge and tools to leverage DynamoDB's flexibility and speed within your Spring application, taking your project's data storage capabilities to the next level.
The complete code for this project is available on my GitHub repository (getButton) #text=(GitHub) #icon=(share) #color=(#000000)
Dive into DynamoDB: A Scalable NoSQL Powerhouse for Modern Applications
DynamoDB is a NoSQL, key-value database service offered by Amazon Web Services (AWS). Unlike relational databases that rely on predefined schemas, DynamoDB offers a flexible data model that scales seamlessly to accommodate massive datasets and surging traffic
This makes it ideal for applications with unpredictable workloads or those requiring ultra-low latency for data access.
Imagine building a mobile app with millions of users – DynamoDB can effortlessly handle the surge in data traffic, ensuring a smooth and responsive experience for everyone.
Creating a DynamoDB Table with the AWS CLI
Before you can interact with DynamoDB from your Spring Cloud AWS application, you'll need to have a table already set up in your DynamoDB database.
Make sure you have the AWS CLI installed and configured with your AWS access key ID and secret access key. This allows the CLI to interact with your AWS resources, including DynamoDB. You can find instructions for configuration in the AWS documentation
aws dynamodb create-table --table-name movie \
--attribute-definitions AttributeName=movie_id,AttributeType=N AttributeName=movie_title,AttributeType=S \
--key-schema AttributeName=movie_id,KeyType=HASH AttributeName=movie_title,KeyType=RANGE \
--provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1
Once you executed the above script, you should see your table details in JSON format in the response, as seen below.
{
"TableDescription": {
"AttributeDefinitions": [
{
"AttributeName": "movie_id",
"AttributeType": "N"
},
{
"AttributeName": "movie_title",
"AttributeType": "S"
}
],
"TableName": "movie",
"KeySchema": [
{
"AttributeName": "movie_id",
"KeyType": "HASH"
},
{
"AttributeName": "movie_title",
"KeyType": "RANGE"
}
],
"TableStatus": "CREATING",
"CreationDateTime": "2024-04-26T10:33:15.733000+00:00",
"ProvisionedThroughput": {
"NumberOfDecreasesToday": 0,
"ReadCapacityUnits": 1,
"WriteCapacityUnits": 1
},
"TableSizeBytes": 0,
"ItemCount": 0,
"TableArn": "arn:aws:dynamodb:us-east-1:581907371749:table/movie",
"TableId": "ef4ae555-0d45-4ba5-a846-d78c6098d765",
"DeletionProtectionEnabled": false
}
}
Maximum number of tables per account and region.
Any AWS account has an initial quota of 2,500 tables per AWS Region.
If you require more than 2,500 tables for a single account, please contact the Amazon Web Services (AWS) support team to discuss an increase to a maximum of 10,000 tables. For more than 10,000 tables, the recommended best practice is to create numerous accounts, each capable of serving up to 10,000 tables.(alert-passed)
Adding the Spring Cloud AWS DynamoDB dependency
Spring Cloud AWS provides a starter dependency to simplify integrating with DynamoDB.
ext {
springCloudAwsVersion = '3.0.0'
}
dependencies {
// spring cloud aws BOM(Bill of materials)
implementation platform("io.awspring.cloud:spring-cloud-aws-dependencies:${springCloudAwsVersion}")
// dynamoDB
implementation 'io.awspring.cloud:spring-cloud-aws-starter-dynamodb'
}
Configuring DynamoDB with Spring Boot and Spring Cloud AWS (application.properties)
This simple configuration guide explains how to integrate Amazon DynamoDB into your Spring Boot application with Spring Cloud AWS. Details on credentials, region, and connection properties will be included in the application.properties
file.
# DynamoDB connection
spring.cloud.aws.credentials.access-key=AWS_ACCESS_KEY
spring.cloud.aws.credentials.secret-key=AWS_SECRET_KEY
# Choose your region according to your table. Because I have a DynamoDB table in the US-East-1 area, I've set the endpoint and region.
spring.cloud.aws.region.static=us-east-1
spring.cloud.aws.endpoint=https://dynamodb.us-east-1.amazonaws.com
Defining DynamoDB Documents (Model/Entity)
This guide dives into crafting well-structured documents (data models) for your 'Movies' table in DynamoDB.
It clarifies the concept of DynamoDB documents (JSON data structures) and provides best practices for designing them to efficiently store and retrieve movie data within your application.
package in.learnjavaskills.springcloudawsdynamodb.document;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbAttribute;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;
import java.util.List;
import java.util.Map;
import java.util.Set;
@DynamoDbBean
public class Movie
{
private Long movieId;
private String movieTitle;
private Map<String, List<String>> directors;
private Set<String> characters;
private Genres genre;
private Boolean baseOnBook;
private Double rating;
public Movie()
{
}
public Movie(Long movieId, String movieTitle, Map<String, List<String>> directors, Set<String> characters, Genres genre, Boolean baseOnBook, Double rating)
{
this.movieId = movieId;
this.movieTitle = movieTitle;
this.directors = directors;
this.characters = characters;
this.genre = genre;
this.baseOnBook = baseOnBook;
this.rating = rating;
}
@DynamoDbPartitionKey
@DynamoDbAttribute(value = "movie_id")
public Long getMovieId()
{
return movieId;
}
public void setMovieId(Long movieId)
{
this.movieId = movieId;
}
@DynamoDbSortKey
@DynamoDbAttribute(value = "movie_title")
public String getMovieTitle()
{
return movieTitle;
}
public void setMovieTitle(String movieTitle)
{
this.movieTitle = movieTitle;
}
@DynamoDbAttribute(value = "directors")
public Map<String, List<String>> getDirectors()
{
return directors;
}
public void setDirectors(Map<String, List<String>> directors)
{
this.directors = directors;
}
public Set<String> getCharacters()
{
return characters;
}
public void setCharacters(Set<String> characters)
{
this.characters = characters;
}
public Genres getGenre()
{
return genre;
}
public void setGenre(Genres genre)
{
this.genre = genre;
}
public Boolean getBaseOnBook()
{
return baseOnBook;
}
public void setBaseOnBook(Boolean baseOnBook)
{
this.baseOnBook = baseOnBook;
}
public Double getRating()
{
return rating;
}
public void setRating(Double rating)
{
this.rating = rating;
}
@Override
public String toString()
{
return "Movie{" + "movieId=" + movieId + ", movieTitle='" + movieTitle + '\'' + ", directors=" + directors + ", characters=" + characters + ", genre=" + genre + ", baseOnBook=" + baseOnBook + ", rating=" + rating + '}';
}
}
Introducing @DynamoDbBean
: Mapping Your Model to DynamoDB Documents
The @DynamoDbBean
annotation is a cornerstone of Spring Cloud AWS for interacting with Amazon DynamoDB.
It acts as a bridge between your application's model classes and DynamoDB documents, allowing you to seamlessly map your data structures to the NoSQL database format.
Key Attributes: @DynamoDbPartitionKey and @DynamoDbSortKey
Within a DynamoDB table, data is organized using primary keys. Spring Cloud AWS provides annotations to define these keys within your model classes:
• @DynamoDbPartitionKey
: This annotation marks a property in your model class as the partition key. The partition key is the primary way DynamoDB partitions your data and efficiently retrieves items. It's crucial to choose an attribute with high cardinality (frequent unique values) for optimal performance.
Partition key length
The minimum length of a partition key value is 1 byte. The maximum length is 2048 bytes.(alert-success)
• @DynamoDbSortKey
: This annotation identifies a property as the sort key. The sort key further refines how DynamoDB retrieves items within a partition. It's often used for efficient range queries on frequently accessed data sets within a partition.
Sort key length
The minimum length of a sort key value is 1 byte. The maximum length is 1024 bytes.(alert-success)
Mapping Model Properties: @DynamoDbAttribute
The @DynamoDbAttribute
annotation plays a vital role in Spring Cloud AWS for DynamoDB. It signifies that a property in your model class corresponds to an attribute within a DynamoDB document. Here's a breakdown of its functionality:
• Marks Properties: It explicitly tells Spring Cloud AWS to map the annotated property to a DynamoDB attribute.
• Optional (Sometimes): By default, if the property name in your model class matches the DynamoDB attribute name, this annotation is not strictly required. As evident in the movie mode class, I have purposefully chosen not to use this annotation for a few of the properties. Spring Cloud AWS will match the DynamoDB attribute with the movie class property name.
• Custom Attribute Names: If the property name differs from the desired DynamoDB attribute name, you can specify the attribute name using the value
parameter within the annotation.
Attribute names
In general, an attribute name must be at least one character long, but no greater than 64 KB long.(alert-success)
Performing CRUD Operations on DynamoDB with Spring Cloud AWS
Let's explore at the Create, Read, Update, and Delete (CRUD) operations on the DynamoDB table we created earlier, i.e., the movie, using Spring Cloud AWS.
The DynamoDBTemplate: Your Interface for CRUD Operations
The starter automatically configures and registers a DynamoDbOperations
bean, which provides higher-level abstractions for working with DynamoDB. DynamoDbTemplate
, a default DynamoDbOperations implementation, is built on top of DynamoDbEnhancedClient
and uses annotations provided by AWS.
DynamoDbTemplate allow you to perform CRUD operations (Create, Read, Update, Delete) on your DynamoDB tables in a more convenient and Spring-friendly manner and It may be found in the io.awspring.cloud.dynamodb.DynamoDbTemplate
package.
Persisting Movie Data: Saving Movies with DynamoDBTemplate
This section explores how to leverage the DynamoDbTemplate
class to seamlessly save movie data within your DynamoDB 'Movies' table. Notably, the save
method within DynamoDbTemplate serves as the primary mechanism for persisting your movie information as documents in the DynamoDb database.
public Long addMovie(Movie movie) {
if (Objects.isNull(movie))
throw new DynamoDbDataException("Movie must be non null");
Movie savedMovie = dynamoDbTemplate.save(movie);
if (Objects.nonNull(savedMovie) && Objects.nonNull(savedMovie.getMovieId()))
return savedMovie.getMovieId();
throw new DynamoDbDataException("Unsaved movie, Please try again.");
}
public static final String HARRY_POTTER_AND_THE_DEATHLY_HALLOWS_PART_1 = "Harry Potter and the Deathly Hallows: Part 1";
public static final long MOVIE_ID = 101L;
@Autowired private MovieService movieService;
@Test
void saveMovie() {
Map<String, List<String>> directors = new HashMap<>();
List<String> directorsName = new ArrayList<>();
directorsName.add("David Yates");
directors.put("Dir1", directorsName);
Set<String> characters = new HashSet<>();
characters.add("Harry Potter");
characters.add("Hermione Granger");
characters.add("Ron Weasley");
characters.add("Draco Malfoy");
Movie movie = new Movie(MOVIE_ID, HARRY_POTTER_AND_THE_DEATHLY_HALLOWS_PART_1,
directors, characters, Genres.DRAMA, true, 3.9);
Long movieId = movieService.addMovie(movie);
Assertions.assertThat(movieId).isEqualTo(movie.getMovieId());
}
DynamoDB's maximum item size is 400 KB, which includes both attribute name binary length (UTF-8 length) and attribute value lengths (again, binary length). The attribute name counts toward the size limit.
For example, take an item with two attributes: "movie-name" with the value "Harry Potter and the Philosopher's Stone" and "release-date" with the value "2001". The item's entire size is 67 bytes. (alert-passed)
Retrieving Movie Information: Reading Data with DynamoDBTemplate
This section explores how to efficiently retrieve movie data from your DynamoDB 'Movies' table using the DynamoDbTemplate
class.
A core method for retrieving data is the load
method. It takes two crucial parameters:
• Class: This specifies the class that represents your movie data model (the class annotated with @DynamoDbBean)
• Key: This parameter is fundamental for pinpointing the specific movie entry you want to fetch. The key should correspond to the primary key defined in your model class using @DynamoDbPartitionKey
and optionally @DynamoDbSortKey
annotations. By providing the appropriate key values, load efficiently retrieves the corresponding movie document from DynamoDB.
public Optional<Movie> findMovie(Long movieId, String movieTitle) {
Key key = Key.builder()
.partitionValue(movieId)
.sortValue(movieTitle)
.build();
Movie movie = dynamoDbTemplate.load(key, Movie.class);
return Optional.ofNullable(movie);
}
@Test
public void findMovie() {
Optional<Movie> movie = movieService.findMovie(MOVIE_ID, HARRY_POTTER_AND_THE_DEATHLY_HALLOWS_PART_1);
Assertions.assertThat(movie).isPresent();
}
Removing Movies: Deleting Items with DynamoDBTemplate
This section tackles deleting movie entries from your DynamoDB 'Movies' table using the DynamoDbTemplate class.
Just like the load() method, the delete() method also takes two objects as input, i.e., the key and the instance of the model class.
void deleteMovie(Long movieId, String movieTitle) {
Key key = Key.builder()
.partitionValue(movieId)
.sortValue(movieTitle)
.build();
dynamoDbTemplate.delete(key, Movie.class);
}
@Test
public void deleteMovie() {
movieService.deleteMovie(MOVIE_ID, HARRY_POTTER_AND_THE_DEATHLY_HALLOWS_PART_1);
}
You may alternatively simply delete entries from the DynamoDB table by calling the overloaded delete method and accepting the data model, as seen in the example below.
public void deleteMovie(Movie movie) {
dynamoDbTemplate.delete(movie);
}
Modifying Movies: Updating Data with DynamoDBTemplate
This section delves into updating existing movie entries within your DynamoDB 'Movies' table using the DynamoDbTemplate class. We'll explore how to leverage the update method of DynamoDbTemplate to make targeted changes to your movie data, ensuring your information remains accurate and up-to-date.
The update
method is the primary mechanism for updating data in DynamoDB. It takes a single argument:
• Your Model Class Instance: You pass an instance of your movie data model class (annotated with @DynamoDbBean). The update method recognizes the changes made to the object's properties and translates them into corresponding updates within the DynamoDB document associated with the movie's primary key. This approach simplifies the update process, as you only need to modify the relevant fields in your model object.
public Long updateMovie(Movie movie) {
Movie updatedMovie = dynamoDbTemplate.update(movie);
if (Objects.nonNull(updatedMovie))
return updatedMovie.getMovieId();
return 0L;
}
@Test
public void updateMovie() {
Map<String, List<String>> directors = new HashMap<>();
List<String> directorsName = new ArrayList<>();
directorsName.add("David Yates");
directors.put("Dir1", directorsName);
Set<String> characters = new HashSet<>();
characters.add("Harry Potter");
characters.add("Hermione Granger");
characters.add("Ron Weasley");
characters.add("Draco Malfoy");
Movie movie = new Movie(MOVIE_ID, HARRY_POTTER_AND_THE_DEATHLY_HALLOWS_PART_1,
directors, characters, Genres.DRAMA, true, 4.7);
Long movieId = movieService.updateMovie(movie);
Assertions.assertThat(movieId).isEqualTo(MOVIE_ID);
}
Conclusion
This article has explored performing CRUD operations (Create, Read, Update, Delete) on your DynamoDB 'Movies' table using Spring Cloud AWS and the DynamoDbTemplate
class. We've equipped you with the tools to effectively manage your movie data within the DnamoDb database.
The complete code for this project is available on my GitHub repository (getButton) #text=(GitHub) #icon=(share) #color=(#000000)
For a deep dive into scan and query operations, this blog is a great resource: (getButton) #text=(Mastering DynamoDB Data Retrieval: Advanced Scan and Query Operations) #icon=(link) #color=(#35a576)
Keep learning and keep growing.