Travaillez avec des attributs tels que des beans, des cartes, des listes et des ensembles - AWS SDK for Java 2.x

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

Travaillez avec des attributs tels que des beans, des cartes, des listes et des ensembles

Une définition de bean, telle que la Person classe illustrée ci-dessous, peut définir des propriétés (ou des attributs) qui font référence à des types dotés d'attributs supplémentaires. Par exemple, dans la Person classe, se mainAddress trouve une propriété qui fait référence à un Address bean qui définit des attributs de valeur supplémentaires. addressesfait référence à une carte Java, dont les éléments font référence à des Address beans. Ces types complexes peuvent être considérés comme des conteneurs d'attributs simples que vous utilisez pour leur valeur de données dans le contexte de DynamoDB.

DynamoDB désigne les propriétés de valeur des éléments imbriqués, tels que les cartes, les listes ou les beans, en tant qu'attributs imbriqués. Le guide du développeur Amazon DynamoDB fait référence à la forme enregistrée d'une carte, d'une liste ou d'un bean Java en tant que type de document. Les attributs simples que vous utilisez pour leur valeur de données en Java sont appelés types scalaires dans DynamoDB. Ensembles, qui contiennent plusieurs éléments scalaires du même type, appelés types d'ensembles.

Il est important de savoir que le API client amélioré DynamoDB convertit une propriété qui est un bean en un type de document ArcMap DynamoDB lors de son enregistrement.

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

Enregistrer des types complexes

Utiliser des classes de données annotées

Pour enregistrer les attributs imbriqués des classes personnalisées, il suffit de les annoter. La Address classe et la PhoneNumber classe affichées précédemment sont annotées uniquement avec l'@DynamoDbBeanannotation. Lorsque le API client DynamoDB Enhanced crée le schéma de table pour la classe à Person l'aide de l'extrait suivant, il découvre API l'utilisation des Address classes et et crée les mappages PhoneNumber correspondants pour fonctionner avec DynamoDB.

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

Utiliser des schémas abstraits avec des constructeurs

L'approche alternative consiste à utiliser des générateurs de schémas de tables statiques pour chaque classe de bean imbriquée, comme indiqué dans le code suivant.

Les schémas de table des PhoneNumber classes Address et sont abstraits dans le sens où ils ne peuvent pas être utilisés avec une table DynamoDB. Cela est dû au fait qu'ils ne disposent pas de définitions pour la clé primaire. Ils sont toutefois utilisés comme schémas imbriqués dans le schéma de table de la Person classe.

Après les lignes de commentaire 1 et 2 de la définition dePERSON_TABLE_SCHEMA, vous pouvez voir le code qui utilise les schémas de table abstraits. L'utilisation de documentOf dans la EnhanceType.documentOf(...) méthode n'indique pas que la méthode renvoie un EnhancedDocument type du document amélioréAPI. Dans ce contexte, la documentOf(...) méthode renvoie un objet qui sait comment mapper son argument de classe vers et depuis les attributs de table DynamoDB en utilisant l'argument de schéma de table.

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

Attributs de projet de types complexes

Pour les scan() méthodes query() et, vous pouvez spécifier les attributs que vous souhaitez voir renvoyés dans les résultats en utilisant des appels de méthode tels que addNestedAttributeToProject() etattributesToProject(). Le API client DynamoDB Enhanced convertit les paramètres d'appel de méthode Java en expressions de projection avant l'envoi de la demande.

L'exemple suivant remplit le Person tableau avec deux éléments, puis exécute trois opérations de numérisation.

Le premier scan accède à tous les éléments du tableau afin de comparer les résultats aux autres opérations d'analyse.

Le second scan utilise la méthode du addNestedAttributeToProject()générateur pour renvoyer uniquement la valeur de street l'attribut.

La troisième opération d'analyse utilise la méthode du attributesToProject()générateur pour renvoyer les données de l'attribut de premier niveau,hobbies. Le type d'attribut de hobbies est une liste. Pour accéder à des éléments de liste individuels, effectuez une get() opération sur la liste.

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
Note

