UpdateItem do DynamoDB em Java (AWS SDK v2)
A atualização em si é uma chamada de builder. O que custa tempo aos desenvolvedores Java é tudo em volta dela: um objeto de resposta que nunca retorna null, uma hierarquia de exceções em que a falha interessante é subclasse daquela que você provavelmente capturou, e um cliente de alto nível que não consegue expressar esta operação de jeito nenhum.
Código
import java.util.HashMap;
import java.util.Map;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.ReturnValue;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemResponse;
public class UpdateItemExample {
public static void main(String[] args) {
try (DynamoDbClient ddb = DynamoDbClient.builder()
.region(Region.US_EAST_1)
.build()) {
Map<String, AttributeValue> key = new HashMap<>();
key.put("Artist", AttributeValue.builder().s("Arturo Sandoval").build());
key.put("SongTitle", AttributeValue.builder().s("Cubano Chant").build());
Map<String, String> names = new HashMap<>();
names.put("#upd0", "Genre");
names.put("#upd1", "Year");
names.put("#upd2", "Awards");
Map<String, AttributeValue> values = new HashMap<>();
values.put(":updValue0", AttributeValue.builder().s("Latin Jazz").build());
values.put(":updValue1", AttributeValue.builder().n("1994").build());
values.put(":updValue2", AttributeValue.builder().n("1").build());
UpdateItemRequest request = UpdateItemRequest.builder()
.tableName("Music")
.key(key)
.updateExpression("SET #upd0 = :updValue0, #upd1 = :updValue1 ADD #upd2 :updValue2")
.expressionAttributeNames(names)
.expressionAttributeValues(values)
.returnValues(ReturnValue.ALL_NEW)
.build();
UpdateItemResponse response = ddb.updateItem(request);
System.out.println(response.attributes()); // the item after the update
} catch (DynamoDbException e) {
System.err.println(e.getMessage());
}
}
}Explicação
AttributeValue.builder().n("1994")recebe umaString, e o mais curtoAttributeValue.fromN("1994")também. Não existe sobrecargan(int), porque números do DynamoDB guardam 38 dígitos significativos e nenhum primitivo do Java guarda. Na volta,attributes().get("Awards").n()também é umaString; o acessor do tipo errado retorna null em vez de lançar erro, então.s()em um número é um null silencioso, e.type()te diz qual deles está definido.response.attributes()nunca retorna null. ComReturnValue.NONEele retorna umDefaultSdkAutoConstructMapque é vazio mas não nulo, então uma checagem de null nunca dispara e uma checagem deisEmpty()não consegue distinguir "o serviço não enviou nada" de "o item não tem atributos". OhasAttributes()gerado é o acessor que sabe a diferença. Todo membro de coleção neste SDK tem um.O builder verifica os tipos de tudo, menos da parte que importa.
updateExpression(String)aceita qualquer string; o compilador não distingueSETde um erro de digitação, então erros de expressão são 400s em tempo de execução.ADD #upd2 :updValue2é o incremento atômico, umaconditionExpressiondeattribute_exists(Artist)torna a chamada somente-atualização, e a gramática está em expressões de atualização.Prefira a expressão ao mapa legado
attributeUpdates. Exemplos mais antigos ainda o mostram; ele não consegue expressar múltiplos tipos de cláusula, aliases ou uma condição em uma única requisição.getMessage()não é a mensagem do serviço. O SDK acrescenta seus próprios detalhes de transporte:The conditional request failed (Service: DynamoDb, Status Code: 400, Request ID: d99b117c-edd6-4dc9-8d3a-a5fa4fe9666c) (SDK Attempt Count: 1)Logue isso se você quiser o request ID para um chamado de suporte. Compare por
awsErrorDetails().errorCode()em vez disso, e useawsErrorDetails().errorMessage()quando quiser a string pura.
A ordem dos catches importa mais que o normal aqui
ConditionalCheckFailedException extends DynamoDbException, então um catch (DynamoDbException e) colocado primeiro engole justamente a falha na qual você quase certamente queria ramificar. Capture o tipo específico primeiro, e aproveite para pegar o item:
} catch (ConditionalCheckFailedException e) {
// with .returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD)
if (e.hasItem()) {
Map<String, AttributeValue> loser = e.item(); // the item as it actually was
}
} catch (DynamoDbException e) {
// everything else
}e.retryable() é false neste caso, o que está correto: tentar de novo uma condição que falhou apenas falha de novo.
A assimetria a lembrar é que ValidationException não tem classe neste SDK. Procure no dynamodb-2.35.9.jar e não há nada para capturar. Uma palavra reservada, uma expressão malformada, uma chave parcial: todas chegam como um DynamoDbException comum cujo awsErrorDetails().errorCode() por acaso diz ValidationException. Em uma linguagem estaticamente tipada isso é uma lacuna incômoda, e significa que erros de expressão viram comparações de string em tempo de execução.
É por isso que as palavras reservadas do DynamoDB merecem uma passada antes de você entregar, e não depois: a lista chega a 573 entradas e inclui Year, Name e Status, nenhuma das quais parece perigosa em um bean Java. Para navegar pela tabela crua em vez do bean mapeado por cima dela, baixe o DynoTable.
O enhanced client não consegue expressar isso
Se o resto do seu acesso a dados passa pelo DynamoDbEnhancedClient e beans anotados, esta operação é a que te joga de volta para o DynamoDbClient. A reflexão sobre UpdateItemEnhancedRequest.Builder revela item, conditionExpression, ignoreNulls, ignoreNullsMode, returnValues, returnValuesOnConditionCheckFailure, returnConsumedCapacity e returnItemCollectionMetrics. Não há método que aceite uma expressão de atualização.
A consequência prática é o contador atômico. ADD #upd2 :updValue2 incrementa Awards no lado do servidor sem leitura prévia; o enhanced client te dá um bean mapeado e ignoreNulls para decidir se campos ausentes são removidos, e nada que compile para ADD. Ler-modificar-escrever através de um bean é uma corrida de atualização perdida sob concorrência, que é exatamente o que o trecho desta página evita.
Exemplos relacionados
- UpdateItem do DynamoDB em Go — a mesma atualização com o AWS SDK for Go v2.
- PutItem do DynamoDB em Java — substitua o item inteiro em vez disso.
- Expressões de atualização do DynamoDB —
SET,ADD,REMOVE,DELETEe idiomas. - Entendendo ReturnValues — o que cada opção de
ReturnValueste dá. - "Attribute name is a reserved keyword" — por que o mapa de aliases aqui não é opcional.
- Erros de sintaxe "Invalid UpdateExpression" — os erros comuns de sintaxe de SET/ADD, decodificados.
Referências
- UpdateItem — Amazon DynamoDB API Reference
- Use UpdateItem with an AWS SDK or CLI — Amazon DynamoDB Developer Guide
- DynamoDbClient — AWS SDK for Java 2.x API Reference
- UpdateItemRequest — AWS SDK for Java 2.x API Reference
- Update expressions — Amazon DynamoDB Developer Guide
Verificado pela última vez em 2026-07-28 contra a documentação oficial da AWS vinculada acima.