UpdateItem DynamoDB en Java (AWS SDK v2)
La mise à jour elle-même tient en un appel de builder. Ce qui coûte du temps aux développeurs Java, c'est tout ce qui l'entoure : un objet de réponse qui ne renvoie jamais null, une hiérarchie d'exceptions où l'échec intéressant est une sous-classe de celle que tu as probablement attrapée, et un client haut niveau qui ne sait pas exprimer cette opération du tout.
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.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());
}
}
}Explication
AttributeValue.builder().n("1994")prend unString, tout comme la forme plus courteAttributeValue.fromN("1994"). Il n'y a pas de surchargen(int), parce que les nombres DynamoDB tiennent 38 chiffres significatifs et qu'aucun primitif Java n'en fait autant. À la relecture,attributes().get("Awards").n()est unStringlui aussi ; l'accesseur du mauvais type renvoie null au lieu de lever, donc.s()sur un nombre est un null silencieux, et.type()te dit lequel est renseigné.response.attributes()ne renvoie jamais null. AvecReturnValue.NONE, il renvoie uneDefaultSdkAutoConstructMapvide mais non nulle : un test de nullité ne se déclenche donc jamais, et un testisEmpty()ne peut pas distinguer « le service n'a rien envoyé » de « l'élément n'a aucun attribut ». LehasAttributes()généré est l'accesseur qui connaît la différence. Chaque membre de type collection de ce SDK en possède un.Le builder type-vérifie tout sauf ce qui compte.
updateExpression(String)accepte n'importe quelle chaîne ; le compilateur ne distingue pasSETd'une faute de frappe, donc les erreurs d'expression sont des 400 à l'exécution.ADD #upd2 :updValue2est l'incrément atomique, uneconditionExpressionenattribute_exists(Artist)rend l'appel exclusivement modificateur, et la grammaire est dans les expressions de mise à jour.Préfère l'expression à la map
attributeUpdateshéritée. De vieux exemples la montrent encore ; elle ne sait exprimer ni plusieurs types de clauses, ni des alias, ni une condition dans une même requête.getMessage()n'est pas le message du service. Le SDK y accole son propre détail de transport :The conditional request failed (Service: DynamoDb, Status Code: 400, Request ID: d99b117c-edd6-4dc9-8d3a-a5fa4fe9666c) (SDK Attempt Count: 1)Journalise ça si tu veux l'identifiant de requête pour un ticket de support. Compare plutôt sur
awsErrorDetails().errorCode(), et utiliseawsErrorDetails().errorMessage()quand tu veux la chaîne nue.
L'ordre des catch compte plus que d'habitude ici
ConditionalCheckFailedException extends DynamoDbException : un catch (DynamoDbException e) placé en premier avale donc le seul échec sur lequel tu voulais presque certainement brancher. Attrape d'abord le type précis, et récupère l'élément au passage :
} 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() vaut false sur celle-ci, et c'est correct : réessayer une condition en échec échoue simplement à nouveau.
L'asymétrie à retenir, c'est que ValidationException n'a aucune classe dans ce SDK. Fouille dynamodb-2.35.9.jar et il n'y a rien à attraper. Un mot réservé, une expression malformée, une clé partielle : tout arrive sous forme de DynamoDbException ordinaire dont le awsErrorDetails().errorCode() vaut justement ValidationException. Dans un langage à typage statique, c'est un trou déroutant, et ça veut dire que les erreurs d'expression sont des comparaisons de chaînes à l'exécution.
C'est pour ça que les mots réservés de DynamoDB méritent un passage avant la livraison plutôt qu'après : la liste compte 573 entrées et inclut Year, Name et Status, dont aucun n'a l'air dangereux dans un bean Java. Pour parcourir la table brute plutôt que le bean posé dessus, télécharge DynoTable.
Le client enhanced ne sait pas exprimer ceci
Si le reste de ton accès aux données passe par DynamoDbEnhancedClient et des beans annotés, c'est l'opération qui te ramène à DynamoDbClient. Une réflexion sur UpdateItemEnhancedRequest.Builder fait apparaître item, conditionExpression, ignoreNulls, ignoreNullsMode, returnValues, returnValuesOnConditionCheckFailure, returnConsumedCapacity et returnItemCollectionMetrics. Aucune méthode n'accepte d'expression de mise à jour.
La conséquence pratique, c'est le compteur atomique. ADD #upd2 :updValue2 incrémente Awards côté serveur sans lecture préalable ; le client enhanced te donne un bean mappé et ignoreNulls pour décider si les champs absents sont supprimés, et rien qui se compile en ADD. Un cycle lecture-modification-écriture à travers un bean est une course à la mise à jour perdue en cas de concurrence, ce que l'extrait de cette page évite précisément.
Exemples liés
- UpdateItem DynamoDB en Go — la même mise à jour avec l'AWS SDK for Go v2.
- DynamoDB PutItem en Java — remplacer l'élément entier à la place.
- Les expressions de mise à jour DynamoDB —
SET,ADD,REMOVE,DELETE, et les idiomes. - Comprendre ReturnValues — ce que chaque option
ReturnValueste donne. - "Attribute name is a reserved keyword" — pourquoi la map d'alias n'est pas optionnelle ici.
- Les erreurs de syntaxe "Invalid UpdateExpression" — les fautes de syntaxe SET/ADD courantes, décodées.
Références
- 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
Dernière vérification le 2026-07-28 par rapport à la documentation officielle AWS liée ci-dessus.