ネストされた属性の使用 - AWS SDK for Java 2.x

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

ネストされた属性の使用

DynamoDB のネストされた属性は、別の属性に埋め込まれています。例としては、リスト要素やマップエントリがあります。

Java では、DynamoDB のネストされた属性は、List または Map のクラスのメンバーに対応します。また、次の Person クラスで使用されている AddressPhoneNumber などの複合型のインスタンスにも対応しています。

@DynamoDbBean public class Person { Integer id; String firstName; String lastName; Integer age; Map<String, Address> addresses; List<PhoneNumber> phoneNumbers; List<String> hobbies; @DynamoDbPartitionKey public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Map<String, Address> getAddresses() { return addresses; } public void setAddresses(Map<String, Address> addresses) { this.addresses = addresses; } public List<PhoneNumber> getPhoneNumbers() { return phoneNumbers; } public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) { this.phoneNumbers = phoneNumbers; } public List<String> getHobbies() { return hobbies; } public void setHobbies(List<String> hobbies) { this.hobbies = hobbies; } @Override public String toString() { return "Person{" + "id=" + id + ", firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", age=" + age + ", addresses=" + addresses + ", phoneNumbers=" + phoneNumbers + ", hobbies=" + hobbies + '}'; } }
@DynamoDbBean public class Address { private String street; private String city; private String state; private String zipCode; public Address() { } public String getStreet() { return this.street; } public String getCity() { return this.city; } public String getState() { return this.state; } public String getZipCode() { return this.zipCode; } public void setStreet(String street) { this.street = street; } public void setCity(String city) { this.city = city; } public void setState(String state) { this.state = state; } public void setZipCode(String zipCode) { this.zipCode = zipCode; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Address address = (Address) o; return Objects.equals(street, address.street) && Objects.equals(city, address.city) && Objects.equals(state, address.state) && Objects.equals(zipCode, address.zipCode); } @Override public int hashCode() { return Objects.hash(street, city, state, zipCode); } @Override public String toString() { return "Address{" + "street='" + street + '\'' + ", city='" + city + '\'' + ", state='" + state + '\'' + ", zipCode='" + zipCode + '\'' + '}'; } }
@DynamoDbBean public class PhoneNumber { String type; String number; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } @Override public String toString() { return "PhoneNumber{" + "type='" + type + '\'' + ", number='" + number + '\'' + '}'; } }

ネストされた属性をマップする

注釈付きのクラスを使用する

カスタムクラスのネストされた属性は、注釈を付けることで保存できます。前に示した Address クラスと PhoneNumber クラスには、@DynamoDbBean 注釈のみの注釈が付けられています。DynamoDB Enhanced Client API が次のスニペットを使用して Person クラスのテーブルスキーマを構築すると、API はAddress および PhoneNumber クラスの使用を検出し、DynamoDB と動作する対応するマッピングを構築します。

TableSchema<Person> personTableSchema = TableSchema.fromBean(Person.class);

ネストされたスキーマを使用する

別の方法は、次のコードに示すように、各クラスに静的テーブルスキーマビルダーを使用することです。

Address および PhoneNumber クラスのテーブルスキーマは、DynamoDB テーブルでは使用できないという意味で抽象的です。これは、プライマリキーの定義が不足しているためです。ただし、Person クラスのテーブルスキーマではネストされたスキーマとして使用されます。

PERSON_TABLE_SCHEMA の定義のコメント 1 行目と 2 行目の後に、抽象テーブルスキーマを使用するコードが表示されます。EnhanceType.documentOf(...) メソッド内で documentOf を使用しても、メソッドが拡張ドキュメント API の EnhancedDocument タイプを返すことを示すわけではありません。このコンテキストの documentOf(...) メソッドは、テーブルスキーマ引数を使用して DynamoDB テーブル属性との間でクラス引数をマッピングする方法を知っているオブジェクトを返します。

