avatarSwathi Prasad

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

6467

Abstract

">eu-central-1</span> <span class="hljs-attr">secretkey:</span> <span class="hljs-string">fakeSecret</span> <span class="hljs-attr">dynamodb:</span> <span class="hljs-attr">endpoint:</span> <span class="hljs-string">'http://localhost:8000/'</span></pre></div><p id="0846">The properties for access and secret keys can have random values for your local configuration but they are not necessary to authenticate for local installation. We are adding plain text credentials in our code for demo purposes only.</p><p id="2984">Let’s define a Spring config for DynamoDB which will read config values from application.yml file and establishes connectivity with DynamoDB.</p><div id="0b55"><pre><span class="hljs-keyword">package</span> org.swathisprasad.dynamodbdemo.config;

<span class="hljs-keyword">import</span> com.amazonaws.auth.AWSCredentialsProvider; <span class="hljs-keyword">import</span> com.amazonaws.auth.AWSStaticCredentialsProvider; <span class="hljs-keyword">import</span> com.amazonaws.auth.BasicAWSCredentials; <span class="hljs-keyword">import</span> com.amazonaws.client.builder.AwsClientBuilder; <span class="hljs-keyword">import</span> com.amazonaws.services.dynamodbv2.AmazonDynamoDB; <span class="hljs-keyword">import</span> com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder; <span class="hljs-keyword">import</span> com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; <span class="hljs-keyword">import</span> org.springframework.beans.factory.<span class="hljs-keyword">annotation</span>.Value; <span class="hljs-keyword">import</span> org.springframework.context.<span class="hljs-keyword">annotation</span>.Bean; <span class="hljs-keyword">import</span> org.springframework.context.<span class="hljs-keyword">annotation</span>.Configuration;

<span class="hljs-meta">@Configuration</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">DynamoDBConfig</span> {

<span class="hljs-meta">@Value(<span class="hljs-string">"<span class="hljs-subst">${amazon.dynamodb.endpoint}</span>"</span>)</span>
String endpoint;
<span class="hljs-meta">@Value(<span class="hljs-string">"<span class="hljs-subst">${amazon.aws.accesskey}</span>"</span>)</span>
String accesskey;
<span class="hljs-meta">@Value(<span class="hljs-string">"<span class="hljs-subst">${amazon.aws.secretkey}</span>"</span>)</span>
String secretkey;
<span class="hljs-meta">@Value(<span class="hljs-string">"<span class="hljs-subst">${amazon.aws.region}</span>"</span>)</span>
String region;

<span class="hljs-meta">@Bean</span>
<span class="hljs-keyword">public</span> DynamoDBMapper dynamoDBMapper() {
    <span class="hljs-keyword">return</span> new DynamoDBMapper(amazonDynamoDB());
}

<span class="hljs-meta">@Bean</span>
<span class="hljs-keyword">public</span> AmazonDynamoDB amazonDynamoDB() {
    <span class="hljs-keyword">return</span> AmazonDynamoDBClientBuilder
            .standard()
            .withEndpointConfiguration(endpointConfiguration())
            .withCredentials(awsCredentialsProvider())
            .build();
}

<span class="hljs-keyword">public</span> AwsClientBuilder.EndpointConfiguration endpointConfiguration() {
    <span class="hljs-keyword">return</span> new AwsClientBuilder.EndpointConfiguration(endpoint, region);
}

<span class="hljs-keyword">public</span> AWSCredentialsProvider awsCredentialsProvider() {
    <span class="hljs-keyword">return</span> new AWSStaticCredentialsProvider(new BasicAWSCredentials(accesskey, secretkey));
}

}</pre></div><p id="bae0"><code>DynamoDBMapper</code> class is the entry point to Amazon DynamoDB and allows to map client-side classes to Amazon DynamoDB tables. It enables us to access our data in various tables.</p><p id="f761">Let’s define an Entity class for DynamoDB table:</p><div id="526d"><pre><span class="hljs-keyword">package</span> org.swathisprasad.dynamodbdemo.model;

<span class="hljs-keyword">import</span> com.amazonaws.services.dynamodbv2.datamodeling.*; <span class="hljs-keyword">import</span> lombok.AllArgsConstructor; <span class="hljs-keyword">import</span> lombok.Data; <span class="hljs-keyword">import</span> lombok.NoArgsConstructor;

