Realizar operações de transação - AWS SDK for Java 2.x

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

Realizar operações de transação

A API do Cliente Aprimorado do DynamoDB fornece o transactGetItems() e os métodos transactWriteItems(). Os métodos de transação do SDK para Java fornecem atomicidade, consistência, isolamento e durabilidade (ACID) nas tabelas do DynamoDB, ajudando você a manter a correção dos dados em suas aplicações.

Exemplo de transactGetItems()

O método transactGetItems() aceita até 100 solicitações individuais de itens. Todos os itens são lidos em uma única transação atômica. O Guia do desenvolvedor do Amazon DynamoDB tem informações sobre as condições que causam a falha do método transactGetItems() e também sobre o nível de isolamento usado ao chamar transactGetItem().

Depois da linha de comentário 1 no exemplo a seguir, o código chama o método transactGetItems() com um parâmetro builder. O addGetItem() do construtor é chamado três vezes com um objeto de dados contendo os valores-chave que o SDK usará para gerar a solicitação final.

A solicitação retorna uma lista de objetos Document após a linha de comentário 2. A lista de documentos que é retornada contém instâncias de Documentos não nulas dos dados do item na mesma ordem solicitada. O método Document.getItem(MappedTableResource<T> mappedTableResource) converte um objeto não digitado em um objeto Document Java digitado se os dados do item forem retornados, caso contrário, o método retornará nulo.

public static void transactGetItemsExample(DynamoDbEnhancedClient enhancedClient, DynamoDbTable<ProductCatalog> catalogTable, DynamoDbTable<MovieActor> movieActorTable) { // 1. Request three items from two tables using a builder. final List<Document> documents = enhancedClient.transactGetItems(b -> b .addGetItem(catalogTable, Key.builder().partitionValue(2).sortValue("Title 55").build()) .addGetItem(movieActorTable, Key.builder().partitionValue("Sophie's Choice").sortValue("Meryl Streep").build()) .addGetItem(movieActorTable, Key.builder().partitionValue("Blue Jasmine").sortValue("Cate Blanchett").build()) .build()); // 2. A list of Document objects is returned in the same order as requested. ProductCatalog title55 = documents.get(0).getItem(catalogTable); if (title55 != null) { logger.info(title55.toString()); } MovieActor sophiesChoice = documents.get(1).getItem(movieActorTable); if (sophiesChoice != null) { logger.info(sophiesChoice.toString()); } // 3. The getItem() method returns null if the Document object contains no item from DynamoDB. MovieActor blueJasmine = documents.get(2).getItem(movieActorTable); if (blueJasmine != null) { logger.info(blueJasmine.toString()); } }

As tabelas do DynamoDB contêm os seguintes itens antes da execução do exemplo de código.

ProductCatalog{id=2, title='Title 55', isbn='orig_isbn', authors=[b, g], price=10} MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}

A saída a seguir é registrada. Se um item for solicitado, mas não encontrado, ele não será retornado, como é o caso da solicitação do filme nomeado Blue Jasmine.

ProductCatalog{id=2, title='Title 55', isbn='orig_isbn', authors=[b, g], price=10} MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}

Exemplos do transactWriteItems()

O transactWriteItems() aceita até 100 ações de colocar, atualizar ou excluir em uma única transação atômica em várias tabelas. O Guia do desenvolvedor do Amazon DynamoDB contém detalhes sobre restrições e condições de falha da operação do serviço subjacente do DynamoDB.

Exemplo básico

No exemplo a seguir, quatro operações são solicitadas para duas tabelas. As classes de modelo correspondentes ProductCatalog e MovieActor foram mostradas anteriormente.

Cada uma das três operações possíveis: colocar, atualizar e excluir usa um parâmetro de solicitação dedicado para especificar os detalhes.

O código após a linha de comentário 1 mostra a variação simples do método addPutItem(). O método aceita um objeto MappedTableResource e a instância do objeto de dados onde colocar. A declaração após a linha de comentário 2 mostra a variação que aceita uma instância TransactPutItemEnhancedRequest. Essa variação permite adicionar mais opções na solicitação, como uma expressão de condição. Um exemplo subsequente mostra uma expressão de condição para uma operação individual.