Si la attributesToProject() méthode suit une autre méthode de création qui ajoute les attributs que vous souhaitez projeter, la liste des noms d'attributs fournie au attributesToProject() remplace tous les autres noms d'attributs.

Une analyse effectuée avec l'ScanEnhancedRequestinstance figurant dans l'extrait suivant renvoie uniquement les données relatives aux loisirs.

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

L'extrait de code suivant utilise d'abord la attributesToProject() méthode. Cet ordre préserve tous les autres attributs demandés.

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

Utiliser des types complexes dans les expressions

Vous pouvez utiliser des types complexes dans les expressions, telles que les expressions de filtre et les expressions de condition, en utilisant des opérateurs de déréférencement pour parcourir la structure du type complexe. Pour les objets et les cartes, utilisez le . (dot) et pour les éléments de liste [n] (crochets autour du numéro de séquence de l'élément). Vous ne pouvez pas faire référence à des éléments individuels d'un ensemble, mais vous pouvez utiliser la containsfonction.

L'exemple suivant montre deux expressions de filtre utilisées dans les opérations de numérisation. Les expressions de filtre spécifient les conditions de correspondance pour les éléments que vous souhaitez voir apparaître dans les résultats. L'exemple utilisePerson,Address, et PhoneNumber les classes présentés précédemment.

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

Mettre à jour les éléments contenant des types complexes

Pour mettre à jour un élément contenant des types complexes, vous avez deux approches de base :

  • Approche 1 : récupérez d'abord l'élément (en utilisantgetItem), mettez à jour l'objet, puis appelezDynamoDbTable#updateItem.

  • Approche 2 : Ne récupérez pas l'élément, mais créez une nouvelle instance, définissez les propriétés que vous souhaitez mettre à jour et soumettez l'instance DynamoDbTable#updateItem en définissant la valeur appropriée de IgnoreNullsMode. Cette approche ne nécessite pas que vous récupériez l'élément avant de le mettre à jour.

Les exemples présentés dans cette section utilisent les PhoneNumber classes PersonAddress,, et présentées précédemment.

Approche de mise à jour 1 : récupération, puis mise à jour

En utilisant cette approche, vous vous assurez qu'aucune donnée n'est perdue lors de la mise à jour. Le API client amélioré DynamoDB recrée le bean avec les attributs de l'élément enregistré dans DynamoDB, y compris les valeurs de types complexes. Vous devez ensuite utiliser les getters et setters pour mettre à jour le bean. L'inconvénient de cette approche est le coût que vous devez engager pour récupérer l'article en premier.

L'exemple suivant montre qu'aucune donnée n'est perdue si vous récupérez l'élément avant de le mettre à jour.

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

Approche de mise à jour 2 : utilisez une IgnoreNullsMode énumération sans récupérer l'élément au préalable

Pour mettre à jour un élément dans DynamoDB, vous pouvez fournir un nouvel objet contenant uniquement les propriétés que vous souhaitez mettre à jour et laisser les autres valeurs nulles. Avec cette approche, vous devez savoir comment les valeurs nulles de l'objet sont traitées par le SDK et comment vous pouvez contrôler le comportement.

Pour spécifier les propriétés à valeur nulle que vous SDK souhaitez ignorer, fournissez une IgnoreNullsMode énumération lorsque vous créez le. UpdateItemEnhancedRequest À titre d'exemple d'utilisation de l'une des valeurs énumérées, l'extrait de code suivant utilise le mode. 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" } } */

Le guide du développeur Amazon DynamoDB contient plus d'informations sur les expressions de mise à jour.

Descriptions des IgnoreNullsMode options

  • IgnoreNullsMode.SCALAR_ONLY- Utilisez ce paramètre pour mettre à jour les attributs scalaires à tous les niveaux. SDKConstruit une instruction de mise à jour qui envoie uniquement des attributs scalaires non nuls à DynamoDB. SDKIgnore les attributs scalaires à valeur nulle d'un bean ou d'une carte, en conservant la valeur enregistrée dans DynamoDB.

    Lorsque vous mettez à jour un attribut scalaire de map ou bean, la carte doit déjà exister dans DynamoDB. Si vous ajoutez une carte ou un bean à l'objet qui n'existe pas déjà pour cet objet dans DynamoDB, le message « Le chemin du document indiqué dans l'expression de mise à jour n'est pas DynamoDbException valide pour la mise à jour » s'affiche. Vous devez utiliser MAPS_ONLY le mode pour ajouter un bean ou une carte à DynamoDB avant de mettre à jour l'un de ses attributs.

  • IgnoreNullsMode.MAPS_ONLY- Utilisez ce paramètre pour ajouter ou remplacer des propriétés qui sont un bean ou une carte. SDKRemplace ou ajoute n'importe quelle carte ou bean fourni dans l'objet. Tous les beans ou cartes dont la valeur est nulle dans l'objet sont ignorés, ce qui permet de conserver la carte qui existe dans DynamoDB.

  • IgnoreNullsMode.DEFAULT- Avec ce paramètre, les valeurs nulles SDK ne sont jamais ignorées. Les attributs scalaires de tous les niveaux qui sont nuls sont mis à jour à zéro. SDKmet à jour toute propriété bean, map, list ou set à valeur nulle de l'objet sur null dans DynamoDB. Lorsque vous utilisez ce mode (ou que vous n'en fournissez aucun puisqu'il s'agit du mode par défaut), vous devez d'abord récupérer l'élément afin que les valeurs fournies dans l'objet pour la mise à jour dans DynamoDB ne soient pas définies sur null, sauf si vous avez l'intention de définir les valeurs sur null.