<span class="hljs-keyword">import</span> java.io.Serializable;

<span class="hljs-meta">@Data</span> <span class="hljs-meta">@NoArgsConstructor</span> <span class="hljs-meta">@AllArgsConstructor</span> <span class="hljs-meta">@DynamoDBTable(tableName = "LibraryCatalog")</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LibraryCatalog</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">Serializable</span> {

<span class="hljs-meta">@DynamoDBHashKey</span>
<span class="hljs-meta">@DynamoDBAutoGeneratedKey</span>
<span class="hljs-keyword">private</span> String author;

<span class="hljs-meta">@DynamoDBRangeKey</span>
<span class="hljs-keyword">private</span> String topic;

<span class="hljs-meta">@DynamoDBAttribute</span>
<span class="hljs-keyword">private</span> String title;

<span class="hljs-meta">@DynamoDBAttribute</span>
<span class="hljs-keyword">private</span> <span class="hljs-type">int</span> year;

}</pre></div><p id="2545">Here, we have defined a DynamoDB table name “<i>LibraryCatalog</i>” using @DynamoDBTable annotation.</p><p id="3ea1">An <i>it</i>em in DyanmoDB table is comparable to a row in a relational database or a document in NoSQL database. Each item in the table is uniquely identifiable by a primary key that is set on a table level. An item consists of <i>attributes</i>, which are bits of data in the item.</p><p id="0c39">In the class <i>LibraryCatalog, </i>there are four attributes: author, topic, title and year. Author is annotated with <i>DynamoDBHashKey, </i>which is also known as a partition key. The primary key in DynamoDB table can be composed of a Hash and Range Key, or just a Hash key.</p><p id="679b">In our table, we have defined both Hash and Range keys which constitute a primary key. A Range key is also known as sort key in DynamoDB.</p><p id="06f3">Let’s define a Service class and a simple REST controller to test interaction with DynamoDB.</p><div id="7008"><pre><span class="hljs-keyword">package</span> org.swathisprasad.dynamodbdemo.s

Options

ervice;

<span class="hljs-keyword">import</span> lombok.AllArgsConstructor; <span class="hljs-keyword">import</span> org.springframework.stereotype.Service; <span class="hljs-keyword">import</span> org.swathisprasad.dynamodbdemo.model.LibraryCatalog; <span class="hljs-keyword">import</span> org.swathisprasad.dynamodbdemo.repository.LibraryCatalogRepository;

<span class="hljs-meta">@AllArgsConstructor</span> <span class="hljs-meta">@Service</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LibraryCatalogService</span> {

<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> LibraryCatalogRepository libraryCatalogRepository;

<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">save</span><span class="hljs-params">(<span class="hljs-keyword">final</span> LibraryCatalog libraryCatalog)</span> {
    libraryCatalogRepository.saveLibraryCatalog(libraryCatalog);
}

<span class="hljs-keyword">public</span> LibraryCatalog <span class="hljs-title function_">getLibraryCatalog</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String author, <span class="hljs-keyword">final</span> String topic)</span> {
    <span class="hljs-keyword">return</span> libraryCatalogRepository.getLibraryCatalog(author, topic);
}

<span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title function_">delete</span><span class="hljs-params">(<span class="hljs-keyword">final</span> String author, <span class="hljs-keyword">final</span> String topic)</span> {
    libraryCatalogRepository.deleteLibraryCatalog(author, topic);
}

}</pre></div><div id="920f"><pre><span class="hljs-keyword">package</span> org.swathisprasad.dynamodbdemo.controller;

<span class="hljs-keyword">import</span> lombok.AllArgsConstructor; <span class="hljs-keyword">import</span> org.springframework.http.HttpStatus; <span class="hljs-keyword">import</span> org.springframework.http.ResponseEntity; <span class="hljs-keyword">import</span> org.springframework.web.bind.<span class="hljs-keyword">annotation</span>.*; <span class="hljs-keyword">import</span> org.swathisprasad.dynamodbdemo.model.LibraryCatalog; <span class="hljs-keyword">import</span> org.swathisprasad.dynamodbdemo.service.LibraryCatalogService;

