Classe DynamoDBMapper - Amazon DynamoDB

Classe DynamoDBMapper

A classe DynamoDBMapper é o ponto de entrada do Amazon DynamoDB. Ela dá acesso a um endpoint do DynamoDB e permite acessar seus dados em várias tabelas. Ela também permite realizar várias operações de criação, leitura, atualização e exclusão (CRUD) em itens e executar consultas e verificações em tabelas. Essa classe fornece os seguintes métodos para trabalhar com o DynamoDB.

Para acessar a documentação Javadoc correspondente, consulte DynamoDBMapper no Referência da API do AWS SDK for Java.

save

Salva o objeto especificado na tabela. O objeto que você deseja salvar é o único parâmetro necessário para esse método. É possível fornecer parâmetros de configuração opcionais usando o objeto DynamoDBMapperConfig.

Se um item com a mesma chave primária não existir, esse método criará um novo item na tabela. Se existir um item com a mesma chave primária, ele atualizará o item existente. Se a chave de partição e a chave de classificação forem do tipo String e estiverem anotadas com @DynamoDBAutoGeneratedKey, elas receberão um identificador universal exclusivo (UUID) aleatório se não forem inicializadas. Campos de versão anotados com @DynamoDBVersionAttribute são incrementados em um. Além disso, se um campo de versão for atualizado ou se uma chave for gerada, o objeto transmitido será atualizado como resultado da operação.

Por padrão, somente atributos correspondentes às propriedades de classe mapeadas são atualizados. Quaisquer atributos existentes em um item não são afetados. No entanto, se você especificar SaveBehavior.CLOBBER, poderá forçar o item a ser completamente substituído.

mapper.save(obj, new DynamoDBMapperConfig(DynamoDBMapperConfig.SaveBehavior.CLOBBER));

Se o versionamento estiver habilitado, as versões dos itens no lado do servidor e no lado do cliente deverão corresponder. No entanto, a versão não precisará corresponder se a opção SaveBehavior.CLOBBER for usada. Para obter mais informações sobre versionamento, consulte Bloqueio positivo com número de versão.

load

Recupera um item de uma tabela. É necessário fornecer a chave primária do item que você deseja recuperar. É possível fornecer parâmetros de configuração opcionais usando o objeto DynamoDBMapperConfig. Por exemplo, você pode solicitar opcionalmente leituras fortemente consistentes para garantir que esse método recupere apenas os valores de itens mais recentes, como mostra a seguinte instrução Java.

CatalogItem item = mapper.load(CatalogItem.class, item.getId(), new DynamoDBMapperConfig(DynamoDBMapperConfig.ConsistentReads.CONSISTENT));

Por padrão, o DynamoDB retorna o item que possui valores eventualmente consistentes. Para obter informações sobre o modelo de consistência eventual do DynamoDB, consulte Consistência de leitura.

delete

Exclui um item da tabela. Você deve transmitir uma instância do objeto da classe mapeada.

Se o versionamento estiver habilitado, as versões dos itens no lado do servidor e no lado do cliente deverão corresponder. No entanto, a versão não precisará corresponder se a opção SaveBehavior.CLOBBER for usada. Para obter mais informações sobre versionamento, consulte Bloqueio positivo com número de versão.

query

Consulta uma tabela ou um índice secundário. Você poderá consultar uma tabela ou um índice somente se ele tiver uma chave primária composta (chave de partição e chave de classificação). Esse método requer que você forneça um valor de chave de partição e um filtro de consulta que é aplicado à chave de classificação. Uma expressão de filtro inclui uma condição e um valor.

Vamos supor que você tenha uma tabela, Reply, que armazena as respostas de tópicos de fórum. Cada assunto de tópico pode ter 0 ou mais respostas. A chave primária da tabela Reply consiste nos campos Id e ReplyDateTime, em que Id é a chave de partição e ReplyDateTime é a chave de classificação da chave primária.

Reply ( Id, ReplyDateTime, ... )

Vamos supor que você tenha criado um mapeamento entre uma classe Reply e a tabela Reply correspondente no DynamoDB. O código Java a seguir usa DynamoDBMapper para localizar todas as respostas nas últimas duas semanas para um assunto de tópico específico.

exemplo

String forumName = "&DDB;"; String forumSubject = "&DDB; Thread 1"; String partitionKey = forumName + "#" + forumSubject; long twoWeeksAgoMilli = (new Date()).getTime() - (14L*24L*60L*60L*1000L); Date twoWeeksAgo = new Date(); twoWeeksAgo.setTime(twoWeeksAgoMilli); SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); String twoWeeksAgoStr = df.format(twoWeeksAgo); Map<String, AttributeValue> eav = new HashMap<String, AttributeValue>(); eav.put(":v1", new AttributeValue().withS(partitionKey)); eav.put(":v2",new AttributeValue().withS(twoWeeksAgoStr.toString())); DynamoDBQueryExpression<Reply> queryExpression = new DynamoDBQueryExpression<Reply>() .withKeyConditionExpression("Id = :v1 and ReplyDateTime > :v2") .withExpressionAttributeValues(eav); List<Reply> latestReplies = mapper.query(Reply.class, queryExpression);