// Abstract table schema that cannot be used to work with a DynamoDB table, // but can be used as a nested schema. public static final TableSchema<Address> TABLE_SCHEMA_ADDRESS = TableSchema.builder(Address.class) .newItemSupplier(Address::new) .addAttribute(String.class, a -> a.name("street") .getter(Address::getStreet) .setter(Address::setStreet)) .addAttribute(String.class, a -> a.name("city") .getter(Address::getCity) .setter(Address::setCity)) .addAttribute(String.class, a -> a.name("zipcode") .getter(Address::getZipCode) .setter(Address::setZipCode)) .addAttribute(String.class, a -> a.name("state") .getter(Address::getState) .setter(Address::setState)) .build(); // Abstract table schema that cannot be used to work with a DynamoDB table, // but can be used as a nested schema. public static final TableSchema<PhoneNumber> TABLE_SCHEMA_PHONENUMBER = TableSchema.builder(PhoneNumber.class) .newItemSupplier(PhoneNumber::new) .addAttribute(String.class, a -> a.name("type") .getter(PhoneNumber::getType) .setter(PhoneNumber::setType)) .addAttribute(String.class, a -> a.name("number") .getter(PhoneNumber::getNumber) .setter(PhoneNumber::setNumber)) .build(); // A static table schema that can be used with a DynamoDB table. // The table schema contains two nested schemas that are used to perform mapping to/from DynamoDB. public static final TableSchema<Person> PERSON_TABLE_SCHEMA = TableSchema.builder(Person.class) .newItemSupplier(Person::new) .addAttribute(Integer.class, a -> a.name("id") .getter(Person::getId) .setter(Person::setId) .addTag(StaticAttributeTags.primaryPartitionKey())) .addAttribute(String.class, a -> a.name("firstName") .getter(Person::getFirstName) .setter(Person::setFirstName)) .addAttribute(String.class, a -> a.name("lastName") .getter(Person::getLastName) .setter(Person::setLastName)) .addAttribute(Integer.class, a -> a.name("age") .getter(Person::getAge) .setter(Person::setAge)) .addAttribute(EnhancedType.listOf(String.class), a -> a.name("hobbies") .getter(Person::getHobbies) .setter(Person::setHobbies)) .addAttribute(EnhancedType.mapOf( EnhancedType.of(String.class), // 1. Use mapping functionality of the Address table schema. EnhancedType.documentOf(Address.class, TABLE_SCHEMA_ADDRESS)), a -> a.name("addresses") .getter(Person::getAddresses) .setter(Person::setAddresses)) .addAttribute(EnhancedType.listOf( // 2. Use mapping functionality of the PhoneNumber table schema. EnhancedType.documentOf(PhoneNumber.class, TABLE_SCHEMA_PHONENUMBER)), a -> a.name("phoneNumbers") .getter(Person::getPhoneNumbers) .setter(Person::setPhoneNumbers)) .build();

プロジェクトのネストされた属性

query() および scan() メソッドでは、addNestedAttributeToProject()attributesToProject() などのメソッドの呼び出しを使用して、結果で返される属性を指定できます。DynamoDB 拡張クライアント API は、リクエストが送信される前に Java メソッド呼び出しパラメータをプロジェクション式に変換します。

次の例では、Person テーブルに 2 つの項目を設定し、3 回のスキャンオペレーションを実行します。

1 回目のスキャンでは、結果を他のスキャンオペレーションと比較するために、テーブル内のすべての項目にアクセスします。

2 回目のスキャンでは、addNestedAttributeToProject() ビルダーメソッドを使用して street 属性値のみを返します。

3 回目のスキャンオペレーションでは、attributesToProject() ビルダーメソッドを使用して第 1 レベルの属性のデータ、hobbies を返します。hobbies の属性タイプはリストです。個々のリスト項目にアクセスするには、リストに対して get() オペレーションを実行します。

