DynamoDB UpdateItem in Java (AWS SDK v2)
The update itself is one builder call. What costs Java developers time is everything around it: a response object that never returns null, an exception hierarchy where the interesting failure is a subclass of the one you probably caught, and a high-level client that cannot express this operation at all.
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());
}
}
}Explanation
AttributeValue.builder().n("1994")takes aString, and so does the shorterAttributeValue.fromN("1994"). There is non(int)overload, because DynamoDB numbers hold 38 significant digits and no Java primitive does. Reading back,attributes().get("Awards").n()is aStringtoo; the accessor for the wrong type returns null rather than throwing, so.s()on a number is a silent null, and.type()tells you which one is set.response.attributes()— never null. WithReturnValue.NONEit returns aDefaultSdkAutoConstructMapthat is empty but non-null, so a null check never fires and anisEmpty()check cannot distinguish "the service sent nothing" from "the item has no attributes". The generatedhasAttributes()is the accessor that knows the difference. Every collection member in this SDK has one.The builder type-checks everything except the part that matters.
updateExpression(String)accepts any string; the compiler cannot tellSETfrom a typo, so expression mistakes are runtime 400s.ADD #upd2 :updValue2is the atomic increment, aconditionExpressionofattribute_exists(Artist)makes the call update-only, and the grammar is in update expressions.Legacy
attributeUpdatesmap — prefer the expression over it. Older examples still show it; it cannot express multiple clause types, aliases, or a condition in one request.getMessage()is not the service's message. The SDK appends its own transport detail:The conditional request failed (Service: DynamoDb, Status Code: 400, Request ID: d99b117c-edd6-4dc9-8d3a-a5fa4fe9666c) (SDK Attempt Count: 1)Log that if you want the request ID for a support case. Compare on
awsErrorDetails().errorCode()instead, and useawsErrorDetails().errorMessage()when you want the bare string.
Catch order matters more than usual here
ConditionalCheckFailedException extends DynamoDbException, so a catch (DynamoDbException e) placed first swallows the one failure you almost certainly wanted to branch on. Catch the specific type first, and take the item while you are there:
} 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() is false on this one, which is correct: retrying a failed condition just fails again.
The asymmetry to remember is that ValidationException has no class in this SDK. Search dynamodb-2.35.9.jar and there is nothing to catch. A reserved word, a malformed expression, a partial key: all of them arrive as a plain DynamoDbException whose awsErrorDetails().errorCode() happens to read ValidationException. In a statically typed language that is a jarring gap, and it means expression mistakes are runtime string comparisons.
Which is why DynamoDB's reserved words deserve a pass before you ship rather than after: the list runs to 573 entries and includes Year, Name and Status, none of which look dangerous in a Java bean. To browse the raw table rather than the bean mapping over it, download DynoTable.
The enhanced client cannot express this
If the rest of your data access goes through DynamoDbEnhancedClient and annotated beans, this operation is the one that drops you back to DynamoDbClient. Reflecting over UpdateItemEnhancedRequest.Builder turns up item, conditionExpression, ignoreNulls, ignoreNullsMode, returnValues, returnValuesOnConditionCheckFailure, returnConsumedCapacity and returnItemCollectionMetrics. There is no method that accepts an update expression.
The practical consequence is the atomic counter. ADD #upd2 :updValue2 increments Awards server-side with no read first; the enhanced client gives you a mapped bean and ignoreNulls to decide whether absent fields are removed, and nothing that compiles down to ADD. Read-modify-write through a bean is a lost-update race under concurrency, which is precisely what this page's snippet avoids.
Related examples
- DynamoDB UpdateItem in Go — the same update with AWS SDK for Go v2.
- DynamoDB PutItem in Java — replace the whole item instead.
- DynamoDB update expressions —
SET,ADD,REMOVE,DELETE, and idioms. - Understanding ReturnValues — what each
ReturnValuesoption gives you. - "Attribute name is a reserved keyword" — why the alias map here isn't optional.
- "Invalid UpdateExpression" syntax errors — the common SET/ADD syntax mistakes, decoded.
References
- 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
Last verified 2026-07-28 against the official AWS documentation linked above.