AWS SDK for Java v2 DynamoDB client library for interacting with Amazon DynamoDB NoSQL database service
ACID transaction support for complex multi-item operations with strong consistency guarantees. DynamoDB transactions allow you to perform multiple operations across multiple items and tables atomically, ensuring data consistency and integrity.
Performs a synchronous write operation that groups up to 100 action requests as a single atomic transaction.
/**
* Synchronous write operation that groups up to 100 action requests
* @param request - The request containing transaction items and options
* @return Response containing consumed capacity and item collection metrics
*/
TransactWriteItemsResponse transactWriteItems(TransactWriteItemsRequest request);
class TransactWriteItemsRequest {
static Builder builder();
/** An ordered array of up to 100 TransactWriteItem objects */
List<TransactWriteItem> transactItems();
Builder transactItems(Collection<TransactWriteItem> transactItems);
/** Determines the level of detail about consumed capacity returned */
ReturnConsumedCapacity returnConsumedCapacity();
Builder returnConsumedCapacity(ReturnConsumedCapacity returnConsumedCapacity);
/** Determines whether item collection metrics are returned */
ReturnItemCollectionMetrics returnItemCollectionMetrics();
Builder returnItemCollectionMetrics(ReturnItemCollectionMetrics returnItemCollectionMetrics);
/** Providing a ClientRequestToken makes the call idempotent */
String clientRequestToken();
Builder clientRequestToken(String clientRequestToken);
}
class TransactWriteItem {
static Builder builder();
/** A request to perform a check item operation */
ConditionCheck conditionCheck();
Builder conditionCheck(ConditionCheck conditionCheck);
/** A request to perform a PutItem operation */
Put put();
Builder put(Put put);
/** A request to perform a DeleteItem operation */
Delete delete();
Builder delete(Delete delete);
/** A request to perform an UpdateItem operation */
Update update();
Builder update(Update update);
}
class ConditionCheck {
static Builder builder();
/** Name of the table for the check item request */
String tableName();
Builder tableName(String tableName);
/** The primary key of the item to be checked */
Map<String, AttributeValue> key();
Builder key(Map<String, AttributeValue> key);
/** A condition that must be satisfied to include an item in the response */
String conditionExpression();
Builder conditionExpression(String conditionExpression);
/** One or more substitution tokens for attribute names in expressions */
Map<String, String> expressionAttributeNames();
Builder expressionAttributeNames(Map<String, String> expressionAttributeNames);
/** One or more values that can be substituted in expressions */
Map<String, AttributeValue> expressionAttributeValues();
Builder expressionAttributeValues(Map<String, AttributeValue> expressionAttributeValues);
/** Use ReturnValuesOnConditionCheckFailure to get the item attributes if the condition fails */
ReturnValuesOnConditionCheckFailure returnValuesOnConditionCheckFailure();
Builder returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure returnValuesOnConditionCheckFailure);
}
class Put {
static Builder builder();
/** A map of attribute name/value pairs, one for each attribute */
Map<String, AttributeValue> item();
Builder item(Map<String, AttributeValue> item);
/** Name of the table for the put item request */
String tableName();
Builder tableName(String tableName);
/** A condition that must be satisfied to include an item in the response */
String conditionExpression();
Builder conditionExpression(String conditionExpression);
/** One or more substitution tokens for attribute names in expressions */
Map<String, String> expressionAttributeNames();
Builder expressionAttributeNames(Map<String, String> expressionAttributeNames);
/** One or more values that can be substituted in expressions */
Map<String, AttributeValue> expressionAttributeValues();
Builder expressionAttributeValues(Map<String, AttributeValue> expressionAttributeValues);
/** Use ReturnValuesOnConditionCheckFailure to get the item attributes if the condition fails */
ReturnValuesOnConditionCheckFailure returnValuesOnConditionCheckFailure();
Builder returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure returnValuesOnConditionCheckFailure);
}
class Delete {
static Builder builder();
/** The primary key of the item to be deleted */
Map<String, AttributeValue> key();
Builder key(Map<String, AttributeValue> key);
/** Name of the table for the delete item request */
String tableName();
Builder tableName(String tableName);
/** A condition that must be satisfied to include an item in the response */
String conditionExpression();
Builder conditionExpression(String conditionExpression);
/** One or more substitution tokens for attribute names in expressions */
Map<String, String> expressionAttributeNames();
Builder expressionAttributeNames(Map<String, String> expressionAttributeNames);
/** One or more values that can be substituted in expressions */
Map<String, AttributeValue> expressionAttributeValues();
Builder expressionAttributeValues(Map<String, AttributeValue> expressionAttributeValues);
/** Use ReturnValuesOnConditionCheckFailure to get the item attributes if the condition fails */
ReturnValuesOnConditionCheckFailure returnValuesOnConditionCheckFailure();
Builder returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure returnValuesOnConditionCheckFailure);
}
class Update {
static Builder builder();
/** The primary key of the item to be updated */
Map<String, AttributeValue> key();
Builder key(Map<String, AttributeValue> key);
/** Name of the table for the update item request */
String tableName();
Builder tableName(String tableName);
/** An expression that defines one or more attributes to be updated */
String updateExpression();
Builder updateExpression(String updateExpression);
/** A condition that must be satisfied to include an item in the response */
String conditionExpression();
Builder conditionExpression(String conditionExpression);
/** One or more substitution tokens for attribute names in expressions */
Map<String, String> expressionAttributeNames();
Builder expressionAttributeNames(Map<String, String> expressionAttributeNames);
/** One or more values that can be substituted in expressions */
Map<String, AttributeValue> expressionAttributeValues();
Builder expressionAttributeValues(Map<String, AttributeValue> expressionAttributeValues);
/** Use ReturnValuesOnConditionCheckFailure to get the item attributes if the condition fails */
ReturnValuesOnConditionCheckFailure returnValuesOnConditionCheckFailure();
Builder returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure returnValuesOnConditionCheckFailure);
}
class TransactWriteItemsResponse {
/** The capacity units consumed by the entire transaction */
List<ConsumedCapacity> consumedCapacity();
/** A list of tables that were processed by TransactWriteItems and their item collection metrics */
Map<String, List<ItemCollectionMetrics>> itemCollectionMetrics();
}Usage Example:
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.*;
import java.util.List;
import java.util.Map;
DynamoDbClient client = DynamoDbClient.builder().build();
// Transfer money between accounts atomically
List<TransactWriteItem> transactItems = List.of(
// Debit from source account
TransactWriteItem.builder()
.update(Update.builder()
.tableName("Accounts")
.key(Map.of("accountId", AttributeValue.builder().s("account1").build()))
.updateExpression("SET balance = balance - :amount")
.conditionExpression("balance >= :amount") // Ensure sufficient funds
.expressionAttributeValues(Map.of(
":amount", AttributeValue.builder().n("100.00").build()
))
.build())
.build(),
// Credit to destination account
TransactWriteItem.builder()
.update(Update.builder()
.tableName("Accounts")
.key(Map.of("accountId", AttributeValue.builder().s("account2").build()))
.updateExpression("SET balance = balance + :amount")
.conditionExpression("attribute_exists(accountId)") // Ensure account exists
.expressionAttributeValues(Map.of(
":amount", AttributeValue.builder().n("100.00").build()
))
.build())
.build(),
// Log the transaction
TransactWriteItem.builder()
.put(Put.builder()
.tableName("TransactionLogs")
.item(Map.of(
"transactionId", AttributeValue.builder().s("txn-123").build(),
"fromAccount", AttributeValue.builder().s("account1").build(),
"toAccount", AttributeValue.builder().s("account2").build(),
"amount", AttributeValue.builder().n("100.00").build(),
"timestamp", AttributeValue.builder().s("2025-09-07T17:30:00Z").build()
))
.conditionExpression("attribute_not_exists(transactionId)") // Prevent duplicates
.build())
.build()
);
try {
TransactWriteItemsResponse response = client.transactWriteItems(
TransactWriteItemsRequest.builder()
.transactItems(transactItems)
.returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)
.clientRequestToken("transfer-123") // For idempotency
.build()
);
System.out.println("Transaction completed successfully");
System.out.println("Total consumed capacity: " +
response.consumedCapacity().stream()
.mapToDouble(cc -> cc.capacityUnits())
.sum());
} catch (TransactionCanceledException e) {
System.err.println("Transaction failed: " + e.getMessage());
// Handle transaction failure (e.g., insufficient funds, account doesn't exist)
}Retrieves multiple items from one or more tables in a single atomic operation.
/**
* Synchronous read operation that groups up to 100 Get actions together
* @param request - The request containing transaction items and options
* @return Response containing retrieved items and consumed capacity
*/
TransactGetItemsResponse transactGetItems(TransactGetItemsRequest request);
class TransactGetItemsRequest {
static Builder builder();
/** An ordered array of up to 100 TransactGetItem objects */
List<TransactGetItem> transactItems();
Builder transactItems(Collection<TransactGetItem> transactItems);
/** Determines the level of detail about consumed capacity returned */
ReturnConsumedCapacity returnConsumedCapacity();
Builder returnConsumedCapacity(ReturnConsumedCapacity returnConsumedCapacity);
}
class TransactGetItem {
static Builder builder();
/** Contains the primary key that identifies the item to get */
Get get();
Builder get(Get get);
}
class Get {
static Builder builder();
/** A map of attribute names to AttributeValue objects that specifies the primary key */
Map<String, AttributeValue> key();
Builder key(Map<String, AttributeValue> key);
/** The name of the table from which to retrieve the specified item */
String tableName();
Builder tableName(String tableName);
/** A string that identifies one or more attributes of the specified item to retrieve */
String projectionExpression();
Builder projectionExpression(String projectionExpression);
/** One or more substitution tokens for attribute names in the ProjectionExpression */
Map<String, String> expressionAttributeNames();
Builder expressionAttributeNames(Map<String, String> expressionAttributeNames);
}
class TransactGetItemsResponse {
/** An ordered array of up to 100 ItemResponse objects */
List<ItemResponse> responses();
/** The capacity units consumed by the entire transaction */
List<ConsumedCapacity> consumedCapacity();
}
class ItemResponse {
/** Map of attribute data consisting of the data type and attribute value */
Map<String, AttributeValue> item();
}Usage Example:
// Get multiple related items atomically
List<TransactGetItem> transactItems = List.of(
TransactGetItem.builder()
.get(Get.builder()
.tableName("Users")
.key(Map.of("userId", AttributeValue.builder().s("user123").build()))
.projectionExpression("userId, name, email, accountId")
.build())
.build(),
TransactGetItem.builder()
.get(Get.builder()
.tableName("Accounts")
.key(Map.of("accountId", AttributeValue.builder().s("acc456").build()))
.projectionExpression("accountId, balance, #status")
.expressionAttributeNames(Map.of("#status", "status"))
.build())
.build()
);
TransactGetItemsResponse response = client.transactGetItems(
TransactGetItemsRequest.builder()
.transactItems(transactItems)
.returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)
.build()
);
// Process results in order
List<ItemResponse> responses = response.responses();
Map<String, AttributeValue> user = responses.get(0).item();
Map<String, AttributeValue> account = responses.get(1).item();
if (user != null && account != null) {
String userName = user.get("name").s();
String balance = account.get("balance").n();
System.out.println("User " + userName + " has balance: $" + balance);
}Execute multiple PartiQL statements as a single atomic transaction.
/**
* Executes multiple PartiQL statements within a transaction block
* @param request - The request containing PartiQL statements and options
* @return Response containing statement results and consumed capacity
*/
ExecuteTransactionResponse executeTransaction(ExecuteTransactionRequest request);
class ExecuteTransactionRequest {
static Builder builder();
/** The list of PartiQL statements representing the transaction to run */
List<ParameterizedStatement> transactStatements();
Builder transactStatements(Collection<ParameterizedStatement> transactStatements);
/** Set this value to get remaining results, if NextToken was returned in the previous response */
String nextToken();
Builder nextToken(String nextToken);
/** Providing a ClientRequestToken makes the call idempotent */
String clientRequestToken();
Builder clientRequestToken(String clientRequestToken);
/** Determines the level of detail about consumed capacity returned */
ReturnConsumedCapacity returnConsumedCapacity();
Builder returnConsumedCapacity(ReturnConsumedCapacity returnConsumedCapacity);
}
class ParameterizedStatement {
static Builder builder();
/** A PartiQL statement that can be run against a DynamoDB table */
String statement();
Builder statement(String statement);
/** The parameter values for the PartiQL statement, if any */
List<AttributeValue> parameters();
Builder parameters(Collection<AttributeValue> parameters);
/** An optional parameter for the ReturnValues on this PartiQL statement */
ReturnValuesOnConditionCheckFailure returnValuesOnConditionCheckFailure();
Builder returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure returnValuesOnConditionCheckFailure);
}
class ExecuteTransactionResponse {
/** The response to a PartiQL transaction */
List<ItemResponse> responses();
/** The capacity units consumed by the entire transaction */
List<ConsumedCapacity> consumedCapacity();
}Usage Example:
// Execute multiple PartiQL statements in a transaction
List<ParameterizedStatement> statements = List.of(
ParameterizedStatement.builder()
.statement("UPDATE \"Accounts\" SET balance = balance - ? WHERE accountId = ? AND balance >= ?")
.parameters(List.of(
AttributeValue.builder().n("100.00").build(), // amount to debit
AttributeValue.builder().s("account1").build(), // account ID
AttributeValue.builder().n("100.00").build() // minimum balance check
))
.build(),
ParameterizedStatement.builder()
.statement("UPDATE \"Accounts\" SET balance = balance + ? WHERE accountId = ?")
.parameters(List.of(
AttributeValue.builder().n("100.00").build(), // amount to credit
AttributeValue.builder().s("account2").build() // account ID
))
.build(),
ParameterizedStatement.builder()
.statement("INSERT INTO \"TransactionLogs\" VALUE {'transactionId': ?, 'amount': ?, 'status': ?}")
.parameters(List.of(
AttributeValue.builder().s("txn-456").build(),
AttributeValue.builder().n("100.00").build(),
AttributeValue.builder().s("completed").build()
))
.build()
);
try {
ExecuteTransactionResponse response = client.executeTransaction(
ExecuteTransactionRequest.builder()
.transactStatements(statements)
.clientRequestToken("partiql-txn-456")
.returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)
.build()
);
System.out.println("PartiQL transaction completed successfully");
} catch (TransactionCanceledException e) {
System.err.println("PartiQL transaction failed: " + e.getMessage());
}class TransactionCanceledException extends DynamoDbException {
/** List of cancellation reasons for each item */
List<CancellationReason> cancellationReasons();
}
class TransactionConflictException extends DynamoDbException;
class TransactionInProgressException extends DynamoDbException;
class CancellationReason {
/** Status code for the result of the cancellation */
String code();
/** Descriptive message for the cancellation */
String message();
/** Item in the request which caused the transaction to get cancelled */
Map<String, AttributeValue> item();
}Error Handling Example:
try {
client.transactWriteItems(request);
} catch (TransactionCanceledException e) {
List<CancellationReason> reasons = e.cancellationReasons();
for (int i = 0; i < reasons.size(); i++) {
CancellationReason reason = reasons.get(i);
if (reason.code() != null) {
System.err.println("Item " + i + " failed: " + reason.code() + " - " + reason.message());
}
}
} catch (ValidationException e) {
System.err.println("Invalid transaction request: " + e.getMessage());
} catch (ProvisionedThroughputExceededException e) {
System.err.println("Throughput exceeded, retry with backoff");
}Always use clientRequestToken for idempotent transactions:
String requestToken = UUID.randomUUID().toString();
TransactWriteItemsRequest request = TransactWriteItemsRequest.builder()
.transactItems(items)
.clientRequestToken(requestToken) // Ensures idempotency
.build();Use condition expressions to ensure data integrity:
// Only update if current value meets criteria
Update.builder()
.updateExpression("SET balance = balance - :amount")
.conditionExpression("balance >= :amount AND #status = :active")
.expressionAttributeNames(Map.of("#status", "status"))
.expressionAttributeValues(Map.of(
":amount", AttributeValue.builder().n("100").build(),
":active", AttributeValue.builder().s("ACTIVE").build()
))
.build()Handle partial failures and implement retry logic:
try {
client.transactWriteItems(request);
} catch (TransactionCanceledException e) {
// Analyze cancellation reasons
// Implement appropriate retry or compensation logic
handleTransactionFailure(e.cancellationReasons());
}Install with Tessl CLI
npx tessl i tessl/maven-software-amazon-awssdk--dynamodb