DynamoDB UpdateItem in Java (AWS SDK v2)
Das Update selbst ist ein Builder-Aufruf. Was Java-Entwickler Zeit kostet, ist alles drumherum: ein Antwortobjekt, das nie null zurückgibt, eine Exception-Hierarchie, in der der interessante Fehlschlag eine Unterklasse dessen ist, was du vermutlich abgefangen hast, und ein High-Level-Client, der diese Operation überhaupt nicht ausdrücken kann.
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());
}
}
}Erklärung
AttributeValue.builder().n("1994")nimmt einenString, und das kürzereAttributeValue.fromN("1994")ebenso. Es gibt keinen(int)-Überladung, denn DynamoDB-Zahlen halten 38 signifikante Stellen und kein Java-Primitive tut das. Beim Zurücklesen istattributes().get("Awards").n()ebenfalls einString; der Accessor für den falschen Typ gibt null zurück, statt zu werfen —.s()auf einer Zahl ist also ein stilles null, und.type()sagt dir, welcher gesetzt ist.response.attributes()gibt nie null zurück. MitReturnValue.NONEgibt es eineDefaultSdkAutoConstructMapzurück, die leer, aber nicht null ist — eine Null-Prüfung greift also nie, und eineisEmpty()-Prüfung kann „der Dienst hat nichts geschickt" nicht von „das Item hat keine Attribute" unterscheiden. Das generiertehasAttributes()ist der Accessor, der den Unterschied kennt. Jedes Collection-Member in diesem SDK hat eins.Der Builder typprüft alles außer dem, worauf es ankommt.
updateExpression(String)akzeptiert jeden String; der Compiler kannSETnicht von einem Tippfehler unterscheiden, Expression-Fehler sind also 400er zur Laufzeit.ADD #upd2 :updValue2ist das atomare Inkrement, eineconditionExpressionvonattribute_exists(Artist)macht den Aufruf zu reinem Aktualisieren, und die Grammatik steht in Update Expressions.Bevorzuge die Expression gegenüber der alten
attributeUpdates-Map. Ältere Beispiele zeigen sie noch; sie kann weder mehrere Klauseltypen noch Aliase noch eine Bedingung in einem Request ausdrücken.getMessage()ist nicht die Nachricht des Dienstes. Das SDK hängt sein eigenes Transportdetail an:The conditional request failed (Service: DynamoDb, Status Code: 400, Request ID: d99b117c-edd6-4dc9-8d3a-a5fa4fe9666c) (SDK Attempt Count: 1)Logge das, wenn du die Request-ID für einen Supportfall willst. Vergleiche stattdessen über
awsErrorDetails().errorCode()und nutzeawsErrorDetails().errorMessage(), wenn du den blanken String willst.
Die Catch-Reihenfolge zählt hier mehr als sonst
ConditionalCheckFailedException extends DynamoDbException — ein zuerst platziertes catch (DynamoDbException e) verschluckt also genau den einen Fehlschlag, auf den du fast sicher verzweigen wolltest. Fang den spezifischen Typ zuerst ab und nimm gleich das Item mit:
} 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() ist hier false, und das ist richtig: Eine fehlgeschlagene Bedingung zu wiederholen scheitert einfach erneut.
Die Asymmetrie, die du dir merken solltest: ValidationException hat in diesem SDK keine Klasse. Durchsuch dynamodb-2.35.9.jar und es gibt nichts abzufangen. Ein reserviertes Wort, eine fehlerhafte Expression, ein Teilschlüssel: Sie alle kommen als schlichte DynamoDbException an, deren awsErrorDetails().errorCode() zufällig ValidationException lautet. In einer statisch typisierten Sprache ist das eine unangenehme Lücke, und sie bedeutet, dass Expression-Fehler zu String-Vergleichen zur Laufzeit werden.
Deshalb verdienen DynamoDBs reservierte Wörter einen Durchgang vor dem Ausliefern statt danach: Die Liste umfasst 573 Einträge und enthält Year, Name und Status — von denen keines in einer Java-Bean gefährlich aussieht. Um die rohe Tabelle statt der Bean-Abbildung darüber zu durchsuchen, lade DynoTable herunter.
Der Enhanced Client kann das nicht ausdrücken
Wenn dein übriger Datenzugriff über DynamoDbEnhancedClient und annotierte Beans läuft, ist diese Operation die eine, die dich auf DynamoDbClient zurückwirft. Reflection über UpdateItemEnhancedRequest.Builder fördert item, conditionExpression, ignoreNulls, ignoreNullsMode, returnValues, returnValuesOnConditionCheckFailure, returnConsumedCapacity und returnItemCollectionMetrics zutage. Es gibt keine Methode, die eine Update-Expression annimmt.
Die praktische Folge ist der atomare Zähler. ADD #upd2 :updValue2 inkrementiert Awards serverseitig ohne vorheriges Lesen; der Enhanced Client gibt dir eine gemappte Bean und ignoreNulls, um zu entscheiden, ob fehlende Felder entfernt werden — und nichts, was zu ADD kompiliert. Read-Modify-Write über eine Bean ist unter Nebenläufigkeit ein Lost-Update-Race, und genau das vermeidet das Snippet dieser Seite.
Verwandte Beispiele
- DynamoDB UpdateItem in Go — dasselbe Update mit AWS SDK for Go v2.
- DynamoDB PutItem in Java — stattdessen das ganze Item ersetzen.
- DynamoDB Update Expressions —
SET,ADD,REMOVE,DELETEund Idiome. - ReturnValues verstehen — was dir jede
ReturnValues-Option liefert. - „Attribute name is a reserved keyword" — warum die Alias-Map hier nicht optional ist.
- „Invalid UpdateExpression"-Syntaxfehler — die häufigen SET/ADD-Syntaxfehler, entschlüsselt.
Referenzen
- 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
Zuletzt verifiziert am 2026-07-28 gegen die oben verlinkte offizielle AWS-Dokumentation.