Como exemplo de uma situação em que as Amazon DynamoDB Transactions podem ser úteis, considere este exemplo de aplicação Java para um marketplace online.
A aplicação tem três tabelas do DynamoDB no backend:
Customers
: esta tabela armazena detalhes sobre os clientes do marketplace. Sua chave primária é um identificador exclusivoCustomerId
.ProductCatalog
: esta tabela armazena detalhes como preço e disponibilidade sobre os produtos para venda no mercado. Sua chave primária é um identificador exclusivoProductId
.Orders
: esta tabela armazena detalhes sobre os pedidos do marketplace. Sua chave primária é um identificador exclusivoOrderId
.
Fazer um pedido
Os trechos de código a seguir ilustram como usar transações do DynamoDB para coordenar as várias etapas necessárias para criar e processar um pedido. O uso de uma única operação de tudo ou nada garante que, se qualquer parte da transação falhar, nenhuma ação na transação será executada e nenhuma alteração será feita.
Neste exemplo, você configura um pedido de um cliente cujo customerId
é 09e8e9c8-ec48
. Em seguida, execute-o como uma única transação usando o seguinte fluxo de trabalho simples de processamento de pedidos:
Determine se o ID do cliente é válido.
Verifique se o produto é
IN_STOCK
e atualize o status do produto paraSOLD
.Certifique-se de que o pedido ainda não exista e crie-o.
Validar o cliente
Primeiro, defina uma ação para verificar se um cliente com customerId
igual a 09e8e9c8-ec48
existe na tabela de clientes.
final String CUSTOMER_TABLE_NAME = "Customers";
final String CUSTOMER_PARTITION_KEY = "CustomerId";
final String customerId = "09e8e9c8-ec48";
final HashMap<String, AttributeValue> customerItemKey = new HashMap<>();
customerItemKey.put(CUSTOMER_PARTITION_KEY, new AttributeValue(customerId));
ConditionCheck checkCustomerValid = new ConditionCheck()
.withTableName(CUSTOMER_TABLE_NAME)
.withKey(customerItemKey)
.withConditionExpression("attribute_exists(" + CUSTOMER_PARTITION_KEY + ")");
Atualizar o status do produto
Em seguida, defina uma ação para atualizar o status do produto para SOLD
se a condição do status atual do produto IN_STOCK
for true
. Configurar a opção ReturnValuesOnConditionCheckFailure
retornará o item se o atributo de status do produto do item não for igual a IN_STOCK
.
final String PRODUCT_TABLE_NAME = "ProductCatalog";
final String PRODUCT_PARTITION_KEY = "ProductId";
HashMap<String, AttributeValue> productItemKey = new HashMap<>();
productItemKey.put(PRODUCT_PARTITION_KEY, new AttributeValue(productKey));
Map<String, AttributeValue> expressionAttributeValues = new HashMap<>();
expressionAttributeValues.put(":new_status", new AttributeValue("SOLD"));
expressionAttributeValues.put(":expected_status", new AttributeValue("IN_STOCK"));
Update markItemSold = new Update()
.withTableName(PRODUCT_TABLE_NAME)
.withKey(productItemKey)
.withUpdateExpression("SET ProductStatus = :new_status")
.withExpressionAttributeValues(expressionAttributeValues)
.withConditionExpression("ProductStatus = :expected_status")
.withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD);
Criar o pedido
Por último, crie o pedido, desde que um pedido com esse OrderId
ainda não exista.
final String ORDER_PARTITION_KEY = "OrderId";
final String ORDER_TABLE_NAME = "Orders";
HashMap<String, AttributeValue> orderItem = new HashMap<>();
orderItem.put(ORDER_PARTITION_KEY, new AttributeValue(orderId));
orderItem.put(PRODUCT_PARTITION_KEY, new AttributeValue(productKey));
orderItem.put(CUSTOMER_PARTITION_KEY, new AttributeValue(customerId));
orderItem.put("OrderStatus", new AttributeValue("CONFIRMED"));
orderItem.put("OrderTotal", new AttributeValue("100"));
Put createOrder = new Put()
.withTableName(ORDER_TABLE_NAME)
.withItem(orderItem)
.withReturnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD)
.withConditionExpression("attribute_not_exists(" + ORDER_PARTITION_KEY + ")");
Executar a transação
O exemplo a seguir ilustra como executar as ações definidas anteriormente como uma única operação tudo ou nada.
Collection<TransactWriteItem> actions = Arrays.asList(
new TransactWriteItem().withConditionCheck(checkCustomerValid),
new TransactWriteItem().withUpdate(markItemSold),
new TransactWriteItem().withPut(createOrder));
TransactWriteItemsRequest placeOrderTransaction = new TransactWriteItemsRequest()
.withTransactItems(actions)
.withReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL);
// Run the transaction and process the result.
try {
client.transactWriteItems(placeOrderTransaction);
System.out.println("Transaction Successful");
} catch (ResourceNotFoundException rnf) {
System.err.println("One of the table involved in the transaction is not found" + rnf.getMessage());
} catch (InternalServerErrorException ise) {
System.err.println("Internal Server Error" + ise.getMessage());
} catch (TransactionCanceledException tce) {
System.out.println("Transaction Canceled " + tce.getMessage());
}
Ler os detalhes do pedido
O exemplo a seguir mostra como ler a ordem concluída transacionalmente entre as tabelas Orders
e ProductCatalog
.
HashMap<String, AttributeValue> productItemKey = new HashMap<>();
productItemKey.put(PRODUCT_PARTITION_KEY, new AttributeValue(productKey));
HashMap<String, AttributeValue> orderKey = new HashMap<>();
orderKey.put(ORDER_PARTITION_KEY, new AttributeValue(orderId));
Get readProductSold = new Get()
.withTableName(PRODUCT_TABLE_NAME)
.withKey(productItemKey);
Get readCreatedOrder = new Get()
.withTableName(ORDER_TABLE_NAME)
.withKey(orderKey);
Collection<TransactGetItem> getActions = Arrays.asList(
new TransactGetItem().withGet(readProductSold),
new TransactGetItem().withGet(readCreatedOrder));
TransactGetItemsRequest readCompletedOrder = new TransactGetItemsRequest()
.withTransactItems(getActions)
.withReturnConsumedCapacity(ReturnConsumedCapacity.TOTAL);
// Run the transaction and process the result.
try {
TransactGetItemsResult result = client.transactGetItems(readCompletedOrder);
System.out.println(result.getResponses());
} catch (ResourceNotFoundException rnf) {
System.err.println("One of the table involved in the transaction is not found" + rnf.getMessage());
} catch (InternalServerErrorException ise) {
System.err.println("Internal Server Error" + ise.getMessage());
} catch (TransactionCanceledException tce) {
System.err.println("Transaction Canceled" + tce.getMessage());
}