DynamoDB PutItem em Java (AWS SDK v2)
PutItem grava um item inteiro e substitui qualquer item existente com a mesma chave primária (ações baseadas em item cobre como isso difere de UpdateItem). No AWS SDK for Java 2.x cada atributo entra em um PutItemRequest como um AttributeValue tipado, e o builder vai deixar você construir um que não tem como ser válido.
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.ConditionalCheckFailedException;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
public class PutItemExample {
public static void main(String[] args) {
try (DynamoDbClient ddb = DynamoDbClient.builder()
.region(Region.US_EAST_1)
.build()) {
Map<String, AttributeValue> item = new HashMap<>();
item.put("Artist", AttributeValue.builder().s("Arturo Sandoval").build());
item.put("SongTitle", AttributeValue.builder().s("Cubano Chant").build());
item.put("AlbumTitle", AttributeValue.builder().s("Danzon").build());
item.put("Year", AttributeValue.builder().n("1994").build());
item.put("Awards", AttributeValue.builder().n("0").build());
Map<String, String> names = new HashMap<>();
names.put("#cond0", "Artist");
names.put("#cond1", "SongTitle");
PutItemRequest request = PutItemRequest.builder()
.tableName("Music")
.item(item)
.conditionExpression("attribute_not_exists(#cond0) AND attribute_not_exists(#cond1)")
.expressionAttributeNames(names)
.build();
try {
ddb.putItem(request);
System.out.println("Song written");
} catch (ConditionalCheckFailedException e) {
System.out.println("A song with that key already exists — not overwritten");
}
} catch (DynamoDbException e) {
System.err.println(e.getMessage());
}
}
}Explicação
AttributeValue.builder().build() compila. E também é inenviável. O builder não tem nenhum campo obrigatório, então um atributo em que você esqueceu o .s(...) passa perfeitamente na checagem de tipos e falha no serviço:
DynamoDbException | ValidationException | Supplied AttributeValue is empty, must contain exactly one of the supported datatypesEsta é a forma específica em Java de um erro que outros SDKs tornam impossível: os types.AttributeValueMember* de Go são tipos separados, então não há nada para deixar sem definir. Veja "Supplied AttributeValue is empty" para a correção mais ampla.
Um null de Java entregue a .s(...) não vira um NULL do DynamoDB. Esta é a versão que morde de verdade, porque parece um valor:
AttributeValue.builder().s(customer.getNotes()).build() // getNotes() returned nullNenhum NullPointerException é lançado na construção. O builder simplesmente não registra nada, e a requisição falha com a mensagem Supplied AttributeValue is empty idêntica, apontando para um atributo do qual você nunca suspeitou. Se você quer um null de verdade, ele é AttributeValue.builder().nul(true).build(); na maioria das vezes o que você quer é omitir a entrada. Repare que isso é o oposto do SDK de Go, onde um ponteiro nil é marshalled para NULL e cria um atributo silenciosamente; ambos foram executados contra o mesmo motor no mesmo dia.
getMessage() não é a mensagem do serviço. O SDK acrescenta o seu próprio contexto, então a string fica:
The conditional request failed (Service: DynamoDb, Status Code: 400, Request ID: 77a08ef1-a3a9-4f97-b309-4cb0741edd1a) (SDK Attempt Count: 1)Faça match em e.awsErrorDetails().errorCode() e leia e.awsErrorDetails().errorMessage() para obter o texto puro. Qualquer coisa que compare getMessage() com um literal quebra em um retry, que muda a contagem de tentativas.
Capture ConditionalCheckFailedException antes de DynamoDbException, e leia o que ela carrega. Ela estende DynamoDbException, então ordenar os blocos catch ao contrário torna o handler específico inalcançável. No objeto capturado: statusCode() retornou 400 e retryable() retornou false, que é a resposta honesta para uma rejeição de regra de negócio. Adicione .returnValuesOnConditionCheckFailure("ALL_OLD") à requisição e e.item() volta preenchido com o item que bloqueou a escrita (cinco atributos na execução acima, Year como AttributeValue(N=1994)), de modo que você não precisa de um getItem posterior para descobrir quem venceu.
Números entram como strings via .n(...). O tipo N do DynamoDB é texto decimal no protocolo, que é o que impede 1994 de virar um double. .n(String.valueOf(year)) é o idioma; não existe sobrecarga .n(int) para recorrer.
O client é Closeable, e de vida longa. O try-with-resources acima está certo para um programa de execução única e errado para um serviço: DynamoDbClient é dono de um pool de conexões HTTP e é thread-safe, então construa um por aplicação e deixe-o viver. Construir um por requisição é o bug de desempenho em Java mais comum nesta API.
Prefere beans a mapas de AttributeValue? O DynamoDB Enhanced Client (software.amazon.awssdk.enhanced.dynamodb) mapeia uma classe anotada direto para um item, o que elimina completamente a armadilha do builder vazio. Isso custa uma varredura reflexiva de TableSchema.fromBean na inicialização, que StaticTableSchema evita, se isso importar para você.
Faça isso visualmente
Toda falha acima começa com valores tipados construídos à mão. O conversor de JSON do DynamoDB gratuito pega JSON comum e devolve a forma tipada, para que você veja exatamente com o que o item deve parecer no protocolo antes de escrever um único AttributeValue.builder().
Para gravar e editar itens contra as suas próprias tabelas — um formulário por atributo, seletores de tipo, copiar o resultado de volta como Java — baixe o DynoTable.
Exemplos relacionados
- DynamoDB PutItem em Go — a mesma escrita condicional com o AWS SDK for Go v2, onde um ponteiro nil falha do jeito oposto.
- DynamoDB UpdateItem em Java — altere atributos específicos em vez de substituir o item.
- Expressões de condição do DynamoDB —
attribute_not_exists, bloqueio otimista e mais. - DynamoDB ConditionalCheckFailedException — o que a condição de criar-somente lança quando o item já existe.
- DynamoDB ValidationException — o pega-tudo para um item ou expressão malformados.
Referências
- PutItem — Amazon DynamoDB API Reference
- Use PutItem with an AWS SDK or CLI — Amazon DynamoDB Developer Guide
- DynamoDbClient — AWS SDK for Java 2.x API Reference
- AttributeValue — AWS SDK for Java 2.x API Reference
- PutItemRequest — AWS SDK for Java 2.x API Reference
- Condition expressions — Amazon DynamoDB Developer Guide
Reproduzido em 2026-07-28 com o AWS SDK for Java 2.49.4 no OpenJDK 26.0.1, contra o DynamoDB Local (amazon/dynamodb-local) na porta 9000. O texto da exceção, o código de status e o conteúdo do item acima são saída capturada, copiada literalmente.