

# DynamoDB 事务示例
<a name="transaction-example"></a>

作为 Amazon DynamoDB Transactions 可能有用的情况示例，请考虑此适用于在线商城的 Java 应用程序示例。

该应用程序在后端有三个 DynamoDB 表：
+ `Customers`— 此表存储有关商城买家的详细信息。它的主键是 `CustomerId` 唯一标识符。
+ `ProductCatalog`— 此表存储有关商品在商城中销售的价格和供货情况等详细信息。它的主键是 `ProductId` 唯一标识符。
+ `Orders`— 此表存储有关来自市场的订单的详细信息。它的主键是 `OrderId` 唯一标识符。

## 下订单
<a name="transaction-example-write-order"></a>

以下代码片段说明了如何使用 DynamoDB 事务来协调创建和处理订单所需的多个步骤。使用单个全有或全无操作可确保如果事务的任何部分失败，事务中不会运行任何操作，也不会进行任何更改。

在此示例中，您设置来自 `customerId` 为 `09e8e9c8-ec48` 的客户订单。然后，您可以使用以下简单的订单处理工作流将其作为单个事务处理运行：

1. 确定客户 ID 是否有效。

1. 确保产品是 `IN_STOCK`，并将产品状态更新为 `SOLD`。

1. 确保订单不存在，然后创建订单。

### 验证客户
<a name="transaction-example-order-part-a"></a>

首先，定义一个操作以验证 `customerId` 等于 `09e8e9c8-ec48` 的客户存在于客户表中。

```
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 + ")");
```

### 更新产品状态
<a name="transaction-example-order-part-b"></a>

接下来，定义一个操作，如果产品状态当前设置为 `IN_STOCK` 的条件为 `true`，则将产品状态更新为 `SOLD`。设置 `ReturnValuesOnConditionCheckFailure` 参数，如果项目的产品状态属性不等于 `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);
```

### 创建订单
<a name="transaction-example-order-part-c"></a>

最后，只要不存在具有 `OrderId` 的订单，则创建订单。

```
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 + ")");
```

### 运行事务
<a name="transaction-example-order-part-d"></a>

以下示例说明如何运行以前定义为单个全有或全无操作的操作。

```
    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());
    }
```

## 阅读订单详细信息
<a name="transaction-example-read-order"></a>

以下示例演示了如何以事务方式读取 `Orders` 和 `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());
}
```