Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.
Bekerja dengan atribut yang kacang, peta, daftar, dan set
Definisi kacang, seperti Person
kelas yang ditunjukkan di bawah ini, mungkin mendefinisikan properti (atau atribut) yang merujuk ke tipe dengan atribut tambahan. Misalnya, di Person
kelas, mainAddress
adalah properti yang mengacu pada Address
kacang yang mendefinisikan atribut nilai tambahan. addresses
mengacu pada Peta Jawa, yang unsur-unsurnya mengacu pada Address
kacang. Tipe kompleks ini dapat dianggap sebagai wadah atribut sederhana yang Anda gunakan untuk nilai datanya dalam konteks DynamoDB.
DynamoDB mengacu pada properti nilai elemen bersarang, seperti peta, daftar, atau kacang, sebagai atribut bersarang. Panduan Pengembang Amazon DynamoDB mengacu pada bentuk yang disimpan dari peta Java, daftar atau kacang sebagai jenis dokumen. Atribut sederhana yang Anda gunakan untuk nilai data mereka di Java disebut sebagai tipe skalar di DynamoDB. Set, yang berisi beberapa elemen skalar dari jenis yang sama, dan disebut sebagai tipe set.
Penting untuk diketahui bahwa DynamoDB Enhanced API Client mengonversi properti yang bean ke jenis dokumen peta DynamoDB saat disimpan.
@DynamoDbBean public class Person { private Integer id; private String firstName; private String lastName; private Integer age; private Address mainAddress; private Map<String, Address> addresses; private List<PhoneNumber> phoneNumbers; private Set<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 Address getMainAddress() { return mainAddress; } public void setMainAddress(Address mainAddress) { this.mainAddress = mainAddress; } 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 Set<String> getHobbies() { return hobbies; } public void setHobbies(Set<String> hobbies) { this.hobbies = hobbies; } @Override public String toString() { return "Person{" + "addresses=" + addresses + ", id=" + id + ", firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", age=" + age + ", mainAddress=" + mainAddress + ", 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 + '\'' + '}'; } }
Simpan tipe kompleks
Gunakan kelas data beranotasi
Anda menyimpan atribut bersarang untuk kelas khusus hanya dengan membuat anotasi. Address
Kelas dan PhoneNumber
kelas yang ditampilkan sebelumnya dianotasi hanya dengan anotasi. @DynamoDbBean
Ketika DynamoDB Enhanced API Client membangun skema tabel untuk Person
kelas dengan cuplikan berikut, menemukan penggunaan kelas PhoneNumber
dan dan membangun pemetaan API yang sesuai untuk bekerja dengan DynamoDB. Address
TableSchema<Person> personTableSchema = TableSchema.fromBean(Person.class);
Gunakan skema abstrak dengan pembangun
Pendekatan alternatifnya adalah dengan menggunakan pembangun skema tabel statis untuk setiap kelas kacang bersarang seperti yang ditunjukkan pada kode berikut.
Skema tabel untuk PhoneNumber
kelas Address
dan abstrak dalam arti bahwa mereka tidak dapat digunakan dengan tabel DynamoDB. Ini karena mereka tidak memiliki definisi untuk kunci utama. Mereka digunakan, bagaimanapun, sebagai skema bersarang dalam skema tabel untuk kelas. Person
Setelah baris komentar 1 dan 2 dalam definisiPERSON_TABLE_SCHEMA
, Anda melihat kode yang menggunakan skema tabel abstrak. Penggunaan documentOf
dalam EnhanceType.documentOf(...)
metode ini tidak menunjukkan bahwa metode mengembalikan EnhancedDocument
jenis Dokumen yang DitingkatkanAPI. documentOf(...)
Metode dalam konteks ini mengembalikan objek yang tahu bagaimana memetakan argumen kelasnya ke dan dari DynamoDB atribut tabel dengan menggunakan argumen skema tabel.
// 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.documentOf(Address.class, TABLE_SCHEMA_ADDRESS), a -> a.name("mainAddress") .getter(Person::getMainAddress) .setter(Person::setMainAddress)) .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();
Atribut proyek dari tipe kompleks
Untuk query()
dan scan()
metode, Anda dapat menentukan atribut mana yang ingin Anda kembalikan dalam hasil dengan menggunakan panggilan metode seperti addNestedAttributeToProject()
danattributesToProject()
. DynamoDB Enhanced API Client mengubah parameter panggilan metode Java menjadi ekspresi proyeksi sebelum permintaan dikirim.
Contoh berikut mengisi Person
tabel dengan dua item, kemudian melakukan tiga operasi pemindaian.
Pemindaian pertama mengakses semua item dalam tabel untuk membandingkan hasilnya dengan operasi pemindaian lainnya.
Pemindaian kedua menggunakan metode addNestedAttributeToProject()
street
atribut.
Operasi pemindaian ketiga menggunakan metode attributesToProject()
hobbies
. Jenis atribut hobbies
adalah daftar. Untuk mengakses item daftar individual, lakukan get()
operasi pada daftar.
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> hobbiesScanResult = personDynamoDbTable.scan(b -> b // Use the 'attributesToProject()' method to access first-level attributes. .attributesToProject("hobbies")); hobbiesScanResult.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
catatan
Jika attributesToProject()
metode mengikuti metode pembangun lain yang menambahkan atribut yang ingin Anda proyeksikan, daftar nama atribut yang diberikan ke attributesToProject()
menggantikan semua nama atribut lainnya.
Pemindaian yang dilakukan dengan ScanEnhancedRequest
instance dalam cuplikan berikut hanya mengembalikan data hobi.
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]}
Cuplikan kode berikut menggunakan attributesToProject()
metode terlebih dahulu. Urutan ini mempertahankan semua atribut lain yang diminta.
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]}
Gunakan tipe kompleks dalam ekspresi
Anda dapat menggunakan tipe kompleks dalam ekspresi, seperti ekspresi filter dan ekspresi kondisi, dengan menggunakan operator dereferencing untuk menavigasi struktur tipe kompleks. Untuk objek dan peta, gunakan . (dot)
dan untuk elemen daftar gunakan [n]
(tanda kurung siku di sekitar nomor urut elemen). Anda tidak dapat merujuk ke elemen individual dari satu set, tetapi Anda dapat menggunakan contains
fungsi tersebut.
Contoh berikut menunjukkan dua ekspresi filter yang digunakan dalam operasi scan. Ekspresi filter menentukan kondisi kecocokan untuk item yang Anda inginkan dalam hasil. Contoh menggunakanPerson
,Address
, dan PhoneNumber
kelas yang ditampilkan sebelumnya.
public void scanUsingFilterOfNestedAttr() { // The following is a filter expression for an attribute that is a map of Address objects. // By using this filter expression, the SDK returns Person objects that have an address // with 'mailing' as a key and 'MS2' for a state value. Expression addressFilter = Expression.builder() .expression("addresses.#type.#field = :value") .putExpressionName("#type", "mailing") .putExpressionName("#field", "state") .putExpressionValue(":value", AttributeValue.builder().s("MS2").build()) .build(); PageIterable<Person> addressFilterResults = personDynamoDbTable.scan(rb -> rb. filterExpression(addressFilter)); addressFilterResults.items().stream().forEach(p -> logger.info("Person: {}", p)); assert addressFilterResults.items().stream().count() == 1; // The following is a filter expression for an attribute that is a list of phone numbers. // By using this filter expression, the SDK returns Person objects whose second phone number // in the list has a type equal to 'cell'. Expression phoneFilter = Expression.builder() .expression("phoneNumbers[1].#type = :type") .putExpressionName("#type", "type") .putExpressionValue(":type", AttributeValue.builder().s("cell").build()) .build(); PageIterable<Person> phoneFilterResults = personDynamoDbTable.scan(rb -> rb .filterExpression(phoneFilter) .attributesToProject("id", "firstName", "lastName", "phoneNumbers") ); phoneFilterResults.items().stream().forEach(p -> logger.info("Person: {}", p)); assert phoneFilterResults.items().stream().count() == 1; assert phoneFilterResults.items().stream().findFirst().get().getPhoneNumbers().get(1).getType().equals("cell"); }
public static void populateDatabase() { Person person1 = new Person(); person1.setId(1); person1.setFirstName("FirstName1"); person1.setLastName("LastName1"); Address billingAddr1 = new Address(); billingAddr1.setState("BS1"); billingAddr1.setCity("BillingTown1"); Address mailing1 = new Address(); mailing1.setState("MS1"); mailing1.setCity("MailingTown1"); person1.setAddresses(Map.of("billing", billingAddr1, "mailing", mailing1)); PhoneNumber pn1_1 = new PhoneNumber(); pn1_1.setType("work"); pn1_1.setNumber("111-111-1111"); PhoneNumber pn1_2 = new PhoneNumber(); pn1_2.setType("home"); pn1_2.setNumber("222-222-2222"); List<PhoneNumber> phoneNumbers1 = List.of(pn1_1, pn1_2); person1.setPhoneNumbers(phoneNumbers1); personDynamoDbTable.putItem(person1); Person person2 = person1; person2.setId(2); person2.setFirstName("FirstName2"); person2.setLastName("LastName2"); Address billingAddress2 = billingAddr1; billingAddress2.setCity("BillingTown2"); billingAddress2.setState("BS2"); Address mailing2 = mailing1; mailing2.setCity("MailingTown2"); mailing2.setState("MS2"); person2.setAddresses(Map.of("billing", billingAddress2, "mailing", mailing2)); PhoneNumber pn2_1 = new PhoneNumber(); pn2_1.setType("work"); pn2_1.setNumber("333-333-3333"); PhoneNumber pn2_2 = new PhoneNumber(); pn2_2.setType("cell"); pn2_2.setNumber("444-444-4444"); List<PhoneNumber> phoneNumbers2 = List.of(pn2_1, pn2_2); person2.setPhoneNumbers(phoneNumbers2); personDynamoDbTable.putItem(person2); }
{ "id": 1, "addresses": { "billing": { "city": "BillingTown1", "state": "BS1", "street": null, "zipCode": null }, "mailing": { "city": "MailingTown1", "state": "MS1", "street": null, "zipCode": null } }, "firstName": "FirstName1", "lastName": "LastName1", "phoneNumbers": [ { "number": "111-111-1111", "type": "work" }, { "number": "222-222-2222", "type": "home" } ] } { "id": 2, "addresses": { "billing": { "city": "BillingTown2", "state": "BS2", "street": null, "zipCode": null }, "mailing": { "city": "MailingTown2", "state": "MS2", "street": null, "zipCode": null } }, "firstName": "FirstName2", "lastName": "LastName2", "phoneNumbers": [ { "number": "333-333-3333", "type": "work" }, { "number": "444-444-4444", "type": "cell" } ] }
Perbarui item yang berisi tipe kompleks
Untuk memperbarui item yang berisi tipe kompleks, Anda memiliki dua pendekatan dasar:
-
Pendekatan 1: Pertama ambil item (dengan menggunakan
getItem
), perbarui objek, lalu panggilDynamoDbTable#updateItem
. -
Pendekatan 2: Jangan mengambil item, tetapi buat instance baru, atur properti yang ingin Anda perbarui, dan kirimkan instance
DynamoDbTable#updateItem
dengan menyetel nilai yang sesuai dari.IgnoreNullsMode
Pendekatan ini tidak mengharuskan Anda mengambil item sebelum memperbaruinya.
Contoh yang ditunjukkan di bagian ini menggunakanPerson
,Address
, dan PhoneNumber
kelas yang ditunjukkan sebelumnya.
Perbarui pendekatan 1: ambil, lalu perbarui
Dengan menggunakan pendekatan ini, Anda memastikan bahwa tidak ada data yang hilang pada pembaruan. DynamoDB Enhanced API Client membuat ulang kacang dengan atribut dari item yang disimpan di DynamoDB termasuk nilai tipe kompleks. Anda kemudian perlu menggunakan getter dan setter untuk memperbarui kacang. Kelemahan dari pendekatan ini adalah biaya yang Anda keluarkan untuk mengambil item terlebih dahulu.
Contoh berikut menunjukkan bahwa tidak ada data yang hilang jika Anda pertama kali mengambil item sebelum memperbaruinya.
public void retrieveThenUpdateExample() { // Assume that we ran this code yesterday. Person person = new Person(); person.setId(1); person.setFirstName("FirstName"); person.setLastName("LastName"); Address mainAddress = new Address(); mainAddress.setStreet("123 MyStreet"); mainAddress.setCity("MyCity"); mainAddress.setState("MyState"); mainAddress.setZipCode("MyZipCode"); person.setMainAddress(mainAddress); PhoneNumber homePhone = new PhoneNumber(); homePhone.setNumber("1111111"); homePhone.setType("HOME"); person.setPhoneNumbers(List.of(homePhone)); personDynamoDbTable.putItem(person); // Assume that we are running this code now. // First, retrieve the item Person retrievedPerson = personDynamoDbTable.getItem(Key.builder().partitionValue(1).build()); // Make any updates. retrievedPerson.getMainAddress().setCity("YourCity"); // Save the updated bean. 'updateItem' returns the bean as it appears after the update. Person updatedPerson = personDynamoDbTable.updateItem(retrievedPerson); // Verify for this example. Address updatedMainAddress = updatedPerson.getMainAddress(); assert updatedMainAddress.getCity().equals("YourCity"); assert updatedMainAddress.getState().equals("MyState"); // Unchanged. // The list of phone numbers remains; it was not set to null; assert updatedPerson.getPhoneNumbers().size() == 1; }
Pendekatan pembaruan 2: Gunakan IgnoreNullsMode
enum tanpa mengambil item terlebih dahulu
Untuk memperbarui item di DynamoDB, Anda dapat memberikan objek baru yang hanya memiliki properti yang ingin diperbarui dan membiarkan nilai lainnya sebagai null. Dengan pendekatan ini, Anda perlu menyadari bagaimana nilai nol dalam objek diperlakukan oleh SDK dan bagaimana Anda dapat mengontrol perilaku.
Untuk menentukan properti bernilai nol mana yang ingin Anda abaikan, berikan IgnoreNullsMode
enum saat Anda membuat. SDK UpdateItemEnhancedRequest
IgnoreNullsMode.SCALAR_ONLY
// Create a new Person object to update the existing item in DynamoDB. Person personForUpdate = new Person(); personForUpdate.setId(1); personForUpdate.setFirstName("updatedFirstName"); // 'firstName' is a top scalar property. Address addressForUpdate = new Address(); addressForUpdate.setCity("updatedCity"); personForUpdate.setMainAddress(addressForUpdate); personDynamoDbTable.updateItem(r -> r .item(personForUpdate) .ignoreNullsMode(
IgnoreNullsMode.SCALAR_ONLY
) .build()); /* With IgnoreNullsMode.SCALAR_ONLY provided, The SDK ignores all null properties. The SDK adds or replaces the 'firstName' property with the provided value, "updatedFirstName". The SDK updates the 'city' value of 'mainAddress', as long as the 'mainAddress' attribute already exists in DynamoDB. In the background, the SDK generates an update expression that it sends in the request to DynamoDB. The following JSON object is a simplified version of what it sends. Notice that the SDK includes the paths to 'mainAddress.city' and 'firstName' in the SET clause of the update expression. No null values in 'personForUpdate' are included. { "TableName": "PersonTable", "Key": { "id": { "N": "1" } }, "ReturnValues": "ALL_NEW", "UpdateExpression": "SET #mainAddress.#city = :mainAddress_city, #firstName = :firstName", "ExpressionAttributeNames": { "#city": "city", "#firstName": "firstName", "#mainAddress": "mainAddress" }, "ExpressionAttributeValues": { ":firstName": { "S": "updatedFirstName" }, ":mainAddress_city": { "S": "updatedCity" } } } Had we chosen 'IgnoreNullsMode.DEFAULT' instead of 'IgnoreNullsMode.SCALAR_ONLY', the SDK would have included null values in the "ExpressionAttributeValues" section of the request as shown in the following snippet. "ExpressionAttributeValues": { ":mainAddress": { "M": { "zipCode": { "NULL": true }, "city": { "S": "updatedCity" }, "street": { "NULL": true }, "state": { "NULL": true } } }, ":firstName": { "S": "updatedFirstName" } } */
Panduan Pengembang Amazon DynamoDB berisi informasi lebih lanjut tentang ekspresi pembaruan.
Deskripsi opsi IgnoreNullsMode
-
IgnoreNullsMode.SCALAR_ONLY
- Gunakan pengaturan ini untuk memperbarui atribut skalar di tingkat manapun. SDKMembangun pernyataan pembaruan yang hanya mengirimkan atribut skalar non-null ke DynamoDB. SDKMengabaikan atribut skalar bernilai nol dari kacang atau peta, mempertahankan nilai yang disimpan di DynamoDB.Saat Anda memperbarui atribut skalar map atau bean, peta harus sudah ada di DynamoDB. Jika Anda menambahkan peta atau kacang ke objek yang belum ada untuk objek di DynamoDB, Anda mendapatkan dengan pesan Jalur dokumen
DynamoDbException
yang disediakan dalam ekspresi pembaruan tidak valid untuk pembaruan. Anda harus menggunakanMAPS_ONLY
mode untuk menambahkan kacang atau peta ke DynamoDB sebelum Anda memperbarui salah satu atributnya. -
IgnoreNullsMode.MAPS_ONLY
- Gunakan pengaturan ini untuk menambah atau mengganti properti yang merupakan kacang atau peta. SDKMengganti atau menambahkan peta atau kacang apa pun yang disediakan dalam objek. Setiap kacang atau peta yang null dalam objek diabaikan, mempertahankan peta yang ada di DynamoDB. -
IgnoreNullsMode.DEFAULT
- Dengan pengaturan ini, SDK tidak pernah mengabaikan nilai null. Atribut skalar pada tingkat manapun yang null diperbarui ke null. SDKPembaruan setiap kacang bernilai nol, peta, daftar, atau properti set dalam objek ke null di DynamoDB. Saat Anda menggunakan mode ini—atau tidak menyediakan mode karena ini adalah mode default—Anda harus mengambil item terlebih dahulu sehingga nilai di DynamoDB tidak disetel ke null yang disediakan dalam objek untuk diperbarui, kecuali niat Anda adalah untuk mengatur nilai ke null.
Dalam semua mode, jika Anda memberikan objek updateItem
yang memiliki daftar atau set non-null, daftar atau set disimpan ke DynamoDB.
Mengapa modenya?
Saat Anda memberikan objek dengan kacang atau peta ke updateItem
metode, tidak SDK dapat mengetahui apakah itu harus menggunakan nilai properti dalam kacang (atau nilai entri di peta) untuk memperbarui item, atau apakah seluruh kacang/peta harus menggantikan apa yang telah disimpan ke DynamoDB.
Bekerja dari contoh sebelumnya yang menunjukkan pengambilan item terlebih dahulu, mari kita coba memperbarui city
atribut mainAddress
tanpa pengambilan.
/* The retrieval example saved the Person object with a 'mainAddress' property whose 'city' property value is "MyCity". /* Note that we create a new Person with only the necessary information to update the city value of the mainAddress. */ Person personForUpdate = new Person(); personForUpdate.setId(1); // The update we want to make changes the city. Address mainAddressForUpdate = new Address(); mainAddressForUpdate.setCity("YourCity"); personForUpdate.setMainAddress(mainAddressForUpdate); // Lets' try the following: Person updatedPerson = personDynamoDbTable.updateItem(personForUpdate); /* Since we haven't retrieved the item, we don't know if the 'mainAddress' property already exists, so what update expression should the SDK generate? A) Should it replace or add the 'mainAddress' with the provided object (setting all attributes to null other than city) as shown in the following simplified JSON? { "TableName": "PersonTable", "Key": { "id": { "N": "1" } }, "ReturnValues": "ALL_NEW", "UpdateExpression": "SET #mainAddress = :mainAddress", "ExpressionAttributeNames": { "#mainAddress": "mainAddress" }, "ExpressionAttributeValues": { ":mainAddress": { "M": { "zipCode": { "NULL": true }, "city": { "S": "YourCity" }, "street": { "NULL": true }, "state": { "NULL": true } } } } } B) Or should it update only the 'city' attribute of an existing 'mainAddress' as shown in the following simplified JSON? { "TableName": "PersonTable", "Key": { "id": { "N": "1" } }, "ReturnValues": "ALL_NEW", "UpdateExpression": "SET #mainAddress.#city = :mainAddress_city", "ExpressionAttributeNames": { "#city": "city", "#mainAddress": "mainAddress" }, "ExpressionAttributeValues": { ":mainAddress_city": { "S": "YourCity" } } } However, assume that we don't know if the 'mainAddress' already exists. If it doesn't exist, the SDK would try to update an attribute of a non-existent map, which results in an exception. In this particular case, we would likely select option B (SCALAR_ONLY) to retain the other values of the 'mainAddress'. */
Dua contoh berikut menunjukkan penggunaan nilai MAPS_ONLY
dan SCALAR_ONLY
enumerasi. MAPS_ONLY
menambahkan peta dan SCALAR_ONLY
memperbarui peta.
public void mapsOnlyModeExample() { // Assume that we ran this code yesterday. Person person = new Person(); person.setId(1); person.setFirstName("FirstName"); personDynamoDbTable.putItem(person); // Assume that we are running this code now. /* Note that we create a new Person with only the necessary information to update the city value of the mainAddress. */ Person personForUpdate = new Person(); personForUpdate.setId(1); // The update we want to make changes the city. Address mainAddressForUpdate = new Address(); mainAddressForUpdate.setCity("YourCity"); personForUpdate.setMainAddress(mainAddressForUpdate); Person updatedPerson = personDynamoDbTable.updateItem(r -> r .item(personForUpdate) .ignoreNullsMode(IgnoreNullsMode.MAPS_ONLY)); // Since the mainAddress property does not exist, use MAPS_ONLY mode. assert updatedPerson.getMainAddress().getCity().equals("YourCity"); assert updatedPerson.getMainAddress().getState() == null; }
public void scalarOnlyExample() { // Assume that we ran this code yesterday. Person person = new Person(); person.setId(1); Address mainAddress = new Address(); mainAddress.setCity("MyCity"); mainAddress.setState("MyState"); person.setMainAddress(mainAddress); personDynamoDbTable.putItem(person); // Assume that we are running this code now. /* Note that we create a new Person with only the necessary information to update the city value of the mainAddress. */ Person personForUpdate = new Person(); personForUpdate.setId(1); // The update we want to make changes the city. Address mainAddressForUpdate = new Address(); mainAddressForUpdate.setCity("YourCity"); personForUpdate.setMainAddress(mainAddressForUpdate); Person updatedPerson = personDynamoDbTable.updateItem(r -> r .item(personForUpdate) .ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY)); // SCALAR_ONLY mode ignores null properties in the in mainAddress. assert updatedPerson.getMainAddress().getCity().equals("YourCity"); assert updatedPerson.getMainAddress().getState().equals("MyState"); // The state property remains the same. }
Lihat tabel berikut untuk melihat nilai null mana yang diabaikan untuk setiap mode. Anda sering dapat bekerja dengan salah satu SCALAR_ONLY
dan MAPS_ONLY
kecuali ketika Anda bekerja dengan kacang atau peta.
Jenis properti | dalam ONLY mode SCALAR _ | dalam ONLY mode MAPS _ | dalam DEFAULT mode |
---|---|---|---|
Skalar teratas | Ya | Ya | Tidak |
Kacang atau peta | Ya | Ya | Tidak |
Nilai skalar entri kacang atau peta | Ya1 | Tidak 2 | Tidak |
Daftar atau set | Ya | Ya | Tidak |
1 Ini mengasumsikan peta sudah ada di DynamoDB. Nilai skalar apa pun—null atau bukan null—dari kacang atau peta yang Anda berikan di objek untuk pembaruan mengharuskan jalur ke nilai ada di DynamoDB. SDKMembangun jalur ke atribut dengan menggunakan operator . (dot)
dereference sebelum mengirimkan permintaan.
2 Karena Anda menggunakan MAPS_ONLY
mode untuk sepenuhnya mengganti atau menambahkan kacang atau peta, semua nilai nol dalam kacang atau peta dipertahankan di peta yang disimpan ke DynamoDB.