Dans tous les modes, si vous fournissez un objet updateItem dont la liste ou l'ensemble n'est pas nul, la liste ou l'ensemble est enregistré dans DynamoDB.

Pourquoi les modes ?

Lorsque vous fournissez à un objet un bean ou un mappage vers la updateItem méthode, il est SDK impossible de dire s'il doit utiliser les valeurs des propriétés du bean (ou les valeurs d'entrée dans la carte) pour mettre à jour l'élément, ou si le bean/la carte complet doit remplacer ce qui a été enregistré dans DynamoDB.

À partir de notre exemple précédent qui montre d'abord la récupération de l'élément, essayons de mettre à jour l'cityattribut de mainAddress sans la récupération.

/* 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'. */

Les deux exemples suivants montrent les utilisations des valeurs SCALAR_ONLY énumérées MAPS_ONLY et. MAPS_ONLYajoute une carte et SCALAR_ONLY met à jour une carte.

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

Reportez-vous au tableau suivant pour savoir quelles valeurs nulles sont ignorées pour chaque mode. Vous pouvez souvent travailler avec l'un ou SCALAR_ONLY l'autreMAPS_ONLY, sauf lorsque vous travaillez avec des beans ou des cartes.

Quelles sont les propriétés à valeur nulle de l'objet soumis updateItem SDK ignorées pour chaque mode ?
Type de propriété en ONLY mode SCALAR _ en ONLY mode MAPS _ en DEFAULT mode
Meilleur scalaire Oui Oui Non
Bean ou carte Oui Oui Non
Valeur scalaire d'un bean ou d'une entrée de carte Oui1 2 Non
Liste ou ensemble Oui Oui Non

1 Cela suppose que la carte existe déjà dans DynamoDB. Toute valeur scalaire (nulle ou non nulle) du bean ou de la carte que vous fournissez dans l'objet à mettre à jour nécessite qu'un chemin d'accès à la valeur existe dans DynamoDB. SDKConstruit un chemin vers l'attribut en utilisant l'opérateur de . (dot) déréférencement avant de soumettre la demande.

2 Puisque vous utilisez le MAPS_ONLY mode pour remplacer complètement ou ajouter un bean ou une map, toutes les valeurs nulles du bean ou de la map sont conservées dans la map enregistrée dans DynamoDB.