Tutorial DynamoDB utilizando AWS SDK e Java

O DynamoDB é um banco de dados chave-valor e documentos. Indicado para aplicações que necessitam de um super desempenho em leituras e escritas com baixa latência. O DynamoDB pode processar mais de 20 trilhões de solicitações por dia e mais de 20 milhões de solicitações por segundo. A estrutura do DynamoDB é um pouco diferente dos bancos relacionais, não se cria um banco e suas tabelas. Todas as tabelas são criadas de forma descentralizadas. Para conectar nestas tabelas, basta utilizar a SDK da AWS. Não existe um driver JDBC que nem usamos em bancos relacionais para conecta-los. Para este tutorial vamos criar alguns exemplo de acesso ao DynamoDB criando e lendo itens utilizando a AWS SDK, Java como linguagem de programação e Maven como Build Tool. Maven <dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-dynamodb</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-bom</artifactId>
<version>1.11.327</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement> * Adicionamos a dependência do Lombok para auxiliar na criação do modelo a seguir. Criando o modelo Vamos criar o modelo que utilizaremos como uma instância dos itens que serão adicionados no DynamoBD. import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
import lombok.Data;

@Data
@DynamoDBTable(tableName = "Books")
public class Book {

@DynamoDBHashKey
@DynamoDBAttribute(attributeName = "isbn")
private String isbn;

@DynamoDBAttribute(attributeName = "title")
private String title;

@DynamoDBAttribute(attributeName = "pages")
private int pages;

@DynamoDBAttribute(attributeName = "publishing_company")
private String publishingCompany;

@DynamoDBAttribute(attributeName = "year")
private int year;
} Entendendo as anotações da classe Book: @Data Anotação do Lombok que substituirá os getters e setters para cada campo. Estamos utilizando o Lombok apenas para deixar o código menos verboso. Não possui relação direta com o DynamoDB. @DynamoDBTable Anotação da SDK da AWS DynamoDB que define a classe como uma tabela destino do DynamoBD. Veja que é possível passar como parâmetro o nome da tabela que será Books. @DynamoDBHashKey Esta anotação define uma chave de partição, de forma resumida, seria uma espécie de chave primária. Para o nosso exemplo, o campo isbn será definida como campo chave na tabela Books no DynamoDB. @DynamoDBAttribute Na prática, cada campo mapeado no modelo será um campo da tabela de destino Books no DynamoDB. A anotação @DynamoDBAttribute pode ser utilizada para caso você precise definir um nome diferente do que foi declarado no modelo. Por ex: O campo publishingCompany será definido na tabela como publishing_company e os demais serão criadas conforme foram declarados no modelo. Nesse caso, mapeamos todos eles para manter um padrão. Existem várias outras anotações que podem ser utilizadas, basta acessar a documentação para entender um pouco mais. Criando a configuração de acesso ao Dynamo O próximo código será utilizado para conectar ao serviço do DynamoDB na AWS e criar uma instância responsável por manipular os itens. import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;

public class DynamoConfig {

private static AmazonDynamoDB amazonDynamoDB;

private static AmazonDynamoDB getAmazonDynamoDB(){

BasicAWSCredentials credentials =
new BasicAWSCredentials(
"ACCESS-KEY","SECRET-ACCESS-KEY"););

AWSCredentialsProvider awsCredentialsProvider =
new AWSStaticCredentialsProvider(credentials);

return AmazonDynamoDBClientBuilder.standard()
.withCredentials(awsCredentialsProvider)
.withRegion(Regions.US_EAST_1)
.build();
}

public static DynamoDBMapper dynamoDBMapper(){
if(amazonDynamoDB == null){
amazonDynamoDB = getAmazonDynamoDB();
}
return new DynamoDBMapper(amazonDynamoDB);
}
} Entendendo a classe DynamoConfig: O objeto amazonDynamoDB é uma instância do tipo AmazonDynamoDB que será responsável em recuperar os recursos necessários para o acesso ao DynamoDB. O método getAmazonDynamoDB( ) conectará na AWS através de suas credencias de sua conta na região US-EAST-1. Caso utilize outra região é só alterar o enum Regions. O método getDynamoDBMapper( ) criará um um objeto do tipo DynamoDBMapper. Perceba que ele cria uma instância a partir do objeto amazonDynamoDB, responsável por manter a conexão com o serviço do AWS DynamoDB. Um objeto do tipo DynamoDBMapper permite invocar operações do tipo CRUD, segue alguns dos métodos disponíveis: save load delete query scan scanPage As operações estão disponíveis nesta doc. Criando o DAO import com.amazonaws.services.dynamodbv2
.datamodeling.DynamoDBMapper;
import config.DynamoConfig;
import model.Book;