A consulta retorna uma coleção de objetos Reply.

Por padrão, o método query retorna uma coleção de "carregamento preguiçoso". Ele inicialmente retorna apenas uma página de resultados e, em seguida, faz uma chamada de serviço para a próxima página, se necessário. Para obter todos os itens correspondentes, faça uma iteração na coleção latestReplies.

Observe que chamar o método size() na coleção carregará todos os resultados para fornecer uma contagem precisa. Isso pode fazer com que uma grande quantidade de throughput provisionado seja consumida, e em uma tabela muito grande pode até esgotar toda a memória na JVM.

Para consultar um índice, você deve primeiro modelá-lo como uma classe de mapeador. Suponha que a tabela Reply tenha um índice global secundário chamado PostedBy-Message-Index. A chave de partição para esse índice é PostedBy, e a chave de classificação é Message. A definição de classe para um item no índice seria semelhante ao seguinte.

@DynamoDBTable(tableName="Reply") public class PostedByMessage { private String postedBy; private String message; @DynamoDBIndexHashKey(globalSecondaryIndexName = "PostedBy-Message-Index", attributeName = "PostedBy") public String getPostedBy() { return postedBy; } public void setPostedBy(String postedBy) { this.postedBy = postedBy; } @DynamoDBIndexRangeKey(globalSecondaryIndexName = "PostedBy-Message-Index", attributeName = "Message") public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } // Additional properties go here. }

A anotação @DynamoDBTable indica que esse índice está associado à tabela Reply. A anotação @DynamoDBIndexHashKey representa a chave de partição (PostedBy) do índice, enquanto @DynamoDBIndexRangeKey representa a chave de classificação (Message) do índice.

Agora, você pode usar DynamoDBMapper para consultar o índice, recuperando um subconjunto das mensagens que foram postadas por um usuário específico. É necessário especificar withIndexName para que o DynamoDB saiba qual índice deve consultar. O código a seguir consulta um índice secundário global. Como um índice secundário global oferece suporte para leituras eventualmente consistentes, mas não para leituras fortemente consistentes, é necessário especificar withConsistentRead(false).

HashMap<String, AttributeValue> eav = new HashMap<String, AttributeValue>(); eav.put(":v1", new AttributeValue().withS("User A")); eav.put(":v2", new AttributeValue().withS("DynamoDB")); DynamoDBQueryExpression<PostedByMessage> queryExpression = new DynamoDBQueryExpression<PostedByMessage>() .withIndexName("PostedBy-Message-Index") .withConsistentRead(false) .withKeyConditionExpression("PostedBy = :v1 and begins_with(Message, :v2)") .withExpressionAttributeValues(eav); List<PostedByMessage> iList = mapper.query(PostedByMessage.class, queryExpression);

A consulta retorna uma coleção de objetos PostedByMessage.

queryPage

Consulta uma tabela ou um índice secundário e retorna uma única página de resultados correspondentes. Como acontece com o método query, você deve especificar um valor de chave de partição e um filtro de consulta que seja aplicado ao atributo de chave de classificação. No entanto, queryPage retorna somente a primeira "página" de dados, ou seja, a quantidade de dados que caberá em 1 MB

scan

Verifica uma tabela ou um índice secundário inteiro. Você tem a opção de especificar uma FilterExpression para filtrar o conjunto de resultados.

Vamos supor que você tenha uma tabela, Reply, que armazena as respostas de tópicos de fórum. Cada assunto de tópico pode ter 0 ou mais respostas. A chave primária da tabela Reply consiste nos campos Id e ReplyDateTime, em que Id é a chave de partição e ReplyDateTime é a chave de classificação da chave primária.

Reply ( Id, ReplyDateTime, ... )

Se você mapeou uma classe Java para a tabela Reply, será possível usar DynamoDBMapper para verificar essa tabela. Por exemplo, o código Java a seguir verifica a tabela Reply inteira, retornando somente as respostas de um determinado ano.

exemplo

HashMap<String, AttributeValue> eav = new HashMap<String, AttributeValue>(); eav.put(":v1", new AttributeValue().withS("2015")); DynamoDBScanExpression scanExpression = new DynamoDBScanExpression() .withFilterExpression("begins_with(ReplyDateTime,:v1)") .withExpressionAttributeValues(eav); List<Reply> replies = mapper.scan(Reply.class, scanExpression);