personDynamoDbTable = getDynamoDbEnhancedClient().table("Person", PERSON_TABLE_SCHEMA); PersonUtils.createPersonTable(personDynamoDbTable, getDynamoDbClient()); // Use a utility class to add items to the Person table. List<Person> personList = PersonUtils.getItemsForCount(2); // This utility method performs a put against DynamoDB to save the instances in the list argument. PersonUtils.putCollection(getDynamoDbEnhancedClient(), personList, personDynamoDbTable); // The first scan logs all items in the table to compare to the results of the subsequent scans. final PageIterable<Person> allItems = personDynamoDbTable.scan(); allItems.items().forEach(p -> // 1. Log what is in the table. logger.info(p.toString())); // Scan for nested attributes. PageIterable<Person> streetScanResult = personDynamoDbTable.scan(b -> b // Use the 'addNestedAttributeToProject()' or 'addNestedAttributesToProject()' to access data nested in maps in DynamoDB. .addNestedAttributeToProject( NestedAttributeName.create("addresses", "work", "street") )); streetScanResult.items().forEach(p -> //2. Log the results of requesting nested attributes. logger.info(p.toString())); // Scan for a top-level list attribute. PageIterable<Person> phoneNumbersScanResult = personDynamoDbTable.scan(b -> b // Use the 'attributesToProject()' method to access first-level attributes. .attributesToProject("hobbies")); phoneNumbersScanResult.items().forEach((p) -> { // 3. Log the results of the request for the 'hobbies' attribute. logger.info(p.toString()); // To access an item in a list, first get the parent attribute, 'hobbies', then access items in the list. String hobby = p.getHobbies().get(1); // 4. Log an item in the list. logger.info(hobby); });
// Logged results from comment line 1. Person{id=2, firstName='first name 2', lastName='last name 2', age=11, addresses={work=Address{street='street 21', city='city 21', state='state 21', zipCode='33333'}, home=Address{street='street 2', city='city 2', state='state 2', zipCode='22222'}}, phoneNumbers=[PhoneNumber{type='home', number='222-222-2222'}, PhoneNumber{type='work', number='333-333-3333'}], hobbies=[hobby 2, hobby 21]} Person{id=1, firstName='first name 1', lastName='last name 1', age=11, addresses={work=Address{street='street 11', city='city 11', state='state 11', zipCode='22222'}, home=Address{street='street 1', city='city 1', state='state 1', zipCode='11111'}}, phoneNumbers=[PhoneNumber{type='home', number='111-111-1111'}, PhoneNumber{type='work', number='222-222-2222'}], hobbies=[hobby 1, hobby 11]} // Logged results from comment line 2. Person{id=null, firstName='null', lastName='null', age=null, addresses={work=Address{street='street 21', city='null', state='null', zipCode='null'}}, phoneNumbers=null, hobbies=null} Person{id=null, firstName='null', lastName='null', age=null, addresses={work=Address{street='street 11', city='null', state='null', zipCode='null'}}, phoneNumbers=null, hobbies=null} // Logged results from comment lines 3 and 4. Person{id=null, firstName='null', lastName='null', age=null, addresses=null, phoneNumbers=null, hobbies=[hobby 2, hobby 21]} hobby 21 Person{id=null, firstName='null', lastName='null', age=null, addresses=null, phoneNumbers=null, hobbies=[hobby 1, hobby 11]} hobby 11
注記

その attributesToProject() メソッドが、プロジェクションする属性を追加する他のビルダーメソッドに続く場合は、attributesToProject() に渡された属性名のリストは他のすべての属性名を置き換えます。

次のスニペット ScanEnhancedRequest のインスタンスで実行されたスキャンでは、ホビーデータのみが返されます。

ScanEnhancedRequest lastOverwrites = ScanEnhancedRequest.builder() .addNestedAttributeToProject( NestedAttributeName.create("addresses", "work", "street")) .addAttributeToProject("firstName") // If the 'attributesToProject()' method follows other builder methods that add attributes for projection, // its list of attributes replace all previous attributes. .attributesToProject("hobbies") .build(); PageIterable<Person> hobbiesOnlyResult = personDynamoDbTable.scan(lastOverwrites); hobbiesOnlyResult.items().forEach(p -> logger.info(p.toString())); // Logged results. Person{id=null, firstName='null', lastName='null', age=null, addresses=null, phoneNumbers=null, hobbies=[hobby 2, hobby 21]} Person{id=null, firstName='null', lastName='null', age=null, addresses=null, phoneNumbers=null, hobbies=[hobby 1, hobby 11]}

以下のコード例では、最初に attributesToProject() メソッドを使用します。この順序付けでは、要求された他のすべての属性が保持されます。

ScanEnhancedRequest attributesPreserved = ScanEnhancedRequest.builder() // Use 'attributesToProject()' first so that the method call does not replace all other attributes // that you want to project. .attributesToProject("firstName") .addNestedAttributeToProject( NestedAttributeName.create("addresses", "work", "street")) .addAttributeToProject("hobbies") .build(); PageIterable<Person> allAttributesResult = personDynamoDbTable.scan(attributesPreserved); allAttributesResult.items().forEach(p -> logger.info(p.toString())); // Logged results. Person{id=null, firstName='first name 2', lastName='null', age=null, addresses={work=Address{street='street 21', city='null', state='null', zipCode='null'}}, phoneNumbers=null, hobbies=[hobby 2, hobby 21]} Person{id=null, firstName='first name 1', lastName='null', age=null, addresses={work=Address{street='street 11', city='null', state='null', zipCode='null'}}, phoneNumbers=null, hobbies=[hobby 1, hobby 11]}