public class BookDAO {

private DynamoDBMapper dynamoDBMapper;

public BookDAO(){
dynamoDBMapper = DynamoConfig.dynamoDBMapper();
}

public void save(Book book){
dynamoDBMapper.save(book);
}

public void delete(Book book){
dynamoDBMapper.delete(book);
}

public Book findByIsbn(String isbn){
return dynamoDBMapper
.load(Book.class, isbn);
}
} A classe BookDAO é a classe que contém os métodos responsáveis em acessar o DynamoBD e manipular os itens. Entendendo a classe BookDAO: Conforme falamos anteriormente, o objeto dynamoMapper fornece as operações necessárias para manipular um item Método save(Book book) persistirá o item na tabela Books Método delete(Book book) é responsável por remover o item da tabela Books Método findByIsbn(String isbn) executa uma consulta nos itens filtrando pela chave isbn. Dentro deste método invocamos o método load(Class<T> clazz, Object hashKey) do DynamoMapper que nos obriga a passar o tipo do objeto e a chave de busca. Executando o código Antes de executar o código, vamos criar a tabela Books utilizando o console do DynamoDB. Acesse o DynamoDB conforme imagem Clique em Criar Tabela e uma nova página será aberta No campo Nome da Tabela, digite Books No campo Chave primária, digite isbn. Para finalizar, clique em Criar. Após executar as etapas anteriores, vamos criar um código que executará um fluxo de criação de um Item, consulta e por fim, vamos deletá-lo. import dao.BookDAO;
import model.Book;

public class RunDynamo {

public static void main(String[] args) {

BookDAO bookDAO = new BookDAO();

System.out.println("Criando o objeto book");

Book book = new Book();
book.setIsbn("isbn-1");
book.setTitle("Entendendo DynamoDB");
book.setPublishingCompany("Editora Abril");
book.setYear(2021);
book.setPages(300);

System.out.println("Salvando o objeto book");

bookDAO.save(book);

System.out.println("Buscando o objeto book por Isbn");

Book bookSearch = bookDAO.findByIsbn("isbn-1");

System.out.println("
Resultado da consulta: " + bookSearch);

System.out.println("Removendo o objeto book");
bookDAO.delete(bookSearch);
}
} Após executar o código, verifique no console o resultado do fluxo. Paginando uma consulta utilizando scanPage O método scanPage faz parte do pacote de operações disponíveis na classe DynamoDBMapper. Utilizando este método é possível criar consultas mais completas utilizando paginação. No próximo exemplo vamos utilizar este método junto com expressões que pode deixar bem flexível a consulta utilizando diversos filtros. public List<Book> listBooksPagination(Integer year, String title, Integer pagination){

Map<String, AttributeValue> attributeValueMap
= new HashMap<>();

attributeValueMap.put(":year", new AttributeValue()
.withN(year.toString()));

attributeValueMap.put(":title", new AttributeValue()
.withS(title));

DynamoDBScanExpression dbScanExpression
= new DynamoDBScanExpression()
.withFilterExpression("year = :year and
title = :title")
.withExpressionAttributeValues(attributeValueMap)
.withLimit(pagination);

List<Book> listBooks = new ArrayList<>();

do {

ScanResultPage<Book> bookScanResultPage
= dynamoDBMapper.scanPage(Book.class,
dbScanExpression);

bookScanResultPage.getResults().forEach(
result -> listBooks.add(result)
);

} while (dbScanExpression.getExclusiveStartKey() != null);
return listBooks;
} O método acima foi criada para exemplificar uma forma mais dinâmica e completa para fazer consultas no DynamoDB de forma mais segura. Utilize o scanPage ao invés do scan. Pois este faz uma leitura de toda a tabela que pode sair um pouco caro, lembrando que uma das formas de cobrança do DynamoDB são as quantidades de consultas. Imagine uma tabela com milhões de registro, a cobrança pode ser bem alta. É isso, curtiu? Até mais!

Tutorial DynamoDB utilizando AWS SDK e Java