Uma operação de atualização é solicitada após a linha de comentários 3. TransactUpdateItemEnhancedRequest tem um método ignoreNulls() que permite configurar o que o SDK faz com os valores null no objeto modelo. Se o método ignoreNulls() retornar true, o SDK não removerá os valores dos atributos da tabela para os atributos do objeto de dados que são null. Se o método ignoreNulls() retornar false, o SDK solicitará que o serviço do DynamoDB remova os atributos do item na tabela. O valor padrão para ignoreNulls é false.

A afirmação após a linha de comentário 4 mostra a variação de uma solicitação de exclusão que usa um objeto de dados. O cliente aprimorado extrai os valores-chave antes de enviar a solicitação final.

public static void transactWriteItems(DynamoDbEnhancedClient enhancedClient, DynamoDbTable<ProductCatalog> catalogTable, DynamoDbTable<MovieActor> movieActorTable) { enhancedClient.transactWriteItems(b -> b // 1. Simplest variation of put item request. .addPutItem(catalogTable, getProductCatId2()) // 2. Put item request variation that accommodates condition expressions. .addPutItem(movieActorTable, TransactPutItemEnhancedRequest.builder(MovieActor.class) .item(getMovieActorStreep()) .conditionExpression(Expression.builder().expression("attribute_not_exists (movie)").build()) .build()) // 3. Update request that does not remove attribute values on the table if the data object's value is null. .addUpdateItem(catalogTable, TransactUpdateItemEnhancedRequest.builder(ProductCatalog.class) .item(getProductCatId4ForUpdate()) .ignoreNulls(Boolean.TRUE) .build()) // 4. Variation of delete request that accepts a data object. The key values are extracted for the request. .addDeleteItem(movieActorTable, getMovieActorBlanchett()) ); }

Os métodos auxiliares a seguir fornecem os objetos de dados para os parâmetros add*Item.

public static ProductCatalog getProductCatId2() { return ProductCatalog.builder() .id(2) .isbn("1-565-85698") .authors(new HashSet<>(Arrays.asList("a", "b"))) .price(BigDecimal.valueOf(30.22)) .title("Title 55") .build(); } public static ProductCatalog getProductCatId4ForUpdate() { return ProductCatalog.builder() .id(4) .price(BigDecimal.valueOf(40.00)) .title("Title 1") .build(); } public static MovieActor getMovieActorBlanchett() { MovieActor movieActor = new MovieActor(); movieActor.setActorName("Cate Blanchett"); movieActor.setMovieName("Tar"); movieActor.setActingYear(2022); movieActor.setActingAward("Best Actress"); movieActor.setActingSchoolName("National Institute of Dramatic Art"); return movieActor; } public static MovieActor getMovieActorStreep() { MovieActor movieActor = new MovieActor(); movieActor.setActorName("Meryl Streep"); movieActor.setMovieName("Sophie's Choice"); movieActor.setActingYear(1982); movieActor.setActingAward("Best Actress"); movieActor.setActingSchoolName("Yale School of Drama"); return movieActor; }

As tabelas do DynamoDB contêm os seguintes itens antes da execução do exemplo de código.

1 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10} 2 | MovieActor{movieName='Tar', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2022, actingSchoolName='National Institute of Dramatic Art'}

Os itens a seguir estão nas tabelas após a conclusão da execução do código.

3 | ProductCatalog{id=2, title='Title 55', isbn='1-565-85698', authors=[a, b], price=30.22} 4 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=40.0} 5 | MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}

O item na linha 2 foi excluído e as linhas 3 e 5 mostram os itens que foram colocados. A linha 4 mostra a atualização da linha 1. O valor price é o único valor que foi alterado no item. Se ignoreNulls() tivesse retornado false, a linha 4 ficaria como a seguinte linha:

ProductCatalog{id=4, title='Title 1', isbn='null', authors=null, price=40.0}

Exemplo de verificação de condição

O exemplo a seguir mostra o uso de uma verificação de condição. Uma verificação de condição é usada para verificar se um item existe ou para verificar a condição de atributos específicos de um item no banco de dados. O item verificado na verificação de condição não pode ser usado em outra operação na transação.

nota

Você não pode visar o mesmo item com várias operações dentro da mesma transação. Por exemplo, você não pode executar uma verificação de condição e também tentar atualizar o item na mesma transação.