<span class="hljs-meta">@AllArgsConstructor</span> <span class="hljs-meta">@RequestMapping(<span class="hljs-string">"library-catalog"</span>)</span> <span class="hljs-meta">@RestController</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">LibraryCatalogController</span> {

<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> LibraryCatalogService libraryCatalogService;

<span class="hljs-meta">@PostMapping(<span class="hljs-string">"/"</span>)</span>
<span class="hljs-keyword">public</span> ResponseEntity&lt;<span class="hljs-built_in">Void</span>&gt; saveLibraryCatalog(<span class="hljs-meta">@RequestBody</span> LibraryCatalog libraryCatalog) {
    libraryCatalogService.save(libraryCatalog);
    <span class="hljs-keyword">return</span> ResponseEntity.ok().build();
}

<span class="hljs-meta">@GetMapping(<span class="hljs-string">"/"</span>)</span>
<span class="hljs-keyword">public</span> LibraryCatalog getLibraryCatalog(<span class="hljs-meta">@RequestParam(<span class="hljs-string">"author"</span>)</span> String author, <span class="hljs-meta">@RequestParam(value = <span class="hljs-string">"topic"</span>)</span> String topic) {
    <span class="hljs-keyword">return</span> libraryCatalogService.getLibraryCatalog(author, topic);
}

<span class="hljs-meta">@ResponseStatus(value = HttpStatus.NO_CONTENT)</span>
<span class="hljs-meta">@DeleteMapping(<span class="hljs-string">"/"</span>)</span>
<span class="hljs-keyword">public</span> void deleteLibraryCatalog(<span class="hljs-meta">@RequestParam(<span class="hljs-string">"author"</span>)</span> String author, <span class="hljs-meta">@RequestParam(value = <span class="hljs-string">"topic"</span>)</span> String topic) {
    libraryCatalogService.delete(author, topic);
}

}</pre></div><h1 id="42d5">Testing Spring Boot Application</h1><p id="b111">Before we test our application, let’s run docker-compose file using the following command via a terminal or command prompt.</p><blockquote id="99ba"><p>docker-compose up</p></blockquote><p id="c52f">Once the docker container for DynamoDB is successfully started, let’s create DynamoDB table using AWS CLI.</p><div id="e3b3"><pre>aws dynamodb create-table \

  • table-name LibraryCatalog \
  • attribute-definitions
    AttributeName=author,AttributeType=S
    AttributeName=topic,AttributeType=S \
  • key-schema
    AttributeName=author,KeyType=HASH
    AttributeName=topic,KeyType=RANGE \
  • provisioned-throughput
    ReadCapacityUnits=5,WriteCapacityUnits=5 \
  • endpoint-url http://localhost:8000</pre></div><p id="246c">Here, we have specified attributes for a Hash key (mandatory) and Range key (optional) in the attribute definition.</p><p id="8362">All other attributes can be specified when inserting data into the table.</p><p id="7219">Let’s start Spring Boot application and make a sample POST request using Postman.</p><figure id="1831"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*_Sk9jRzdKvT0vheugu7LLw.png"><figcaption>Postman tool</figcaption></figure><p id="23b6">Let’s make a sample GET request.</p><figure id="df8b"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*Z1jYX6pW47wewb-Gkh3GBg.png"><figcaption>Postman tool</figcaption></figure><h1 id="5f59">Conclusion</h1><p id="a862">We integrated DynamoDB with a Spring Boot application successfully using AWS SDK for Java. After we complete testing locally, we should be able to use a production instance of DynamoDB on AWS and run the deployed code with some minor changes to configuration.</p><p id="783c">I hope this article provided a basic understanding on DynamoDB and its integration with Spring Boot.</p><p id="5b3c">The entire code for this article and <i>IaC template</i> can be found in my <a href="https://github.com/swathisprasad/aws-spring-boot-integration-examples/tree/main/dynamodb-demo">GitHub repo</a>.</p></article></body>

Software Integration Series: DynamoDB Integration with Spring Boot

A simple guide to integrating Serverless NoSQL database with a Spring Boot application.

Photo by Jan Antonin Kolar on Unsplash

