

# DynamoDB トランザクションの例
<a name="transaction-example"></a>

Amazon DynamoDB Transactions が役に立つ状況の例として、このサンプルの Java アプリケーションをオンラインマーケットプレイスで検討してください。

アプリケーションには、バックエンドに 3 つの 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());
}
```