Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.
Arbeiten Sie mit Attributen wie Beans, Maps, Listen und Sets
Eine Bean-Definition, wie die unten gezeigte Person
Klasse, könnte Eigenschaften (oder Attribute) definieren, die sich auf Typen mit zusätzlichen Attributen beziehen. In der Person
Klasse mainAddress
handelt es sich beispielsweise um eine Eigenschaft, die sich auf ein Address
Bean bezieht, das zusätzliche Wertattribute definiert. addresses
bezieht sich auf eine Java-Map, deren Elemente sich auf Address
Beans beziehen. Diese komplexen Typen können als Container mit einfachen Attributen betrachtet werden, die Sie für ihren Datenwert im Kontext von DynamoDB verwenden.
DynamoDB bezeichnet die Werteigenschaften verschachtelter Elemente wie Maps, Listen oder Beans als verschachtelte Attribute. Im Amazon DynamoDB Developer Guide wird die gespeicherte Form einer Java-Map, -Liste oder -Bean als Dokumenttyp bezeichnet. Einfache Attribute, die Sie in Java für ihren Datenwert verwenden, werden in DynamoDB als skalare Typen bezeichnet. Mengen, die mehrere skalare Elemente desselben Typs enthalten und als Mengentypen bezeichnet werden.
Es ist wichtig zu wissen, dass der DynamoDB Enhanced Client eine Eigenschaft, die Bean ist, beim Speichern in einen DynamoDB-Kartendokumenttyp API konvertiert.
@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 + '\'' + '}'; } }
Speichern Sie komplexe Typen
Verwenden Sie annotierte Datenklassen
Sie speichern verschachtelte Attribute für benutzerdefinierte Klassen, indem Sie sie einfach mit Anmerkungen versehen. Die Address
Klasse und die PhoneNumber
Klasse, die zuvor gezeigt wurden, sind nur mit der Anmerkung annotiert. @DynamoDbBean
Wenn der DynamoDB Enhanced Client das Tabellenschema für die Person
Klasse mit dem folgenden Codeausschnitt API erstellt, API erkennt er die Verwendung der PhoneNumber
Klassen Address
und und erstellt die entsprechenden Mappings für die Arbeit mit DynamoDB.
TableSchema<Person> personTableSchema = TableSchema.fromBean(Person.class);
Verwenden Sie abstrakte Schemas mit Buildern
Der alternative Ansatz besteht darin, Schema-Builder für statische Tabellen für jede verschachtelte Bean-Klasse zu verwenden, wie im folgenden Code gezeigt.
Die Tabellenschemas für die PhoneNumber
Klassen Address
und sind abstrakt in dem Sinne, dass sie nicht mit einer DynamoDB-Tabelle verwendet werden können. Das liegt daran, dass ihnen Definitionen für den Primärschlüssel fehlen. Sie werden jedoch als verschachtelte Schemas im Tabellenschema für die Person
Klasse verwendet.
Nach den Kommentarzeilen 1 und 2 in der Definition von sehen Sie den CodePERSON_TABLE_SCHEMA
, der die abstrakten Tabellenschemas verwendet. Die Verwendung von documentOf
in der EnhanceType.documentOf(...)
Methode bedeutet nicht, dass die Methode einen EnhancedDocument
Typ des erweiterten Dokuments API zurückgibt. Die documentOf(...)
Methode gibt in diesem Kontext ein Objekt zurück, das weiß, wie es sein Klassenargument mithilfe des Tabellenschema-Arguments DynamoDB-Tabellenattributen zuordnen kann.
// 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();
Projektattribute komplexer Typen
Für query()
und scan()
Methoden können Sie mithilfe von Methodenaufrufen wie addNestedAttributeToProject()
und angeben, welche Attribute in den Ergebnissen zurückgegeben werden sollenattributesToProject()
. Der DynamoDB Enhanced Client API konvertiert die Parameter des Java-Methodenaufrufs in Projektionsausdrücke, bevor die Anforderung gesendet wird.
Das folgende Beispiel füllt die Person
Tabelle mit zwei Elementen und führt dann drei Scanvorgänge durch.
Beim ersten Scan werden alle Elemente in der Tabelle abgerufen, um die Ergebnisse mit den anderen Scanvorgängen zu vergleichen.
Der zweite Scan verwendet die addNestedAttributeToProject()
street
Attributwert zurückzugeben.
Der dritte Scanvorgang verwendet die attributesToProject()
hobbies
. Der Attributtyp von hobbies
ist eine Liste. Um auf einzelne Listenelemente zuzugreifen, führen Sie einen get()
Vorgang in der Liste aus.
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
Anmerkung
Wenn die attributesToProject()
Methode einer anderen Builder-Methode folgt, die Attribute hinzufügt, die Sie projizieren möchten, attributesToProject()
ersetzt die mitgelieferte Liste der Attributnamen alle anderen Attributnamen.
Ein Scan, der mit der ScanEnhancedRequest
Instanz im folgenden Codeausschnitt durchgeführt wird, gibt nur Hobbydaten zurück.
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]}
Der folgende Codeausschnitt verwendet die Methode zuerst. attributesToProject()
Bei dieser Reihenfolge werden alle anderen angeforderten Attribute beibehalten.
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]}
Verwenden Sie komplexe Typen in Ausdrücken
Sie können komplexe Typen in Ausdrücken wie Filterausdrücken und Bedingungsausdrücken verwenden, indem Sie Dereferenzierungsoperatoren verwenden, um in der Struktur des komplexen Typs zu navigieren. Verwenden Sie für Objekte und Maps das . (dot)
und für Listenelemente use [n]
(eckige Klammern um die Sequenznummer des Elements). Sie können nicht auf einzelne Elemente einer Menge verweisen, aber Sie können die contains
Funktion verwenden.
Das folgende Beispiel zeigt zwei Filterausdrücke, die bei Scanvorgängen verwendet werden. Die Filterausdrücke geben die Übereinstimmungsbedingungen für Elemente an, die Sie in den Ergebnissen haben möchten. In dem Beispiel werden Person
die zuvor gezeigten PhoneNumber
KlassenAddress
, und verwendet.
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" } ] }
Aktualisieren Sie Elemente, die komplexe Typen enthalten
Um ein Element zu aktualisieren, das komplexe Typen enthält, haben Sie zwei grundlegende Methoden:
-
Ansatz 1: Rufen Sie zuerst das Element ab (mithilfe von
getItem
), aktualisieren Sie das Objekt und rufen Sie es dann aufDynamoDbTable#updateItem
. -
Ansatz 2: Rufen Sie das Element nicht ab, sondern erstellen Sie eine neue Instanz, legen Sie die Eigenschaften fest, die Sie aktualisieren möchten, und leiten Sie die Instanz weiter,
DynamoDbTable#updateItem
indem Sie den entsprechenden Wert von festlegenIgnoreNullsMode
. Bei diesem Ansatz müssen Sie das Element nicht abrufen, bevor Sie es aktualisieren.
Die in diesem Abschnitt gezeigten Beispiele verwenden die zuvor aufgeführten PhoneNumber
Klassen Person
Address
, und.
Aktualisierungsansatz 1: Abrufen, dann aktualisieren
Mit diesem Ansatz stellen Sie sicher, dass beim Update keine Daten verloren gehen. Der DynamoDB Enhanced Client API erstellt die Bean mit den Attributen des in DynamoDB gespeicherten Elements neu, einschließlich Werten komplexer Typen. Anschließend müssen Sie die Getter und Setter verwenden, um die Bean zu aktualisieren. Der Nachteil dieses Ansatzes sind die Kosten, die Ihnen entstehen, wenn Sie den Artikel zuerst abrufen.
Das folgende Beispiel zeigt, dass keine Daten verloren gehen, wenn Sie das Element zuerst abrufen, bevor Sie es aktualisieren.
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; }
Aktualisierungsansatz 2: Verwenden Sie eine IgnoreNullsMode
Aufzählung, ohne das Element zuerst abzurufen
Um ein Element in DynamoDB zu aktualisieren, können Sie ein neues Objekt angeben, das nur die Eigenschaften hat, die Sie aktualisieren möchten, und die anderen Werte auf Null belassen. Bei diesem Ansatz müssen Sie wissen, wie Nullwerte im Objekt von der behandelt werden SDK und wie Sie das Verhalten steuern können.
Um anzugeben, welche Eigenschaften mit Nullwerten Sie ignorieren SDK möchten, geben Sie beim Erstellen von eine IgnoreNullsMode
Aufzählung an. 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" } } */
Das Amazon DynamoDB Developer Guide enthält weitere Informationen zu Aktualisierungsausdrücken.
Beschreibungen der Optionen IgnoreNullsMode
-
IgnoreNullsMode.SCALAR_ONLY
- Verwenden Sie diese Einstellung, um skalare Attribute auf einer beliebigen Ebene zu aktualisieren. Der SDK konstruiert eine Aktualisierungsanweisung, die nur skalare Attribute ungleich Null an DynamoDB sendet. Das SDK ignoriert nullwertige, skalare Attribute einer Bean oder Map und behält den gespeicherten Wert in DynamoDB bei.Wenn Sie ein skalares Attribut von map oder bean aktualisieren, muss die Map bereits in DynamoDB vorhanden sein. Wenn Sie dem Objekt eine Map oder eine Bean hinzufügen, die noch nicht für das Objekt in DynamoDB vorhanden ist, erhalten Sie eine
DynamoDbException
mit der Meldung Der im Aktualisierungsausdruck angegebene Dokumentpfad ist für die Aktualisierung ungültig. Sie müssenMAPS_ONLY
den Modus verwenden, um DynamoDB ein Bean oder eine Map hinzuzufügen, bevor Sie eines ihrer Attribute aktualisieren. -
IgnoreNullsMode.MAPS_ONLY
- Verwenden Sie diese Einstellung, um Eigenschaften hinzuzufügen oder zu ersetzen, bei denen es sich um eine Bean oder Map handelt. Dadurch werden alle im Objekt bereitgestellten Maps oder Beans SDK ersetzt oder hinzugefügt. Alle Beans oder Maps, die im Objekt Null sind, werden ignoriert, wobei die in DynamoDB vorhandene Map beibehalten wird. -
IgnoreNullsMode.DEFAULT
- Bei dieser Einstellung ignoriert der SDK niemals Nullwerte. Skalare Attribute auf jeder Ebene, die Null sind, werden auf Null aktualisiert. Dadurch werden alle Bean-, Map-, List- oder Set-Eigenschaften mit Nullwert im Objekt in DynamoDB auf Null SDK aktualisiert. Wenn Sie diesen Modus verwenden — oder keinen Modus angeben, da es der Standardmodus ist — sollten Sie das Element zuerst abrufen, damit Werte in DynamoDB nicht auf Null gesetzt werden, die im Objekt zur Aktualisierung bereitgestellt werden, es sei denn, Sie beabsichtigen, die Werte auf Null zu setzen.
Wenn Sie in allen Modi ein Objekt mit einer Liste oder einem Satz angebenupdateItem
, der nicht Null ist, wird die Liste oder der Satz in DynamoDB gespeichert.
Warum die Modi?
Wenn Sie ein Objekt mit einem Bean oder einer Map für die updateItem
Methode angeben, SDK können sie nicht sagen, ob es die Eigenschaftswerte in der Bean (oder die Eingabewerte in der Map) verwenden soll, um das Element zu aktualisieren, oder ob die gesamte Bean/Map das ersetzen soll, was in DynamoDB gespeichert wurde.
Ausgehend von unserem vorherigen Beispiel, das zuerst den Abruf des Elements zeigt, versuchen wir, das city
Attribut von ohne den Abruf zu aktualisieren. mainAddress
/* 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'. */
Die folgenden beiden Beispiele zeigen die Verwendung von Werten MAPS_ONLY
und SCALAR_ONLY
Aufzählungswerten. MAPS_ONLY
fügt eine Map hinzu und SCALAR_ONLY
aktualisiert eine Map.
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. }
In der folgenden Tabelle können Sie nachlesen, welche Nullwerte für jeden Modus ignoriert werden. Sie können oft mit beiden SCALAR_ONLY
arbeiten, MAPS_ONLY
außer wenn Sie mit Beans oder Maps arbeiten.
Art der Immobilie | im ONLY Modus SCALAR _ | im ONLY Modus MAPS _ | im DEFAULT Modus |
---|---|---|---|
Oberster Skalar | Ja | Ja | Nein |
Bean oder Karte | Ja | Ja | Nein |
Skalarwert eines Bean- oder Map-Eintrags | Ja1 | Nein 2 | Nein |
Liste oder Satz | Ja | Ja | Nein |
1 Dies setzt voraus, dass die Map bereits in DynamoDB vorhanden ist. Jeder Skalarwert — Null oder nicht Null — der Bean oder Map, die Sie im Objekt für die Aktualisierung angeben, erfordert, dass ein Pfad zu dem Wert in DynamoDB existiert. Der SDK erstellt mithilfe des Dereferenzierungsoperators einen Pfad zu dem Attribut, bevor er die Anforderung sendet. . (dot)
2 Da Sie den MAPS_ONLY
Modus verwenden, um eine Bean oder Map vollständig zu ersetzen oder hinzuzufügen, werden alle Nullwerte in der Bean oder Map in der in DynamoDB gespeicherten Map beibehalten.