Por padrão, o método scan retorna uma coleção de "carregamento preguiçoso". Ele inicialmente retorna apenas uma página de resultados e, em seguida, faz uma chamada de serviço para a próxima página, se necessário. Para obter todos os itens correspondentes, faça uma iteração na coleção replies.

Observe que chamar o método size() na coleção carregará todos os resultados para fornecer uma contagem precisa. Isso pode fazer com que uma grande quantidade de throughput provisionado seja consumida, e em uma tabela muito grande pode até esgotar toda a memória na JVM.

Para verificar um índice, você deve primeiro modelá-lo como uma classe de mapeador. Suponha que a tabela Reply tenha um índice global secundário chamado PostedBy-Message-Index. A chave de partição para esse índice é PostedBy, e a chave de classificação é Message. Uma classe de mapeador para esse índice é mostrada na seção query. Ela usa as anotações @DynamoDBIndexHashKey e @DynamoDBIndexRangeKey para especificar a chave de classificação e a chave de partição do índice.

O código de exemplo a seguir verifica PostedBy-Message-Index. Ele não usa um filtro de verificação e, portanto, todos os itens no índice são retornados para você.

DynamoDBScanExpression scanExpression = new DynamoDBScanExpression() .withIndexName("PostedBy-Message-Index") .withConsistentRead(false); List<PostedByMessage> iList = mapper.scan(PostedByMessage.class, scanExpression); Iterator<PostedByMessage> indexItems = iList.iterator();

scanPage

Verifica uma tabela ou um índice secundário e retorna uma única página de resultados correspondentes. Como com o método scan, você pode especificar opcionalmente um FilterExpression para filtrar o conjunto de resultados. No entanto, scanPage retorna somente a primeira "página" de dados, ou seja, a quantidade de dados que cabe em 1 MB

parallelScan

Realiza uma verificação paralela de uma tabela ou de um índice secundário inteiro. Você especifica um número de segmentos lógicos para a tabela, juntamente com uma expressão de verificação para filtrar os resultados. O parallelScan divide a tarefa de verificação entre vários trabalhadores, um para cada segmento lógico. Por sua vez, esses trabalhadores processam os dados em paralelo e retornam os resultados.

O exemplo de código Java a seguir realiza uma verificação paralela na tabela Product.

int numberOfThreads = 4; Map<String, AttributeValue> eav = new HashMap<String, AttributeValue>(); eav.put(":n", new AttributeValue().withN("100")); DynamoDBScanExpression scanExpression = new DynamoDBScanExpression() .withFilterExpression("Price <= :n") .withExpressionAttributeValues(eav); List<Product> scanResult = mapper.parallelScan(Product.class, scanExpression, numberOfThreads);

Para obter um exemplo de código Java ilustrando o uso de parallelScan, consulte Exemplo: consulta e verificação.

batchSave

Salva os objetos em uma ou mais tabelas usando uma ou mais chamadas para o método AmazonDynamoDB.batchWriteItem. Esse método não fornece garantias de transação.

O código Java a seguir salva dois itens (livros) na tabela ProductCatalog.

Book book1 = new Book(); book1.id = 901; book1.productCategory = "Book"; book1.title = "Book 901 Title"; Book book2 = new Book(); book2.id = 902; book2.productCategory = "Book"; book2.title = "Book 902 Title"; mapper.batchSave(Arrays.asList(book1, book2));

batchLoad

Recupera vários itens de uma ou mais tabelas usando suas chaves primárias.

O seguinte código Java recupera dois itens de duas tabelas diferentes.

ArrayList<Object> itemsToGet = new ArrayList<Object>(); ForumItem forumItem = new ForumItem(); forumItem.setForumName("Amazon DynamoDB"); itemsToGet.add(forumItem); ThreadItem threadItem = new ThreadItem(); threadItem.setForumName("Amazon DynamoDB"); threadItem.setSubject("Amazon DynamoDB thread 1 message text"); itemsToGet.add(threadItem); Map<String, List<Object>> items = mapper.batchLoad(itemsToGet);

batchDelete

Exclui objetos de uma ou mais tabelas usando uma ou mais chamadas para o método AmazonDynamoDB.batchWriteItem. Esse método não fornece garantias de transação.

O código Java a seguir exclui dois itens (livros) na tabela ProductCatalog.

Book book1 = mapper.load(Book.class, 901); Book book2 = mapper.load(Book.class, 902); mapper.batchDelete(Arrays.asList(book1, book2));

batchWrite

Salva e exclui objetos em/de uma ou mais tabelas usando uma ou mais chamadas para o método AmazonDynamoDB.batchWriteItem. Esse método não oferece garantias de transação ou suporte para versionamento (inserções ou exclusões condicionais).

