Lavora con attributi che sono bean, mappe, elenchi e set - AWS SDK for Java 2.x

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

Lavora con attributi che sono bean, mappe, elenchi e set

Una definizione di bean, come la Person classe mostrata di seguito, potrebbe definire proprietà (o attributi) che si riferiscono a tipi con attributi aggiuntivi. Ad esempio, nella Person classe, mainAddress c'è una proprietà che si riferisce a un Address bean che definisce attributi di valore aggiuntivi. addressessi riferisce a una mappa Java, i cui elementi si riferiscono ai Address bean. Questi tipi complessi possono essere considerati contenitori di attributi semplici da utilizzare per il valore dei dati nel contesto di DynamoDB.

DynamoDB si riferisce alle proprietà di valore degli elementi nidificati, come mappe, elenchi o bean, come attributi nidificati. La Amazon DynamoDB Developer Guide si riferisce al formato salvato di una mappa, un elenco o un bean Java come tipo di documento. Gli attributi semplici utilizzati per il loro valore di dati in Java sono denominati tipi scalari in DynamoDB. Set, che contengono più elementi scalari dello stesso tipo e vengono definiti tipi di set.

È importante sapere che il DynamoDB Enhanced API Client converte una proprietà che è bean in un tipo di documento di mappa DynamoDB quando viene salvata.

@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 + '\'' + '}'; } }

Salva tipi complessi

Usa classi di dati annotate

Puoi salvare gli attributi annidati per le classi personalizzate semplicemente annotandoli. La Address classe e la PhoneNumber classe mostrate in precedenza vengono annotate solo con l'annotazione. @DynamoDbBean Quando il DynamoDB Enhanced API Client crea lo schema della tabella per la classe con Person il seguente frammento, scopre l'uso delle Address classi PhoneNumber and e crea API le mappature corrispondenti per funzionare con DynamoDB.

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

Usa schemi astratti con i builder

L'approccio alternativo consiste nell'utilizzare generatori di schemi di tabelle statiche per ogni classe bean annidata, come mostrato nel codice seguente.

Gli schemi di tabella per le PhoneNumber classi Address and sono astratti, nel senso che non possono essere utilizzati con una tabella DynamoDB. Questo perché mancano di definizioni per la chiave primaria. Vengono tuttavia utilizzati come schemi annidati nello schema tabellare della classe. Person

Dopo le righe di commento 1 e 2 nella definizione diPERSON_TABLE_SCHEMA, viene visualizzato il codice che utilizza gli schemi di tabella astratti. L'uso di documentOf nel EnhanceType.documentOf(...) metodo non indica che il metodo restituisca un EnhancedDocument tipo di documento API avanzato. Il documentOf(...) metodo in questo contesto restituisce un oggetto che sa come mappare l'argomento della classe da e verso gli attributi della tabella DynamoDB utilizzando l'argomento dello schema della tabella.

// 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();

Attributi di progetto di tipi complessi

Per query() i scan() metodi, è possibile specificare quali attributi si desidera vengano restituiti nei risultati utilizzando chiamate di metodo come addNestedAttributeToProject() eattributesToProject(). Il DynamoDB Enhanced API Client converte i parametri di chiamata al metodo Java in espressioni di proiezione prima dell'invio della richiesta.

L'esempio seguente popola la Person tabella con due elementi, quindi esegue tre operazioni di scansione.

La prima scansione accede a tutti gli elementi della tabella per confrontare i risultati con le altre operazioni di scansione.

La seconda scansione utilizza il metodo addNestedAttributeToProject()builder per restituire solo il street valore dell'attributo.

La terza operazione di scansione utilizza il metodo attributesToProject()builder per restituire i dati per l'attributo di primo livello,. hobbies Il tipo di attributo di hobbies è un elenco. Per accedere a singoli elementi dell'elenco, eseguire un'get()operazione sull'elenco.

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
Nota

Se il attributesToProject() metodo segue qualsiasi altro metodo di creazione che aggiunge gli attributi che desiderate proiettare, l'elenco dei nomi degli attributi fornito attributesToProject() sostituisce tutti gli altri nomi di attributo.

Una scansione eseguita con l'ScanEnhancedRequestistanza nel frammento seguente restituisce solo dati relativi agli hobby.

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]}

Il seguente frammento di codice utilizza innanzitutto il metodo. attributesToProject() Questo ordinamento conserva tutti gli altri attributi richiesti.

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]}

Usa tipi complessi nelle espressioni

È possibile utilizzare tipi complessi nelle espressioni, ad esempio espressioni di filtro ed espressioni di condizione, utilizzando gli operatori di dereferenziamento per navigare nella struttura del tipo complesso. Per oggetti e mappe, usa . (dot) e per gli elementi dell'elenco [n] (parentesi quadre attorno al numero di sequenza dell'elemento). Non puoi fare riferimento ai singoli elementi di un set, ma puoi usare la containsfunzione.

L'esempio seguente mostra due espressioni di filtro utilizzate nelle operazioni di scansione. Le espressioni di filtro specificano le condizioni di corrispondenza per gli elementi da inserire nei risultati. L'esempio utilizza e Person Address le PhoneNumber classi mostrate in precedenza.

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" } ] }

Aggiorna gli elementi che contengono tipi complessi

Per aggiornare un elemento che contiene tipi complessi, sono disponibili due approcci di base:

  • Approccio 1: prima recupera l'elemento (utilizzandogetItem), aggiorna l'oggetto, quindi chiamaDynamoDbTable#updateItem.

  • Approccio 2: non recuperate l'elemento, ma create una nuova istanza, impostate le proprietà che desiderate aggiornare e inviate l'istanza DynamoDbTable#updateItem impostando il valore appropriato di. IgnoreNullsMode Questo approccio non richiede il recupero dell'elemento prima di aggiornarlo.