O exemplo mostra um tipo de cada operação em uma solicitação transacional de itens de gravação. Depois da linha de comentário 2, o método addConditionCheck() fornece a condição que falha na transação se o parâmetro conditionExpression for avaliado como false. A expressão de condição que é retornada do método mostrado no bloco de métodos auxiliares verifica se o ano de premiação do filme Sophie's Choice não é igual a 1982. Se for igual, a expressão será avaliada como false e a transação falhará.

Este guia discutirá expressões em profundidade em outro tópico.

public static void conditionCheckFailExample(DynamoDbEnhancedClient enhancedClient, DynamoDbTable<ProductCatalog> catalogTable, DynamoDbTable<MovieActor> movieActorTable) { try { enhancedClient.transactWriteItems(b -> b // 1. Perform one of each type of operation with the next three methods. .addPutItem(catalogTable, TransactPutItemEnhancedRequest.builder(ProductCatalog.class) .item(getProductCatId2()).build()) .addUpdateItem(catalogTable, TransactUpdateItemEnhancedRequest.builder(ProductCatalog.class) .item(getProductCatId4ForUpdate()) .ignoreNulls(Boolean.TRUE).build()) .addDeleteItem(movieActorTable, TransactDeleteItemEnhancedRequest.builder() .key(b1 -> b1 .partitionValue(getMovieActorBlanchett().getMovieName()) .sortValue(getMovieActorBlanchett().getActorName())).build()) // 2. Add a condition check on a table item that is not involved in another operation in this request. .addConditionCheck(movieActorTable, ConditionCheck.builder() .conditionExpression(buildConditionCheckExpression()) .key(k -> k .partitionValue("Sophie's Choice") .sortValue("Meryl Streep")) // 3. Specify the request to return existing values from the item if the condition evaluates to true. .returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD) .build()) .build()); // 4. Catch the exception if the transaction fails and log the information. } catch (TransactionCanceledException ex) { ex.cancellationReasons().stream().forEach(cancellationReason -> { logger.info(cancellationReason.toString()); }); } }

Os métodos auxiliares a seguir são usados no exemplo de código anterior.

private static Expression buildConditionCheckExpression() { Map<String, AttributeValue> expressionValue = Map.of( ":year", numberValue(1982)); return Expression.builder() .expression("actingyear <> :year") .expressionValues(expressionValue) .build(); } public static ProductCatalog getProductCatId2() { return ProductCatalog.builder() .id(2) .isbn("1-565-85698") .authors(new HashSet<>(Arrays.asList("a", "b"))) .price(BigDecimal.valueOf(30.22)) .title("Title 55") .build(); } public static ProductCatalog getProductCatId4ForUpdate() { return ProductCatalog.builder() .id(4) .price(BigDecimal.valueOf(40.00)) .title("Title 1") .build(); } public static MovieActor getMovieActorBlanchett() { MovieActor movieActor = new MovieActor(); movieActor.setActorName("Cate Blanchett"); movieActor.setMovieName("Blue Jasmine"); movieActor.setActingYear(2013); movieActor.setActingAward("Best Actress"); movieActor.setActingSchoolName("National Institute of Dramatic Art"); return movieActor; }

As tabelas do DynamoDB contêm os seguintes itens antes da execução do exemplo de código.

1 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10} 2 | MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'} 3 | MovieActor{movieName='Tar', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2022, actingSchoolName='National Institute of Dramatic Art'}

Os itens a seguir estão nas tabelas após a conclusão da execução do código.

ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10} MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'} MovieActor{movieName='Tar', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2022, actingSchoolName='National Institute of Dramatic Art'}

Os itens permanecem inalterados nas tabelas porque a transação falhou. O valor actingYear do filme Sophie's Choice é 1982, conforme mostrado na linha 2 dos itens na tabela antes da chamada do método transactWriteItem().

Para capturar as informações de cancelamento da transação, inclua a chamada do método transactWriteItems() em um bloco try e catch o TransactionCanceledException. Depois da linha de comentário 4 do exemplo, o código registra cada objeto CancellationReason. Como o código após a linha de comentário 3 do exemplo especifica que os valores devem ser retornados para o item que causou a falha da transação, o registro exibe os valores brutos do banco de dados do item do filme Sophie's Choice.

CancellationReason(Code=None) CancellationReason(Code=None) CancellationReason(Code=None) CancellationReason(Item={actor=AttributeValue(S=Meryl Streep), movie=AttributeValue(S=Sophie's Choice), actingaward=AttributeValue(S=Best Actress), actingyear=AttributeValue(N=1982), actingschoolname=AttributeValue(S=Yale School of Drama)}, ¬ Code=ConditionalCheckFailed, Message=The conditional request failed.)

Exemplo de condição de operação única

O exemplo a seguir mostra o uso de uma condição em uma única operação em uma solicitação de transação. A operação de exclusão após a linha de comentário 1 contém uma condição que verifica o valor do item de destino da operação em relação ao banco de dados. Neste exemplo, a expressão de condição criada com o método auxiliar após a linha de comentário 2 especifica que o item deve ser excluído do banco de dados se o ano de atuação do filme não for igual a 2013.

As expressões serão discutidas posteriormente neste guia.

public static void singleOperationConditionFailExample(DynamoDbEnhancedClient enhancedClient, DynamoDbTable<ProductCatalog> catalogTable, DynamoDbTable<MovieActor> movieActorTable) { try { enhancedClient.transactWriteItems(b -> b .addPutItem(catalogTable, TransactPutItemEnhancedRequest.builder(ProductCatalog.class) .item(getProductCatId2()) .build()) .addUpdateItem(catalogTable, TransactUpdateItemEnhancedRequest.builder(ProductCatalog.class) .item(getProductCatId4ForUpdate()) .ignoreNulls(Boolean.TRUE).build()) // 1. Delete operation that contains a condition expression .addDeleteItem(movieActorTable, TransactDeleteItemEnhancedRequest.builder() .key((Key.Builder k) -> { MovieActor blanchett = getMovieActorBlanchett(); k.partitionValue(blanchett.getMovieName()) .sortValue(blanchett.getActorName()); }) .conditionExpression(buildDeleteItemExpression()) .returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD) .build()) .build()); } catch (TransactionCanceledException ex) { ex.cancellationReasons().forEach(cancellationReason -> logger.info(cancellationReason.toString())); } } // 2. Provide condition expression to check if 'actingyear' is not equal to 2013. private static Expression buildDeleteItemExpression() { Map<String, AttributeValue> expressionValue = Map.of( ":year", numberValue(2013)); return Expression.builder() .expression("actingyear <> :year") .expressionValues(expressionValue) .build(); }

Os métodos auxiliares a seguir são usados no exemplo de código anterior.

public static ProductCatalog getProductCatId2() { return ProductCatalog.builder() .id(2) .isbn("1-565-85698") .authors(new HashSet<>(Arrays.asList("a", "b"))) .price(BigDecimal.valueOf(30.22)) .title("Title 55") .build(); } public static ProductCatalog getProductCatId4ForUpdate() { return ProductCatalog.builder() .id(4) .price(BigDecimal.valueOf(40.00)) .title("Title 1") .build(); } public static MovieActor getMovieActorBlanchett() { MovieActor movieActor = new MovieActor(); movieActor.setActorName("Cate Blanchett"); movieActor.setMovieName("Blue Jasmine"); movieActor.setActingYear(2013); movieActor.setActingAward("Best Actress"); movieActor.setActingSchoolName("National Institute of Dramatic Art"); return movieActor; }

As tabelas do DynamoDB contêm os seguintes itens antes da execução do exemplo de código.

1 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10} 2 | MovieActor{movieName='Blue Jasmine', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2013, actingSchoolName='National Institute of Dramatic Art'}

Os itens a seguir estão nas tabelas após a conclusão da execução do código.

ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10} 2023-03-15 11:29:07 [main] INFO org.example.tests.TransactDemoTest:168 - MovieActor{movieName='Blue Jasmine', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2013, actingSchoolName='National Institute of Dramatic Art'}

Os itens permanecem inalterados nas tabelas porque a transação falhou. O valor actingYear do filme Blue Jasmine é 2013 como mostrado na linha 2 da lista de itens antes da execução do código de exemplo.

As linhas a seguir são registradas no console.

CancellationReason(Code=None) CancellationReason(Code=None) CancellationReason(Item={actor=AttributeValue(S=Cate Blanchett), movie=AttributeValue(S=Blue Jasmine), actingaward=AttributeValue(S=Best Actress), actingyear=AttributeValue(N=2013), actingschoolname=AttributeValue(S=National Institute of Dramatic Art)}, Code=ConditionalCheckFailed, Message=The conditional request failed)