DynamoDB 拡張クライアントの基本を学ぶ API - AWS SDK for Java 2.x

翻訳は機械翻訳により提供されています。提供された翻訳内容と英語版の間で齟齬、不一致または矛盾がある場合、英語版が優先します。

DynamoDB 拡張クライアントの基本を学ぶ API

このトピックでは、DynamoDB 拡張クライアントの基本機能について説明APIし、それを標準の DynamoDB クライアント APIと比較します。

DynamoDB 拡張クライアント を初めて使用する場合はAPI、入門チュートリアルを確認して基本的なクラスに慣れることをお勧めします。

Java の DynamoDB アイテム

DynamoDB テーブルにはアイテムが保存されます。ユースケースに応じて、Java 側のアイテムは静的に構造化されたデータまたは動的に作成された構造の形をとることができます。

ユースケースで一貫性のある属性セットを持つアイテムが必要な場合は、注釈付きクラスを使用するか、ビルダーを使用して適切な静的型 TableSchema を生成します。

または、さまざまな構造で構成される項目を保存する必要がある場合は、 を作成しますDocumentTableSchemaDocumentTableSchema拡張ドキュメントAPIの一部であり、静的にタイプされたプライマリキーのみを必要とし、 EnhancedDocument インスタンスと連携してデータ要素を保持します。拡張ドキュメントAPIは、別のトピックで説明します。

データモデルクラスの属性タイプ

DynamoDB は、Java のリッチタイプシステムと比較して少数の属性タイプをサポートしていますが、DynamoDB 拡張クライアントAPIは、Java クラスのメンバーを DynamoDB 属性タイプとの間で変換するメカニズムを提供します。

Java データクラスの属性タイプ (プロパティ) は、プリミティブではなくオブジェクトタイプである必要があります。例えば、 Long および int プリミティブではなく、常に longおよび Integer オブジェクトのデータ型を使用します。

デフォルトでは、DynamoDB 拡張クライアントは、整数 、文字列 https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html、、およびインスタント BigDecimalなどの多数のタイプの属性コンバータAPIをサポートしています。 https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/InstantAsStringAttributeConverter.htmlリストは、 AttributeConverter インターフェイス の既知の実装クラスに表示されます。リストには、マップ、リスト、セットなど、さまざまなタイプとコレクションが含まれています。

デフォルトでサポートされていない、または JavaBean 規則に準拠していない属性タイプのデータを保存するには、変換を実行するカスタムAttributeConverter実装を記述できます。については、「属性変換」セクションを参照してください。

クラスが Java Bean 仕様に準拠する属性型 (または不変データクラス) のデータを保存するには、2 つの方法があります。

  • ソースファイルにアクセスできる場合は、クラスに @DynamoDbBean (または @DynamoDbImmutable) という注釈を付けることができます。ネストされた属性について説明しているセクションでは、注釈付きクラスの使用を示しています。

  • 属性 JavaBean のデータクラスのソースファイルにアクセスできない場合 (またはアクセス可能なクラスのソースファイルに注釈を付けたくない場合) は、ビルダーアプローチを使用できます。これにより、キーを定義せずにテーブルスキーマが作成されます。次に、このテーブルスキーマを別のテーブルスキーマ内にネストしてマッピングを実行できます。ネストされた属性セクションには、ネストされたスキーマの使用例があります。

Null 値

putItem メソッドを使用する場合、拡張クライアントは DynamoDB へのリクエストにマッピングされたデータオブジェクトの null 値属性を含めません。

updateItem リクエストの SDKのデフォルトの動作は、updateItemメソッドで送信するオブジェクトで null に設定されている DynamoDB の項目から属性を削除します。一部の属性値を更新し、他の属性値を変更しない場合は、2 つのオプションがあります。

  • 値を変更する前に ( を使用してgetItem) 項目を取得します。このアプローチを使用すると、 はすべての更新値と古い値を DynamoDB SDKに送信します。

  • 項目を更新するリクエストを構築するIgnoreNullsMode.MAPS_ONLYときは、 IgnoreNullsMode.SCALAR_ONLY または のいずれかを使用します。どちらのモードも、DynamoDB のスカラー属性を表すオブジェクト内の NULL 値プロパティを無視します。このガイドの複雑なタイプを含む項目を更新するトピックには、IgnoreNullsMode値の詳細と複雑なタイプの操作方法が記載されています。