DynamoDB is a serverless, NoSQL database offered by AWS. It is a fully managed database and offers reliable performance at any scale. It scales up or down automatically to adjust for capacity and maintain performance.

In this article, we will learn how to easily integrate DynamoDB with a Spring Boot application.

Prerequisites

  • Apache Maven
  • Docker Desktop
  • IDE
  • AWS CLI

In this tutorial, we will use docker version of DynamoDB for local integration and testing. Download and Install docker desktop.

Follow the AWS documentation to install AWS CLI.

Maven Dependencies

Let’s create a sample Spring Boot application and include the following dependencies in pom.xml.

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

  <dependency>
   <groupId>com.amazonaws</groupId>
   <artifactId>aws-java-sdk-dynamodb</artifactId>
   <version>1.12.459</version>
  </dependency>

  <dependency>
   <groupId>javax.validation</groupId>
   <artifactId>validation-api</artifactId>
   <version>2.0.1.Final</version>
  </dependency>

  <dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <version>1.18.26</version>
   <scope>provided</scope>
  </dependency>

 </dependencies>

Spring Boot Starter Web dependency is used for building web or RESTful applications using Spring MVC. AWS SDK for DynamoDB provides APIs to work with DynamoDB. Lombok is used for generating boilerplate code such as getters, setters, and so on. JavaBeans Validation (validation-api) can add constraints to the beans with annotations placed on fields, methods, or classes.

There are various ways to install DynamoDB for local testing. Checkout the official documentation. We will install the docker version for this tutorial. Create a docker-compose.yml to install DynamoDB.

version: '3.8'
services:
  dynamodb-local:
    command: "-jar DynamoDBLocal.jar -sharedDb -dbPath ./data"
    image: "amazon/dynamodb-local:latest"
    container_name: dynamodb-local
    ports:
      - "8000:8000"
    volumes:
      - "./docker/dynamodb:/home/dynamodblocal/data"
    working_dir: /home/dynamodblocal

Let’s define some AWS properties in application.yml file to configure access key and secret.

amazon:
  aws:
    accesskey: fakeAccesKey
    region: eu-central-1
    secretkey: fakeSecret
  dynamodb:
    endpoint: 'http://localhost:8000/'

The properties for access and secret keys can have random values for your local configuration but they are not necessary to authenticate for local installation. We are adding plain text credentials in our code for demo purposes only.

Let’s define a Spring config for DynamoDB which will read config values from application.yml file and establishes connectivity with DynamoDB.

package org.swathisprasad.dynamodbdemo.config;

import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.client.builder.AwsClientBuilder;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DynamoDBConfig {

    @Value("${amazon.dynamodb.endpoint}")
    String endpoint;
    @Value("${amazon.aws.accesskey}")
    String accesskey;
    @Value("${amazon.aws.secretkey}")
    String secretkey;
    @Value("${amazon.aws.region}")
    String region;

    @Bean
    public DynamoDBMapper dynamoDBMapper() {
        return new DynamoDBMapper(amazonDynamoDB());
    }

    @Bean
    public AmazonDynamoDB amazonDynamoDB() {
        return AmazonDynamoDBClientBuilder
                .standard()
                .withEndpointConfiguration(endpointConfiguration())
                .withCredentials(awsCredentialsProvider())
                .build();
    }

    public AwsClientBuilder.EndpointConfiguration endpointConfiguration() {
        return new AwsClientBuilder.EndpointConfiguration(endpoint, region);
    }

    public AWSCredentialsProvider awsCredentialsProvider() {
        return new AWSStaticCredentialsProvider(new BasicAWSCredentials(accesskey, secretkey));
    }
}

DynamoDBMapper class is the entry point to Amazon DynamoDB and allows to map client-side classes to Amazon DynamoDB tables. It enables us to access our data in various tables.

Let’s define an Entity class for DynamoDB table:

package org.swathisprasad.dynamodbdemo.model;

import com.amazonaws.services.dynamodbv2.datamodeling.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@NoArgsConstructor
@AllArgsConstructor
@DynamoDBTable(tableName = "LibraryCatalog")
public class LibraryCatalog implements Serializable {

    @DynamoDBHashKey
    @DynamoDBAutoGeneratedKey
    private String author;

    @DynamoDBRangeKey
    private String topic;

