Ejemplo de transacciones en DynamoDB
Como ejemplo de una situación en la que Amazon DynamoDB Transactions pueden ser útiles, considere esta aplicación Java de ejemplo para un mercado en línea.
La aplicación tiene tres tablas de DynamoDB en el backend:
Customers
: en esta tabla se almacenan detalles sobre los clientes del mercado. Su clave principal es un identificador únicoCustomerId
.ProductCatalog
: en esta tabla se almacenan detalles tales como el precio y la disponibilidad de los productos que se venden en el mercado. Su clave principal es un identificador únicoProductId
.Orders
: en esta tabla se almacenan detalles sobre los pedidos del mercado. Su clave principal es un identificador únicoOrderId
.
Hacer un pedido
Los siguientes fragmentos de código ilustran cómo utilizar las transacciones de DynamoDB para coordinar los múltiples pasos necesarios para crear y procesar un pedido. El uso de una única operación todo o nada garantiza que si falla alguna parte de la transacción, no se ejecuten acciones en la transacción y no se realicen cambios.
En este ejemplo, configura un pedido de un cliente cuyo customerId
es 09e8e9c8-ec48
. A continuación, se ejecuta como una única transacción mediante el siguiente flujo de trabajo de procesamiento de pedidos:
Determine que el ID del cliente sea válido.
Asegúrese de que el producto sea
IN_STOCK
y actualice el estado del producto aSOLD
.Asegúrese de que el pedido aún no existe y cree el pedido.
Validar al cliente
Primero, defina una acción para verificar que un cliente con customerId
igual a 09e8e9c8-ec48
existe en la tabla 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 + ")");
Actualizar el estado del producto
A continuación, defina una acción para actualizar el estado del producto a SOLD
si la condición en la que el estado del producto está establecido actualmente en IN_STOCK
es true
. Configuración del parámetro ReturnValuesOnConditionCheckFailure
devuelve el elemento si el atributo de estado del producto del artículo no era 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);
Crear el pedido
Por último, cree el pedido siempre y cuando un pedido con ese OrderId
no existe.
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 + ")");
Ejecutar la transacción
En el siguiente ejemplo de la se muestra cómo ejecutar las acciones definidas anteriormente como una única operación de tipo “todo o 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()); }
Leer los detalles del pedido
En el siguiente ejemplo se muestra cómo leer el pedido completado transaccionalmente a través de las tablas Orders
y 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()); }