Gli esempi mostrati in questa sezione utilizzano le PhoneNumber classi PersonAddress, e mostrate in precedenza.

Approccio di aggiornamento 1: recupera, quindi aggiorna

Utilizzando questo approccio, vi assicurate che nessun dato vada perso durante l'aggiornamento. Il DynamoDB Enhanced API Client ricrea il bean con gli attributi dell'elemento salvato in DynamoDB, inclusi i valori di tipi complessi. È quindi necessario utilizzare i getter e i setter per aggiornare il bean. Lo svantaggio di questo approccio è il costo che si deve sostenere per recuperare prima l'articolo.

L'esempio seguente dimostra che non si perde alcun dato se si recupera l'elemento prima di aggiornarlo.

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; }

Approccio di aggiornamento 2: utilizzare un IgnoreNullsMode enum senza prima recuperare l'elemento

Per aggiornare un elemento in DynamoDB, puoi fornire un nuovo oggetto con solo le proprietà che desideri aggiornare e lasciare gli altri valori come nulli. Con questo approccio, è necessario essere consapevoli di come i valori nulli nell'oggetto vengono trattati da SDK e di come è possibile controllarne il comportamento.

Per specificare quali proprietà con valori nulli vuoi che vengano ignorate, fornisci un IgnoreNullsMode enum quando crei ilSDK. UpdateItemEnhancedRequest Come esempio di utilizzo di uno dei valori enumerati, il frammento seguente utilizza la modalità. 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" } } */

L'Amazon DynamoDB Developer Guide contiene ulteriori informazioni sulle espressioni di aggiornamento.

Descrizioni delle opzioni IgnoreNullsMode

  • IgnoreNullsMode.SCALAR_ONLY- Utilizzate questa impostazione per aggiornare gli attributi scalari a qualsiasi livello. SDKCostruisce un'istruzione di aggiornamento che invia solo attributi scalari non nulli a DynamoDB. SDKIgnora gli attributi scalari con valori nulli di un bean o di una mappa, mantenendo il valore salvato in DynamoDB.

    Quando si aggiorna un attributo scalare di map o bean, la mappa deve già esistere in DynamoDB. Se aggiungi una mappa o un bean all'oggetto che non esiste già per l'oggetto in DynamoDB, ricevi DynamoDbException un messaggio con il messaggio Il percorso del documento fornito nell'espressione di aggiornamento non è valido per l'aggiornamento. È necessario utilizzare MAPS_ONLY la modalità per aggiungere un bean o una map a DynamoDB prima di aggiornare uno qualsiasi dei suoi attributi.

  • IgnoreNullsMode.MAPS_ONLY- Utilizzate questa impostazione per aggiungere o sostituire proprietà che sono un bean o una mappa. SDKSostituisce o aggiunge qualsiasi mappa o bean fornito nell'oggetto. Tutti i bean o le mappe che sono nulli nell'oggetto vengono ignorati, mantenendo la mappa esistente in DynamoDB.

  • IgnoreNullsMode.DEFAULT- Con questa impostazione, non ignora mai i valori nulliSDK. Gli attributi scalari a qualsiasi livello che sono nulli vengono aggiornati a null. SDKaggiorna qualsiasi proprietà bean, map, list o set con valori null nell'oggetto su null in DynamoDB. Quando si utilizza questa modalità, o non si fornisce una modalità poiché è la modalità predefinita, è necessario recuperare prima l'elemento in modo che i valori in DynamoDB non siano impostati su null forniti nell'oggetto per l'aggiornamento, a meno che non si intenda impostare i valori su null.

In tutte le modalità, se si fornisce un oggetto con un elenco o un set non nullo, l'elenco o il set viene salvato in DynamoDB. updateItem

Perché le modalità?

Quando fornisci un oggetto con un bean o una map al updateItem metodo, non SDK riescono a capire se debba usare i valori delle proprietà nel bean (o i valori di immissione nella mappa) per aggiornare l'elemento o se l'intero bean/map debba sostituire ciò che è stato salvato in DynamoDB.

Partendo dal nostro esempio precedente che mostra innanzitutto il recupero dell'elemento, proviamo ad aggiornare l'attributo di without the retrieval. city 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'. */

I due esempi seguenti mostrano gli usi di e dei valori enumeratiMAPS_ONLY. SCALAR_ONLY MAPS_ONLYaggiunge una mappa e SCALAR_ONLY aggiorna una mappa.

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. }

Fate riferimento alla tabella seguente per vedere quali valori nulli vengono ignorati per ciascuna modalità. Spesso è possibile lavorare con entrambiSCALAR_ONLY, MAPS_ONLY tranne quando si lavora con bean o mappe.

Quali proprietà con valori nulli nell'oggetto inviato vengono SDK ignorate per updateItem ciascuna modalità?
Tipo di proprietà in ONLY modalità SCALAR _ in ONLY modalità MAPS _ in DEFAULT modalità
Scalare superiore No
Bean o mappa No
Valore scalare di un bean o di una voce di mappa 1 N. 2 No
Elenco o set No

1 Ciò presuppone che la mappa esista già in DynamoDB. Qualsiasi valore scalare, nullo o non nullo, del bean o della mappa fornito nell'oggetto per l'aggiornamento richiede che esista un percorso al valore in DynamoDB. SDKCostruisce un percorso verso l'attributo utilizzando l'operatore di dereferenza prima di inviare la richiesta. . (dot)

2 Poiché si utilizza la MAPS_ONLY modalità per sostituire completamente o aggiungere un bean o una map, tutti i valori null nel bean o nella mappa vengono mantenuti nella mappa salvata in DynamoDB.