次の例は、 updateItem()メソッドignoreNullsMode()を示しています。

public static void updateItemNullsExample() { Customer customer = new Customer(); customer.setCustName("CustomerName"); customer.setEmail("email"); customer.setId("1"); customer.setRegistrationDate(Instant.now()); logger.info("Original customer: {}", customer); // Put item with values for all attributes. try { customerAsyncDynamoDbTable.putItem(customer).join(); } catch (RuntimeException rte) { logger.error("A exception occurred during putItem: {}", rte.getCause().getMessage(), rte); return; } // Create a Customer instance with the same 'id' and 'email' values, but a different 'name' value. // Do not set the 'registrationDate' attribute. Customer customerForUpdate = new Customer(); customerForUpdate.setCustName("NewName"); customerForUpdate.setEmail("email"); customerForUpdate.setId("1"); // Update item without setting the 'registrationDate' property and set IgnoreNullsMode to SCALAR_ONLY. try { Customer updatedWithNullsIgnored = customerAsyncDynamoDbTable.updateItem(b -> b .item(customerForUpdate) .ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY)) .join(); logger.info("Customer updated with nulls ignored: {}", updatedWithNullsIgnored.toString()); } catch (RuntimeException rte) { logger.error("An exception occurred during updateItem: {}", rte.getCause().getMessage(), rte); return; } // Update item without setting the registrationDate attribute and not setting ignoreNulls to true. try { Customer updatedWithNullsUsed = customerAsyncDynamoDbTable.updateItem(customerForUpdate) .join(); logger.info("Customer updated with nulls used: {}", updatedWithNullsUsed.toString()); } catch (RuntimeException rte) { logger.error("An exception occurred during updateItem: {}", rte.getCause().getMessage(), rte); } } // Logged lines. Original customer: Customer [id=1, name=CustomerName, email=email, regDate=2024-10-11T14:12:30.222858Z] Customer updated with nulls ignored: Customer [id=1, name=NewName, email=email, regDate=2024-10-11T14:12:30.222858Z] Customer updated with nulls used: Customer [id=1, name=NewName, email=email, regDate=null]

DynamoDB Enhanced Client API の基本メソッド

拡張クライアントの基本的なメソッドは、その名前に由来する DynamoDB サービスオペレーションにマッピングされます。次の例は、各方法の最も単純なバリエーションを示しています。拡張リクエストオブジェクトを渡すことで、各メソッドをカスタマイズできます。拡張リクエストオブジェクトは、標準の DynamoDB クライアントで使用できるほとんどの機能を提供します。これらは、 AWS SDK for Java 2.x API リファレンスに完全に文書化されています。

この例では前に示した Customer クラス を使用しています。

// CreateTable customerTable.createTable(); // GetItem Customer customer = customerTable.getItem(Key.builder().partitionValue("a123").build()); // UpdateItem Customer updatedCustomer = customerTable.updateItem(customer); // PutItem customerTable.putItem(customer); // DeleteItem Customer deletedCustomer = customerTable.deleteItem(Key.builder().partitionValue("a123").sortValue(456).build()); // Query PageIterable<Customer> customers = customerTable.query(keyEqualTo(k -> k.partitionValue("a123"))); // Scan PageIterable<Customer> customers = customerTable.scan(); // BatchGetItem BatchGetResultPageIterable batchResults = enhancedClient.batchGetItem(r -> r.addReadBatch(ReadBatch.builder(Customer.class) .mappedTableResource(customerTable) .addGetItem(key1) .addGetItem(key2) .addGetItem(key3) .build())); // BatchWriteItem batchResults = enhancedClient.batchWriteItem(r -> r.addWriteBatch(WriteBatch.builder(Customer.class) .mappedTableResource(customerTable) .addPutItem(customer) .addDeleteItem(key1) .addDeleteItem(key1) .build())); // TransactGetItems transactResults = enhancedClient.transactGetItems(r -> r.addGetItem(customerTable, key1) .addGetItem(customerTable, key2)); // TransactWriteItems enhancedClient.transactWriteItems(r -> r.addConditionCheck(customerTable, i -> i.key(orderKey) .conditionExpression(conditionExpression)) .addUpdateItem(customerTable, customer) .addDeleteItem(customerTable, key));

DynamoDB Enhanced Client を標準の DynamoDB クライアントと比較する

DynamoDB クライアント APIs— 標準および拡張 — では、DynamoDB テーブルを使用してデータレベルのオペレーション CRUD (作成、読み取り、更新、削除) を実行できます。クライアントの違いAPIsは、その達成方法です。標準クライアントを使用すると、低レベルのデータ属性を直接操作できます。拡張クライアントは使い慣れた Java クラスAPIを使用し、シーンの背API後にある低レベルにマッピングします。

両方のクライアントがデータレベルのオペレーションAPIsをサポートしている一方で、標準の DynamoDB クライアントはリソースレベルのオペレーションもサポートしています。リソースレベルのオペレーションは、バックアップの作成、テーブルの一覧表示、テーブルの更新など、データベースを管理します。拡張クライアントAPIは、テーブルの作成、説明、削除など、リソースレベルのオペレーションの一部をサポートしています。

2 つのクライアント で使用されるさまざまなアプローチを説明するためにAPIs、次のコード例は、標準クライアントと拡張クライアントを使用した同じProductCatalogテーブルの作成を示しています。

比較: 標準の DynamoDB クライアントを使用してテーブルを作成

DependencyFactory.dynamoDbClient().createTable(builder -> builder .tableName(TABLE_NAME) .attributeDefinitions( b -> b.attributeName("id").attributeType(ScalarAttributeType.N), b -> b.attributeName("title").attributeType(ScalarAttributeType.S), b -> b.attributeName("isbn").attributeType(ScalarAttributeType.S) ) .keySchema( builder1 -> builder1.attributeName("id").keyType(KeyType.HASH), builder2 -> builder2.attributeName("title").keyType(KeyType.RANGE) ) .globalSecondaryIndexes(builder3 -> builder3 .indexName("products_by_isbn") .keySchema(builder2 -> builder2 .attributeName("isbn").keyType(KeyType.HASH)) .projection(builder2 -> builder2 .projectionType(ProjectionType.INCLUDE) .nonKeyAttributes("price", "authors")) .provisionedThroughput(builder4 -> builder4 .writeCapacityUnits(5L).readCapacityUnits(5L)) ) .provisionedThroughput(builder1 -> builder1 .readCapacityUnits(5L).writeCapacityUnits(5L)) );

比較: DynamoDB Enhanced Client を使用したテーブルの作成

DynamoDbEnhancedClient enhancedClient = DependencyFactory.enhancedClient(); productCatalog = enhancedClient.table(TABLE_NAME, TableSchema.fromImmutableClass(ProductCatalog.class)); productCatalog.createTable(b -> b .provisionedThroughput(b1 -> b1.readCapacityUnits(5L).writeCapacityUnits(5L)) .globalSecondaryIndices(b2 -> b2.indexName("products_by_isbn") .projection(b4 -> b4 .projectionType(ProjectionType.INCLUDE) .nonKeyAttributes("price", "authors")) .provisionedThroughput(b3 -> b3.writeCapacityUnits(5L).readCapacityUnits(5L)) ) );

拡張クライアントは、以下の注釈付きデータクラスを使用します。DynamoDB Enhanced Client は、Java データ型を DynamoDB データ型にマッピングして、より簡潔でわかりやすいコードにします。ProductCatalog は、DynamoDB Enhanced Client で不変クラスを使用する例です。マッピングされたデータクラスでの不変クラスの使用については、このトピックの後半で説明します

package org.example.tests.model; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbIgnore; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbImmutable; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondaryPartitionKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey; import java.math.BigDecimal; import java.util.Objects; import java.util.Set; @DynamoDbImmutable(builder = ProductCatalog.Builder.class) public class ProductCatalog implements Comparable<ProductCatalog> { private Integer id; private String title; private String isbn; private Set<String> authors; private BigDecimal price; private ProductCatalog(Builder builder){ this.authors = builder.authors; this.id = builder.id; this.isbn = builder.isbn; this.price = builder.price; this.title = builder.title; } public static Builder builder(){ return new Builder(); } @DynamoDbPartitionKey public Integer id() { return id; } @DynamoDbSortKey public String title() { return title; } @DynamoDbSecondaryPartitionKey(indexNames = "products_by_isbn") public String isbn() { return isbn; } public Set<String> authors() { return authors; } public BigDecimal price() { return price; } public static final class Builder { private Integer id; private String title; private String isbn; private Set<String> authors; private BigDecimal price; private Builder(){} public Builder id(Integer id) { this.id = id; return this; } public Builder title(String title) { this.title = title; return this; } public Builder isbn(String ISBN) { this.isbn = ISBN; return this; } public Builder authors(Set<String> authors) { this.authors = authors; return this; } public Builder price(BigDecimal price) { this.price = price; return this; } public ProductCatalog build() { return new ProductCatalog(this); } } @Override public String toString() { final StringBuffer sb = new StringBuffer("ProductCatalog{"); sb.append("id=").append(id); sb.append(", title='").append(title).append('\''); sb.append(", isbn='").append(isbn).append('\''); sb.append(", authors=").append(authors); sb.append(", price=").append(price); sb.append('}'); return sb.toString(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ProductCatalog that = (ProductCatalog) o; return id.equals(that.id) && title.equals(that.title) && Objects.equals(isbn, that.isbn) && Objects.equals(authors, that.authors) && Objects.equals(price, that.price); } @Override public int hashCode() { return Objects.hash(id, title, isbn, authors, price); } @Override @DynamoDbIgnore public int compareTo(ProductCatalog other) { if (this.id.compareTo(other.id) != 0){ return this.id.compareTo(other.id); } else { return this.title.compareTo(other.title); } } }

次の 2 つのバッチ文字起こしのコード例は、拡張クライアントではなく標準クライアントを使用する場合の冗長性とタイプの安全性の欠如を示しています。

public static void batchWriteStandard(DynamoDbClient dynamoDbClient, String tableName) { Map<String, AttributeValue> catalogItem = Map.of( "authors", AttributeValue.builder().ss("a", "b").build(), "id", AttributeValue.builder().n("1").build(), "isbn", AttributeValue.builder().s("1-565-85698").build(), "title", AttributeValue.builder().s("Title 1").build(), "price", AttributeValue.builder().n("52.13").build()); Map<String, AttributeValue> catalogItem2 = Map.of( "authors", AttributeValue.builder().ss("a", "b", "c").build(), "id", AttributeValue.builder().n("2").build(), "isbn", AttributeValue.builder().s("1-208-98073").build(), "title", AttributeValue.builder().s("Title 2").build(), "price", AttributeValue.builder().n("21.99").build()); Map<String, AttributeValue> catalogItem3 = Map.of( "authors", AttributeValue.builder().ss("g", "k", "c").build(), "id", AttributeValue.builder().n("3").build(), "isbn", AttributeValue.builder().s("7-236-98618").build(), "title", AttributeValue.builder().s("Title 3").build(), "price", AttributeValue.builder().n("42.00").build()); Set<WriteRequest> writeRequests = Set.of( WriteRequest.builder().putRequest(b -> b.item(catalogItem)).build(), WriteRequest.builder().putRequest(b -> b.item(catalogItem2)).build(), WriteRequest.builder().putRequest(b -> b.item(catalogItem3)).build()); Map<String, Set<WriteRequest>> productCatalogItems = Map.of( "ProductCatalog", writeRequests); BatchWriteItemResponse response = dynamoDbClient.batchWriteItem(b -> b.requestItems(productCatalogItems)); logger.info("Unprocessed items: " + response.unprocessedItems().size()); }
public static void batchWriteEnhanced(DynamoDbTable<ProductCatalog> productCatalog) { ProductCatalog prod = ProductCatalog.builder() .id(1) .isbn("1-565-85698") .authors(new HashSet<>(Arrays.asList("a", "b"))) .price(BigDecimal.valueOf(52.13)) .title("Title 1") .build(); ProductCatalog prod2 = ProductCatalog.builder() .id(2) .isbn("1-208-98073") .authors(new HashSet<>(Arrays.asList("a", "b", "c"))) .price(BigDecimal.valueOf(21.99)) .title("Title 2") .build(); ProductCatalog prod3 = ProductCatalog.builder() .id(3) .isbn("7-236-98618") .authors(new HashSet<>(Arrays.asList("g", "k", "c"))) .price(BigDecimal.valueOf(42.00)) .title("Title 3") .build(); BatchWriteResult batchWriteResult = DependencyFactory.enhancedClient() .batchWriteItem(b -> b.writeBatches( WriteBatch.builder(ProductCatalog.class) .mappedTableResource(productCatalog) .addPutItem(prod).addPutItem(prod2).addPutItem(prod3) .build() )); logger.info("Unprocessed items: " + batchWriteResult.unprocessedPutItemsForTable(productCatalog).size()); }