拡張機能を使用して DynamoDB 拡張クライアントオペレーションをカスタマイズする
DynamoDB 拡張クライアント API は、マッピング操作以外の機能を提供するプラグイン拡張機能をサポートしています。拡張機能は、読み取りおよび書き込みオペレーション中に 2 つのフックメソッドを使用してデータを変更します。
beforeWrite()- 発生する前に書き込みオペレーションを変更するafterRead()- 読み取りオペレーションの実行後にその結果を変更する
一部のオペレーション (アイテムの更新など) は書き込みと読み取りの両方を実行するため、両方のフックメソッドが呼び出されます。
拡張機能のロード方法
拡張機能は、拡張クライアントビルダーで指定された順序でロードされます。1 つのエクステンションが以前のエクステンションによって変換された値に作用する可能性があるため、ロード順序は重要な場合があります。
デフォルトでは、拡張クライアントは 2 つの拡張機能をロードします。
VersionedRecordExtension- 楽観的ロックを提供するAtomicCounterExtension- カウンター属性を自動的に増分する
拡張クライアントビルダーでデフォルトの動作を上書きして、任意の拡張機能を読み込むことができます。デフォルトの拡張機能を使いたくない場合は、none を指定することもできます。
重要
独自の拡張機能をロードしても、拡張クライアントはデフォルトの拡張機能をロードしません。いずれかのデフォルトの拡張機能と同じ動作をさせたい場合は、拡張機能のリストに明示的に追加する必要があります。
次の例は、 VersionedRecordExtension の後に verifyChecksumExtension という名前のカスタム拡張機能をロードする方法を示しています。この例では、AtomicCounterExtension はロードされていません。
DynamoDbEnhancedClientExtension versionedRecordExtension = VersionedRecordExtension.builder().build(); DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder() .dynamoDbClient(dynamoDbClient) .extensions(versionedRecordExtension, verifyChecksumExtension) .build();
利用可能な拡張機能の詳細と設定
以下のセクションでは、 SDK で使用可能な各拡張機能に関する詳細情報を記載します。
VersionedRecordExtension を使用して楽観的ロックを実装する
VersionedRecordExtension 拡張機能は、アイテムがデータベースに書き込まれるとアイテムのバージョン番号を増加して追跡することで、楽観的ロックを提供します。実際に保存されているアイテムのバージョン番号がアプリケーションが最後に読み取った値と一致しない場合、書き込みが失敗する原因となる条件が書き込みごとに追加されます。
設定
アイテムのバージョン番号を追跡するために使用する属性を指定するには、テーブルスキーマの数値属性にタグを付けます。
次のスニペットでは、version 属性がアイテムのバージョン番号を保持するように指定しています。
@DynamoDbVersionAttribute public Integer getVersion() {...}; public void setVersion(Integer version) {...};
これと同等の静的テーブルスキーマのアプローチは、次のスニペットで示されます。
.addAttribute(Integer.class, a -> a.name("version") .getter(Customer::getVersion) .setter(Customer::setVersion) // Apply the 'version' tag to the attribute. .tags(VersionedRecordExtension.AttributeTags.versionAttribute())
仕組み
VersionedRecordExtension を使用した楽観的ロックは、次の DynamoDbEnhancedClient および DynamoDbTable メソッドに対して以下のような影響があります。
putItem-
新しいアイテムには、初期バージョン値 0 が割り当てられます。これは
@DynamoDbVersionAttribute(startAt =で設定できます。X) updateItem-
項目を取得し、その項目の 1 つ以上のプロパティを更新して変更を保存する場合には、クライアント側とサーバー側のバージョン番号が一致する場合のみ、オペレーションが成功します。
成功すると、バージョン番号は自動的に 1 ずつ増加します。これは
@DynamoDbVersionAttribute(incrementBy =で設定できます。X) deleteItem-
DynamoDbVersionAttribute注釈は効力を発揮しません。アイテムを削除するときは、条件式を手動で追加する必要があります。次の例では、条件式を追加して、削除されたアイテムが読み取られたアイテムであることを確認します。次の例では、
recordVersionは@DynamoDbVersionAttributeで注釈を付けた Bean の属性です。// 1. Read the item and get its current version. Customer item = customerTable.getItem(Key.builder().partitionValue("someId").build()); // `recordVersion` is the bean's attribute that is annotated with `@DynamoDbVersionAttribute`. AttributeValue currentVersion = item.getRecordVersion(); // 2. Create conditional delete with the `currentVersion` value. DeleteItemEnhancedRequest deleteItemRequest = DeleteItemEnhancedRequest.builder() .key(KEY) .conditionExpression(Expression.builder() .expression("recordVersion = :current_version_value") .putExpressionValue(":current_version_value", currentVersion) .build()).build(); customerTable.deleteItem(deleteItemRequest); transactWriteItems-
-
addPutItem: このメソッドの動作はputItemと同じです。 -
addUpdateItem: このメソッドの動作はupdateItemと同じです。 -
addDeleteItem: このメソッドの動作はdeleteItemと同じです。
-
batchWriteItem-
-
addPutItem: このメソッドの動作はputItemと同じです。 -
addDeleteItem: このメソッドの動作はdeleteItemと同じです。
-
注記
DynamoDB グローバルテーブルでは最終書き込み者優先を使用して、同時更新間の調整を行い、DynamoDB は最終書き込み者を判断するために最善を尽くします。グローバルテーブルを使用する場合、この「最終書き込み者優先」ポリシーは、すべてのレプリカが最終的に DynamoDB によって決定された最後の書き込みに基づいて収束するため、ロック戦略が期待どおりに機能しない可能性があることを意味します。
無効にする方法
楽観的ロックを無効にするには、@DynamoDbVersionAttribute 注釈を使用しないようにします。
AtomicCounterExtension を使用してカウンターを実装する
AtomicCounterExtension 拡張機能は、レコードがデータベースに書き込まれるたびに、タグ付きの数値属性を増やします。開始値と増分値を指定できます。値を指定しない場合、開始値は 0 に設定され、属性の値は 1 ずつ増加します。
設定
どの属性がカウンターかを指定するには、テーブルスキーマのタイプ Long の属性にタグを付けます。
次のスニペットは、counter 属性のデフォルトの開始値と増分値の使用方法を示しています。
@DynamoDbAtomicCounter public Long getCounter() {...}; public void setCounter(Long counter) {...};
次のスニペットは、静的テーブルスキーマのアプローチを示しています。アトミックカウンタ拡張機能は、開始値を 10 に設定し、レコードが書き込まれるたびに値を 5 ずつ増やします。
.addAttribute(Integer.class, a -> a.name("counter") .getter(Customer::getCounter) .setter(Customer::setCounter) // Apply the 'atomicCounter' tag to the attribute with start and increment values. .tags(StaticAttributeTags.atomicCounter(10L, 5L))
AutoGeneratedTimestampRecordExtension でタイムスタンプを追加する
AutoGeneratedTimestampRecordExtension 拡張機能は、アイテムがデータベースに正常に書き込まれるたびに、そのタイプ Instant のタグ付き属性を現在のタイムスタンプで自動的に更新します。このエクステンションはデフォルトではロードされません。
設定
現在のタイムスタンプで更新する属性を指定するには、テーブルスキーマの Instant 属性にタグを付けます。
次のスニペットでは、lastUpdate 属性が拡張機能の動作の対象となります。属性は Instant タイプでなければならないという要件に注意してください。
@DynamoDbAutoGeneratedTimestampAttribute public Instant getLastUpdate() {...} public void setLastUpdate(Instant lastUpdate) {...}
これと同等の静的テーブルスキーマのアプローチは、次のスニペットで示されます。
.addAttribute(Instant.class, a -> a.name("lastUpdate") .getter(Customer::getLastUpdate) .setter(Customer::setLastUpdate) // Applying the 'autoGeneratedTimestamp' tag to the attribute. .tags(AutoGeneratedTimestampRecordExtension.AttributeTags.autoGeneratedTimestampAttribute())
AutoGeneratedUuidExtension を使用して UUID を生成する
AutoGeneratedUuidExtension 拡張機能は、新しいレコードがデータベースに書き込まれるときに、属性の一意の UUID (ユニバーサル一意識別子) を生成します。Java JDK UUID.randomUUID()java.lang.String 型の属性に適用されます。このエクステンションはデフォルトではロードされません。
設定
次のスニペットでは、uniqueId 属性が拡張機能の動作の対象となります。
@AutoGeneratedUuidExtension public String getUniqueId() {...} public void setUniqueId(String uniqueId) {...}
これと同等の静的テーブルスキーマのアプローチは、次のスニペットで示されます。
.addAttribute(String.class, a -> a.name("uniqueId") .getter(Customer::getUniqueId) .setter(Customer::setUniqueId) // Applying the 'autoGeneratedUuid' tag to the attribute. .tags(AutoGeneratedUuidExtension.AttributeTags.autoGeneratedUuidAttribute())
拡張機能で putItem メソッドに対してのみ UUID に入力されるようにし、updateItem メソッドに対しては入力されないようにするには、次のスニペットに示すように、更新動作
@AutoGeneratedUuidExtension @DynamoDbUpdateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS) public String getUniqueId() {...} public void setUniqueId(String uniqueId) {...}
静的テーブルスキーマアプローチを使用する場合は、次の同等のコードを使用します。
.addAttribute(String.class, a -> a.name("uniqueId") .getter(Customer::getUniqueId) .setter(Customer::setUniqueId) // Applying the 'autoGeneratedUuid' tag to the attribute. .tags(AutoGeneratedUuidExtension.AttributeTags.autoGeneratedUuidAttribute(), StaticAttributeTags.updateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS))