Trabaja con atributos que son beans, mapas, listas y conjuntos - AWS SDK for Java 2.x

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

Trabaja con atributos que son beans, mapas, listas y conjuntos

Una definición de bean, como la Person clase que se muestra a continuación, podría definir propiedades (o atributos) que hagan referencia a tipos con atributos adicionales. Por ejemplo, en la Person clase, mainAddress hay una propiedad que hace referencia a un Address grano que define atributos de valor adicionales. addressesse refiere a un mapa de Java, cuyos elementos hacen referencia a Address granos. Estos tipos complejos pueden considerarse contenedores de atributos simples que se utilizan para su valor de datos en el contexto de DynamoDB.

DynamoDB hace referencia a las propiedades de valor de los elementos anidados, como mapas, listas o beans, como atributos anidados. La Guía para desarrolladores de Amazon DynamoDB hace referencia a la forma guardada de un mapa, lista o bean de Java como tipo de documento. Los atributos simples que se utilizan para su valor de datos en Java se denominan tipos escalares en DynamoDB. Los conjuntos, que contienen varios elementos escalares del mismo tipo, se denominan tipos de conjuntos.

Es importante saber que el API cliente mejorado de DynamoDB convierte una propiedad que es bean en un tipo de documento de mapa de DynamoDB al guardarla.

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

Guarde tipos complejos

Utilice clases de datos anotadas

Los atributos anidados de las clases personalizadas se guardan simplemente anotándolos. La clase Address y la clase PhoneNumber mostradas anteriormente se anotan solo con la anotación @DynamoDbBean. Cuando el API cliente mejorado de DynamoDB crea el esquema de tabla de la clase con Person el siguiente fragmento, descubre el uso de API las clases PhoneNumber y y crea las asignaciones correspondientes Address para que funcionen con DynamoDB.

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

Utilice esquemas abstractos con los compiladores

El enfoque alternativo consiste en utilizar generadores de esquemas de tablas estáticas para cada clase de bean anidada, como se muestra en el código siguiente.

Los esquemas de tabla de las clases Address y PhoneNumber y son abstractos en el sentido de que no se pueden usar con una tabla de DynamoDB. Esto se debe a que carecen de definiciones para la clave principal. Sin embargo, se utilizan como esquemas anidados en el esquema de tabla de la clase Person.

Tras las líneas de comentario 1 y 2 de la definición de PERSON_TABLE_SCHEMA, verá el código que utiliza los esquemas de tabla abstractos. El uso de documentOf en el EnhanceType.documentOf(...) método no indica que el método devuelva un EnhancedDocument tipo de documento API mejorado. En este contexto, el método documentOf(...) devuelve un objeto que sabe cómo asignar su argumento de clase a y desde los atributos de la tabla de DynamoDB mediante el argumento del esquema de la tabla.

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

Atributos del proyecto de tipos complejos

Para los métodos query() y scan(), puede especificar qué atributos desea que se devuelvan en los resultados mediante llamadas a métodos como addNestedAttributeToProject() y attributesToProject(). El API cliente mejorado de DynamoDB convierte los parámetros de llamada al método Java en expresiones de proyección antes de enviar la solicitud.

En el siguiente ejemplo, se rellena la tabla Person con dos elementos y, a continuación, se efectúan tres operaciones de análisis.

El primer análisis accede a todos los elementos de la tabla para comparar los resultados con las demás operaciones de análisis.

El segundo análisis aplica el método del constructor addNestedAttributeToProject() para devolver solo el valor del atributo street.

La tercera operación de análisis sigue el método del constructor attributesToProject() para devolver los datos del atributo de primer nivel, hobbies. El tipo de atributo hobbies es una lista Para acceder a cada elemento de la lista, haga una operación get() en la lista.

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

Si el método attributesToProject() sigue cualquier otro procedimiento de creación que añada los atributos que desee proyectar, la lista de nombres de atributos proporcionada a attributesToProject() sustituirá a todos los demás nombres de atributos.

Un análisis efectuado con la instancia ScanEnhancedRequest del fragmento siguiente devuelve solo datos de hobbies.

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

El siguiente fragmento de código usa primero el método attributesToProject(). Este orden preserva todos los demás atributos solicitados.

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

Utilice tipos complejos en las expresiones

Puede usar tipos complejos en las expresiones, como las expresiones de filtro y las expresiones de condición, mediante el uso de operadores de desreferenciación para navegar por la estructura del tipo complejo. Para objetos y mapas, utilice . (dot) y para elementos de lista [n] (el número de secuencia del elemento aparece entre corchetes). No puede hacer referencia a los elementos individuales de un conjunto, pero puede utilizar la containsfunción.

El siguiente ejemplo muestra dos expresiones de filtro que se utilizan en las operaciones de escaneo. Las expresiones de filtro especifican las condiciones de coincidencia de los elementos que desea incluir en los resultados. En el ejemplo se utilizan PersonAddress, y PhoneNumber las clases mostradas anteriormente.

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

Actualice los elementos que contienen tipos complejos

Para actualizar un elemento que contiene tipos complejos, tiene dos enfoques básicos:

  • Método 1: Primero recupere el elemento (usandogetItem), actualice el objeto y, a continuación, llameDynamoDbTable#updateItem.

  • Método 2: No recupere el elemento, sino que cree una nueva instancia, establezca las propiedades que desee actualizar y envíe la instancia DynamoDbTable#updateItem estableciendo el valor apropiado de IgnoreNullsMode. Este enfoque no requiere que busques el elemento antes de actualizarlo.

Los ejemplos que se muestran en esta sección utilizan las PhoneNumber clases PersonAddress, y mostradas anteriormente.

Método de actualización 1: recuperar y, a continuación, actualizar

Al utilizar este enfoque, se asegura de que no se pierdan datos durante la actualización. El API cliente mejorado de DynamoDB recrea el bean con los atributos del elemento guardado en DynamoDB, incluidos los valores de tipos complejos. A continuación, debe utilizar los captadores y los setters para actualizar el bean. La desventaja de este enfoque es el costo en el que incurres al recuperar primero el artículo.

El siguiente ejemplo demuestra que no se pierde ningún dato si se recupera el elemento antes de actualizarlo.

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

Método de actualización 2: utilice una IgnoreNullsMode enumeración sin recuperar primero el elemento

Para actualizar un elemento en DynamoDB, puede proporcionar un objeto nuevo que solo tenga las propiedades que desee actualizar y dejar los demás valores como nulos. Con este enfoque, debe saber cómo tratan los valores nulos del objeto SDK y cómo puede controlar su comportamiento.

Para especificar qué propiedades con valores nulos desea ignorar, proporcione una IgnoreNullsMode enumeración SDK al crear el. UpdateItemEnhancedRequest Como ejemplo del uso de uno de los valores enumerados, en el siguiente fragmento se utiliza el modo. 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" } } */

La guía para desarrolladores de Amazon DynamoDB contiene más información sobre las expresiones de actualización.

Descripciones de las opciones IgnoreNullsMode

  • IgnoreNullsMode.SCALAR_ONLY- Usa esta configuración para actualizar los atributos escalares en cualquier nivel. Crea una sentencia SDK de actualización que envía solo atributos escalares no nulos a DynamoDB. SDKIgnora los atributos escalares con valores nulos de un bean o mapa y conserva el valor guardado en DynamoDB.

    Al actualizar un atributo escalar de un mapa o bean, el mapa debe existir ya en DynamoDB. Si agrega un mapa o un bean al objeto que aún no existe para el objeto en DynamoDB, aparecerá DynamoDbException un mensaje con el mensaje La ruta del documento proporcionada en la expresión de actualización no es válida para la actualización. Debe usar el MAPS_ONLY modo para añadir un bean o un mapa a DynamoDB antes de actualizar cualquiera de sus atributos.

  • IgnoreNullsMode.MAPS_ONLY- Utilice esta configuración para añadir o reemplazar propiedades que sean un frijol o un mapa. SDKSustituye o añade cualquier mapa o frijol incluido en el objeto. Se ignoran los beans o mapas nulos en el objeto y se conserva el mapa que existe en DynamoDB.

  • IgnoreNullsMode.DEFAULT- Con esta configuración, SDK nunca ignora los valores nulos. Los atributos escalares de cualquier nivel que sean nulos se actualizan a nulos. SDKActualiza cualquier propiedad de bean, mapa, lista o conjunto con valores nulos del objeto a null en DynamoDB. Cuando utilice este modo (o no proporcione un modo, ya que es el modo predeterminado), debe recuperar primero el elemento para que los valores de DynamoDB no se establezcan en nulos que se proporcionan en el objeto para su actualización, a menos que su intención sea establecer los valores en null.

En todos los modos, si proporciona un objeto updateItem que tiene una lista o un conjunto no nulos, la lista o el conjunto se guardan en DynamoDB.

¿Por qué los modos?

Cuando se proporciona un objeto con un bean o un mapa al updateItem método, no SDK se puede saber si debe usar los valores de propiedad del bean (o los valores de entrada del mapa) para actualizar el elemento, o si todo el frijo/mapa debe reemplazar lo que se ha guardado en DynamoDB.

Siguiendo nuestro ejemplo anterior, en el que se muestra primero la recuperación del elemento, intentemos actualizar el city atributo de sin la recuperación. 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'. */

En los dos ejemplos siguientes se muestran los usos de MAPS_ONLY los valores SCALAR_ONLY enumerados. MAPS_ONLYagrega un mapa y SCALAR_ONLY actualiza un mapa.

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

Consulte la siguiente tabla para ver qué valores nulos se ignoran en cada modo. A menudo puede trabajar con cualquiera SCALAR_ONLY de MAPS_ONLY ellos, excepto cuando trabaja con beans o mapas.

¿Qué propiedades con valores nulos del objeto al que se envía se SDK ignoran en cada modo? updateItem
Tipo de propiedad en ONLY modo SCALAR _ en ONLY modo MAPS _ en DEFAULT modo
Escalar superior No
Bean o mapa No
Valor escalar de una entrada de grano o mapa 1 No 2 No
Lista o conjunto No

1 Esto supone que el mapa ya existe en DynamoDB. Cualquier valor escalar (nulo o no nulo) del bean o el mapa que proporcione en el objeto para la actualización requiere que exista una ruta al valor en DynamoDB. Crea una ruta SDK al atributo mediante el operador de desreferencia antes de enviar la solicitud. . (dot)

2 Como se utiliza MAPS_ONLY el modo para sustituir por completo o añadir un bean o mapa, todos los valores nulos del bean o mapa se conservan en el mapa guardado en DynamoDB.