

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 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` 的動作。如果項目的產品狀態屬性不等於 `IN_STOCK`，則設定 `ReturnValuesOnConditionCheckFailure` 參數會傳回項目。

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