DynamoDB PutItem en Java (AWS SDK v2)
PutItem écrit un élément entier et remplace tout élément existant portant la même clé primaire (les actions par élément expliquent en quoi cela diffère d'UpdateItem). Dans l'AWS SDK for Java 2.x, chaque attribut entre dans un PutItemRequest sous forme d'AttributeValue typé, et le builder te laissera en construire un qui ne peut pas être valide.
Code
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());
}
}
}Explication
AttributeValue.builder().build() compile. Et il est aussi inenvoyable. Le builder n'a aucun champ obligatoire : un attribut où tu as oublié le .s(...) passe parfaitement le typage et échoue au niveau du service :
DynamoDbException | ValidationException | Supplied AttributeValue is empty, must contain exactly one of the supported datatypesC'est la forme spécifiquement Java d'une erreur que d'autres SDK rendent impossible : les types.AttributeValueMember* de Go sont des types distincts, il n'y a donc rien à laisser non renseigné. Voir "Supplied AttributeValue is empty" pour le correctif général.
Un null Java passé à .s(...) ne devient pas un NULL DynamoDB. C'est la version qui mord vraiment, parce qu'elle a l'air d'une valeur :
AttributeValue.builder().s(customer.getNotes()).build() // getNotes() returned nullAucune NullPointerException n'est levée à la construction. Le builder n'enregistre simplement rien, et la requête échoue avec le message Supplied AttributeValue is empty identique, en pointant un attribut que tu n'avais pas soupçonné. Si tu veux un vrai null, c'est AttributeValue.builder().nul(true).build() ; le plus souvent, ce que tu veux, c'est omettre l'entrée. Note que c'est l'inverse du SDK Go, où un pointeur nil est marshallé en NULL et crée silencieusement un attribut ; les deux ont été exécutés sur le même moteur le même jour.
getMessage() n'est pas le message du service. Le SDK y ajoute son propre contexte, si bien que la chaîne est :
The conditional request failed (Service: DynamoDb, Status Code: 400, Request ID: 77a08ef1-a3a9-4f97-b309-4cb0741edd1a) (SDK Attempt Count: 1)Fais la correspondance sur e.awsErrorDetails().errorCode() et lis e.awsErrorDetails().errorMessage() pour le texte nu. Tout ce qui compare getMessage() à un littéral est cassé par une reprise, qui change le nombre de tentatives.
Attrape ConditionalCheckFailedException avant DynamoDbException, et lis ce qu'elle transporte. Elle étend DynamoDbException : ordonner les blocs catch dans l'autre sens rend le handler spécifique inatteignable. Sur l'objet attrapé, statusCode() a renvoyé 400 et retryable() a renvoyé false, ce qui est la réponse honnête pour un rejet de logique métier. Ajoute .returnValuesOnConditionCheckFailure("ALL_OLD") à la requête et e.item() revient rempli avec l'élément qui a bloqué l'écriture (cinq attributs dans l'exécution ci-dessus, Year en AttributeValue(N=1994)) : tu n'as donc pas besoin d'un getItem de suivi pour savoir qui a gagné.
Les nombres entrent sous forme de chaînes via .n(...). Le type N de DynamoDB est du texte décimal sur le réseau, et c'est ce qui empêche 1994 de devenir un double. .n(String.valueOf(year)) est l'idiome ; il n'y a pas de surcharge .n(int) à laquelle se raccrocher.
Le client est Closeable, et fait pour durer. Le try-with-resources ci-dessus convient à un programme one-shot et pas à un service : DynamoDbClient possède un pool de connexions HTTP et est thread-safe, donc construis-en un par application et laisse-le vivre. En construire un par requête est le bug de performance Java le plus courant sur cette API.
Tu préfères des beans aux maps d'AttributeValue ? Le DynamoDB Enhanced Client (software.amazon.awssdk.enhanced.dynamodb) mappe une classe annotée directement vers un élément, ce qui élimine complètement le piège du builder vide. Ça coûte un scan réflexif TableSchema.fromBean au démarrage, que StaticTableSchema évite si ça compte pour toi.
Le faire visuellement
Tous les échecs ci-dessus commencent par des valeurs typées construites à la main. Le convertisseur JSON DynamoDB gratuit prend du JSON ordinaire et renvoie la forme typée, pour que tu voies exactement à quoi l'élément doit ressembler sur le réseau avant d'écrire le moindre AttributeValue.builder().
Pour écrire et modifier des éléments dans tes propres tables — un formulaire par attribut, des sélecteurs de type, le résultat recopié en Java — télécharge DynoTable.
Exemples liés
- DynamoDB PutItem en Go — la même écriture conditionnelle avec l'AWS SDK for Go v2, où un pointeur nil échoue dans l'autre sens.
- DynamoDB UpdateItem en Java — modifier des attributs précis au lieu de remplacer l'élément.
- Les expressions de condition DynamoDB —
attribute_not_exists, verrouillage optimiste, et plus encore. - DynamoDB ConditionalCheckFailedException — ce que lève la condition « création seule » quand l'élément existe déjà.
- DynamoDB ValidationException — le fourre-tout pour un élément ou une expression malformés.
Références
- 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
Reproduit le 2026-07-28 avec l'AWS SDK for Java 2.49.4 sur OpenJDK 26.0.1, sur DynamoDB Local (amazon/dynamodb-local) sur le port 9000. Le texte de l'exception, le code de statut et le contenu de l'élément ci-dessus sont la sortie capturée, copiée telle quelle.