    @DynamoDBAttribute
    private String title;

    @DynamoDBAttribute
    private int year;

}

Here, we have defined a DynamoDB table name “LibraryCatalog” using @DynamoDBTable annotation.

An item in DyanmoDB table is comparable to a row in a relational database or a document in NoSQL database. Each item in the table is uniquely identifiable by a primary key that is set on a table level. An item consists of attributes, which are bits of data in the item.

In the class LibraryCatalog, there are four attributes: author, topic, title and year. Author is annotated with DynamoDBHashKey, which is also known as a partition key. The primary key in DynamoDB table can be composed of a Hash and Range Key, or just a Hash key.

In our table, we have defined both Hash and Range keys which constitute a primary key. A Range key is also known as sort key in DynamoDB.

Let’s define a Service class and a simple REST controller to test interaction with DynamoDB.

package org.swathisprasad.dynamodbdemo.service;

import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.swathisprasad.dynamodbdemo.model.LibraryCatalog;
import org.swathisprasad.dynamodbdemo.repository.LibraryCatalogRepository;

@AllArgsConstructor
@Service
public class LibraryCatalogService {

    private final LibraryCatalogRepository libraryCatalogRepository;

    public void save(final LibraryCatalog libraryCatalog) {
        libraryCatalogRepository.saveLibraryCatalog(libraryCatalog);
    }

    public LibraryCatalog getLibraryCatalog(final String author, final String topic) {
        return libraryCatalogRepository.getLibraryCatalog(author, topic);
    }

    public void delete(final String author, final String topic) {
        libraryCatalogRepository.deleteLibraryCatalog(author, topic);
    }
}
package org.swathisprasad.dynamodbdemo.controller;

import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.swathisprasad.dynamodbdemo.model.LibraryCatalog;
import org.swathisprasad.dynamodbdemo.service.LibraryCatalogService;

@AllArgsConstructor
@RequestMapping("library-catalog")
@RestController
public class LibraryCatalogController {

    private final LibraryCatalogService libraryCatalogService;

    @PostMapping("/")
    public ResponseEntity<Void> saveLibraryCatalog(@RequestBody LibraryCatalog libraryCatalog) {
        libraryCatalogService.save(libraryCatalog);
        return ResponseEntity.ok().build();
    }

    @GetMapping("/")
    public LibraryCatalog getLibraryCatalog(@RequestParam("author") String author, @RequestParam(value = "topic") String topic) {
        return libraryCatalogService.getLibraryCatalog(author, topic);
    }

    @ResponseStatus(value = HttpStatus.NO_CONTENT)
    @DeleteMapping("/")
    public void deleteLibraryCatalog(@RequestParam("author") String author, @RequestParam(value = "topic") String topic) {
        libraryCatalogService.delete(author, topic);
    }
}

Testing Spring Boot Application

Before we test our application, let’s run docker-compose file using the following command via a terminal or command prompt.

docker-compose up

Once the docker container for DynamoDB is successfully started, let’s create DynamoDB table using AWS CLI.

aws dynamodb create-table \
 - table-name LibraryCatalog \
 - attribute-definitions \
 AttributeName=author,AttributeType=S \
 AttributeName=topic,AttributeType=S \
 - key-schema \
 AttributeName=author,KeyType=HASH \
 AttributeName=topic,KeyType=RANGE \
 - provisioned-throughput \
 ReadCapacityUnits=5,WriteCapacityUnits=5 \
 - endpoint-url http://localhost:8000

Here, we have specified attributes for a Hash key (mandatory) and Range key (optional) in the attribute definition.

All other attributes can be specified when inserting data into the table.

Let’s start Spring Boot application and make a sample POST request using Postman.

Postman tool

Let’s make a sample GET request.

Postman tool

Conclusion

We integrated DynamoDB with a Spring Boot application successfully using AWS SDK for Java. After we complete testing locally, we should be able to use a production instance of DynamoDB on AWS and run the deployed code with some minor changes to configuration.

I hope this article provided a basic understanding on DynamoDB and its integration with Spring Boot.

The entire code for this article and IaC template can be found in my GitHub repo.

Spring Boot
Dynamodb
AWS
Java
Software Engineering
Recommended from ReadMedium