O código Java a seguir grava um novo item na tabela Forum, grava um novo item na tabela Thread e exclui um item da tabela ProductCatalog.

// Create a Forum item to save Forum forumItem = new Forum(); forumItem.name = "Test BatchWrite Forum"; // Create a Thread item to save Thread threadItem = new Thread(); threadItem.forumName = "AmazonDynamoDB"; threadItem.subject = "My sample question"; // Load a ProductCatalog item to delete Book book3 = mapper.load(Book.class, 903); List<Object> objectsToWrite = Arrays.asList(forumItem, threadItem); List<Book> objectsToDelete = Arrays.asList(book3); mapper.batchWrite(objectsToWrite, objectsToDelete);

transactionWrite

Salva e exclui objetos em/de uma ou mais tabelas usando uma chamada para o método AmazonDynamoDB.transactWriteItems.

Para obter uma lista de exceções específicas de transação, consulte Erros TransactWriteItems.

Para obter mais informações sobre transações do DynamoDB e as garantias de ACID (atomicidade, consistência, isolamento e durabilidade) fornecidas, consulte Amazon DynamoDB Transactions.

nota

Esse método não oferece suporte ao seguinte:

O código Java a seguir grava um novo item em cada uma das tabelas Forum e Thread, de forma transacional.

Thread s3ForumThread = new Thread(); s3ForumThread.forumName = "S3 Forum"; s3ForumThread.subject = "Sample Subject 1"; s3ForumThread.message = "Sample Question 1"; Forum s3Forum = new Forum(); s3Forum.name = "S3 Forum"; s3Forum.category = "Amazon Web Services"; s3Forum.threads = 1; TransactionWriteRequest transactionWriteRequest = new TransactionWriteRequest(); transactionWriteRequest.addPut(s3Forum); transactionWriteRequest.addPut(s3ForumThread); mapper.transactionWrite(transactionWriteRequest);

transactionLoad

Carrega objetos de uma ou mais tabelas usando uma chamada para o método AmazonDynamoDB.transactGetItems.

Para obter uma lista de exceções específicas de transação, consulte Erros TransactGetItems.

Para obter mais informações sobre transações do DynamoDB e as garantias de ACID (atomicidade, consistência, isolamento e durabilidade) fornecidas, consulte Amazon DynamoDB Transactions.

O código Java a seguir carrega um item de cada uma das tabelas Forum e Thread, de forma transacional.

Forum dynamodbForum = new Forum(); dynamodbForum.name = "DynamoDB Forum"; Thread dynamodbForumThread = new Thread(); dynamodbForumThread.forumName = "DynamoDB Forum"; TransactionLoadRequest transactionLoadRequest = new TransactionLoadRequest(); transactionLoadRequest.addLoad(dynamodbForum); transactionLoadRequest.addLoad(dynamodbForumThread); mapper.transactionLoad(transactionLoadRequest);

count

Avalia a expressão de verificação especificada e retorna a contagem de itens correspondentes. Dados de itens não são retornados.

generateCreateTableRequest

Analisa uma classe POJO que representa um tabela do DynamoDB e retorna um CreateTableRequest para essa tabela.

Cria um link para um objeto no Amazon S3. Você deve especificar um nome de bucket e um nome de chave que identifique exclusivamente o objeto no bucket.

Para usar createS3Link, a sua classe de mapeador deve definir métodos getter e setter. O exemplo de código a seguir ilustra isso, adicionando um novo atributo e métodos getter/setter à classe CatalogItem:

@DynamoDBTable(tableName="ProductCatalog") public class CatalogItem { ... public S3Link productImage; .... @DynamoDBAttribute(attributeName = "ProductImage") public S3Link getProductImage() { return productImage; } public void setProductImage(S3Link productImage) { this.productImage = productImage; } ... }

O código Java a seguir define um novo item a ser gravado na tabela Product. O item inclui um link para uma imagem do produto. Os dados da imagem são carregados no Amazon S3.

CatalogItem item = new CatalogItem(); item.id = 150; item.title = "Book 150 Title"; String myS3Bucket = "myS3bucket"; String myS3Key = "productImages/book_150_cover.jpg"; item.setProductImage(mapper.createS3Link(myS3Bucket, myS3Key)); item.getProductImage().uploadFrom(new File("/file/path/book_150_cover.jpg")); mapper.save(item);

A classe S3Link fornece muitos outros métodos para manipular objetos no Amazon S3. Para obter mais informações, consulte os Javadocs para S3Link.

getS3ClientCache

Retorna o S3ClientCache subjacente para acessar o Amazon S3. Um S3ClientCache é um Mapa inteligente para objetos AmazonS3Client. Se você tem vários clientes, um S3ClientCache pode ajudá-lo a manter esses clientes organizados por região da AWS e pode criar novos clientes do Amazon S3 sob demanda.