DynamoDB PutItem in Java (AWS SDK v2)
PutItem schreibt ein ganzes Item und ersetzt jedes vorhandene Item mit demselben Primary Key (Item-basierte Aktionen behandelt, worin sich das von UpdateItem unterscheidet). Im AWS SDK for Java 2.x geht jedes Attribut als typisierter AttributeValue in ein PutItemRequest — und der Builder lässt dich einen konstruieren, der unmöglich gültig sein 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.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());
}
}
}Erklärung
AttributeValue.builder().build() kompiliert. Es ist auch nicht versendbar. Der Builder hat kein Pflichtfeld, ein Attribut, bei dem du das .s(...) vergessen hast, ist also typkorrekt und scheitert erst beim Dienst:
DynamoDbException | ValidationException | Supplied AttributeValue is empty, must contain exactly one of the supported datatypesDas ist die Java-spezifische Form eines Fehlers, den andere SDKs unmöglich machen: Gos types.AttributeValueMember* sind eigene Typen, es gibt also nichts, was ungesetzt bleiben könnte. Die weitergehende Lösung steht unter "Supplied AttributeValue is empty".
Ein an .s(...) übergebenes Java-null wird kein DynamoDB-NULL. Das ist die Variante, die wirklich beißt, weil sie wie ein Wert aussieht:
AttributeValue.builder().s(customer.getNotes()).build() // getNotes() returned nullBei der Konstruktion wird keine NullPointerException geworfen. Der Builder merkt sich schlicht nichts, und die Anfrage scheitert mit derselben Supplied AttributeValue is empty-Meldung, die auf ein Attribut zeigt, das du nie verdächtigt hättest. Willst du ein echtes Null, ist das AttributeValue.builder().nul(true).build(); häufiger willst du den Eintrag ganz weglassen. Beachte, dass es im Go-SDK genau umgekehrt ist: Dort marshallt ein nil-Pointer zu NULL und legt stillschweigend ein Attribut an. Beides wurde am selben Tag gegen dieselbe Engine ausgeführt.
getMessage() ist nicht die Meldung des Dienstes. Das SDK hängt seinen eigenen Kontext an, der String lautet also:
The conditional request failed (Service: DynamoDb, Status Code: 400, Request ID: 77a08ef1-a3a9-4f97-b309-4cb0741edd1a) (SDK Attempt Count: 1)Matche auf e.awsErrorDetails().errorCode() und lies e.awsErrorDetails().errorMessage() für den nackten Text. Alles, was getMessage() mit einem Literal vergleicht, zerbricht an einem Retry, denn der ändert den Attempt Count.
Fange ConditionalCheckFailedException vor DynamoDbException und lies aus, was sie mitbringt. Sie erweitert DynamoDbException — die catch-Blöcke andersherum zu ordnen macht den spezifischen Handler unerreichbar. Auf dem gefangenen Objekt lieferte statusCode() den Wert 400 und retryable() den Wert false, was für eine Ablehnung durch die Geschäftslogik die ehrliche Antwort ist. Ergänze .returnValuesOnConditionCheckFailure("ALL_OLD") an der Anfrage, und e.item() kommt gefüllt mit dem Item zurück, das den Write blockiert hat (fünf Attribute im obigen Lauf, Year als AttributeValue(N=1994)) — du brauchst also kein nachgelagertes getItem, um herauszufinden, wer gewonnen hat.
Zahlen gehen über .n(...) als Strings hinein. DynamoDBs N-Typ ist auf der Leitung dezimaler Text, und genau das verhindert, dass aus 1994 ein Double wird. .n(String.valueOf(year)) ist das Idiom; eine .n(int)-Überladung, nach der man greifen könnte, gibt es nicht.
Der Client ist Closeable — und langlebig. Das try-with-resources oben ist für ein Einmalprogramm richtig und für einen Dienst falsch: DynamoDbClient besitzt einen HTTP-Connection-Pool und ist thread-safe, baue also einen pro Anwendung und lass ihn leben. Einen pro Anfrage zu bauen ist der häufigste Java-Performance-Bug auf dieser API.
Lieber Beans als AttributeValue-Maps? Der DynamoDB Enhanced Client (software.amazon.awssdk.enhanced.dynamodb) bildet eine annotierte Klasse direkt auf ein Item ab, was die Falle des leeren Builders vollständig beseitigt. Er kostet beim Start einen reflexiven TableSchema.fromBean-Scan, den StaticTableSchema vermeidet, falls dir das wichtig ist.
Mach es visuell
Jeder Fehler oben beginnt mit von Hand gebauten typisierten Werten. Der kostenlose DynamoDB-JSON-Konverter nimmt gewöhnliches JSON und gibt die typisierte Form zurück, sodass du genau siehst, wie das Item auf der Leitung aussehen sollte, bevor du einen einzigen AttributeValue.builder() schreibst.
Um Items gegen deine eigenen Tabellen zu schreiben und zu bearbeiten — ein Formular pro Attribut, Typ-Auswahl, das Ergebnis als Java zurückkopieren — lade DynoTable herunter.
Verwandte Beispiele
- DynamoDB PutItem in Go — derselbe bedingte Write mit AWS SDK for Go v2, wo ein nil-Pointer andersherum scheitert.
- DynamoDB UpdateItem in Java — einzelne Attribute ändern, statt das Item zu ersetzen.
- DynamoDB Condition Expressions —
attribute_not_exists, optimistisches Sperren und mehr. - DynamoDB ConditionalCheckFailedException — was die Nur-Anlegen-Bedingung auslöst, wenn das Item schon existiert.
- DynamoDB ValidationException — der Sammelfehler für ein fehlerhaftes Item oder eine fehlerhafte Expression.
Referenzen
- 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
Am 2026-07-28 mit AWS SDK for Java 2.49.4 auf OpenJDK 26.0.1 gegen DynamoDB Local (amazon/dynamodb-local) auf Port 9000 reproduziert. Der Exception-Text, der Statuscode und die Item-Inhalte oben sind aufgezeichnete Ausgabe, wortgetreu kopiert.