UpdateItem de DynamoDB en Java (SDK de AWS v2)
La actualización en sí es una sola llamada al builder. Lo que le cuesta tiempo a un desarrollador Java es todo lo que la rodea: un objeto de respuesta que nunca devuelve null, una jerarquía de excepciones donde el fallo interesante es una subclase de la que probablemente capturaste, y un cliente de alto nivel que no puede expresar esta operación en absoluto.
Código
import java.util.HashMap;
import java.util.Map;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.ReturnValue;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemResponse;
public class UpdateItemExample {
public static void main(String[] args) {
try (DynamoDbClient ddb = DynamoDbClient.builder()
.region(Region.US_EAST_1)
.build()) {
Map<String, AttributeValue> key = new HashMap<>();
key.put("Artist", AttributeValue.builder().s("Arturo Sandoval").build());
key.put("SongTitle", AttributeValue.builder().s("Cubano Chant").build());
Map<String, String> names = new HashMap<>();
names.put("#upd0", "Genre");
names.put("#upd1", "Year");
names.put("#upd2", "Awards");
Map<String, AttributeValue> values = new HashMap<>();
values.put(":updValue0", AttributeValue.builder().s("Latin Jazz").build());
values.put(":updValue1", AttributeValue.builder().n("1994").build());
values.put(":updValue2", AttributeValue.builder().n("1").build());
UpdateItemRequest request = UpdateItemRequest.builder()
.tableName("Music")
.key(key)
.updateExpression("SET #upd0 = :updValue0, #upd1 = :updValue1 ADD #upd2 :updValue2")
.expressionAttributeNames(names)
.expressionAttributeValues(values)
.returnValues(ReturnValue.ALL_NEW)
.build();
UpdateItemResponse response = ddb.updateItem(request);
System.out.println(response.attributes()); // the item after the update
} catch (DynamoDbException e) {
System.err.println(e.getMessage());
}
}
}Explicación
AttributeValue.builder().n("1994")recibe unString, y lo mismo el más cortoAttributeValue.fromN("1994"). No hay sobrecargan(int), porque los números de DynamoDB soportan 38 dígitos significativos y ningún primitivo de Java lo hace. A la vuelta,attributes().get("Awards").n()también es unString; el accesor del tipo equivocado devuelve null en vez de lanzar, así que.s()sobre un número es un null silencioso, y.type()te dice cuál está puesto.response.attributes()nunca devuelve null. ConReturnValue.NONEdevuelve unDefaultSdkAutoConstructMapvacío pero no nulo, así que una comprobación de null nunca se dispara y una comprobación deisEmpty()no puede distinguir «el servicio no envió nada» de «el Item no tiene atributos». ElhasAttributes()generado es el accesor que sabe la diferencia. Cada miembro de colección de este SDK tiene uno.El builder comprueba los tipos de todo salvo de la parte que importa.
updateExpression(String)acepta cualquier cadena; el compilador no distingueSETde una errata, así que los fallos de expresión son 400 en tiempo de ejecución.ADD #upd2 :updValue2es el incremento atómico, unaconditionExpressiondeattribute_exists(Artist)hace que la llamada sea solo de actualización, y la gramática está en expresiones de actualización.Prefiere la expresión al mapa heredado
attributeUpdates. Los ejemplos antiguos todavía lo muestran; no puede expresar varios tipos de cláusula, ni alias, ni una condición en una sola petición.getMessage()no es el mensaje del servicio. El SDK le añade su propio detalle de transporte:The conditional request failed (Service: DynamoDb, Status Code: 400, Request ID: d99b117c-edd6-4dc9-8d3a-a5fa4fe9666c) (SDK Attempt Count: 1)Registra eso si quieres el ID de petición para un caso de soporte. Compara mejor sobre
awsErrorDetails().errorCode(), y usaawsErrorDetails().errorMessage()cuando quieras la cadena a secas.
Aquí el orden de captura importa más de lo normal
ConditionalCheckFailedException extends DynamoDbException, así que un catch (DynamoDbException e) puesto primero se traga el único fallo sobre el que casi con seguridad querías ramificar. Captura primero el tipo específico y, ya que estás, quédate con el Item:
} catch (ConditionalCheckFailedException e) {
// with .returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD)
if (e.hasItem()) {
Map<String, AttributeValue> loser = e.item(); // the item as it actually was
}
} catch (DynamoDbException e) {
// everything else
}e.retryable() es false en este caso, y es correcto: reintentar una condición fallida solo vuelve a fallar.
La asimetría que hay que recordar es que ValidationException no tiene clase en este SDK. Busca en dynamodb-2.35.9.jar y no hay nada que capturar. Una palabra reservada, una expresión mal formada, una clave parcial: todas llegan como un DynamoDbException corriente cuyo awsErrorDetails().errorCode() resulta ser ValidationException. En un lenguaje de tipado estático eso es un hueco chocante, y significa que los fallos de expresión son comparaciones de cadenas en tiempo de ejecución.
Por eso las palabras reservadas de DynamoDB merecen una pasada antes de publicar y no después: la lista llega a 573 entradas e incluye Year, Name y Status, ninguna de las cuales parece peligrosa en un bean de Java. Para explorar la tabla en bruto en vez del bean mapeado encima, descarga DynoTable.
El enhanced client no puede expresar esto
Si el resto de tu acceso a datos pasa por DynamoDbEnhancedClient y beans anotados, esta operación es la que te devuelve a DynamoDbClient. Reflexionar sobre UpdateItemEnhancedRequest.Builder saca item, conditionExpression, ignoreNulls, ignoreNullsMode, returnValues, returnValuesOnConditionCheckFailure, returnConsumedCapacity y returnItemCollectionMetrics. No hay ningún método que acepte una expresión de actualización.
La consecuencia práctica es el contador atómico. ADD #upd2 :updValue2 incrementa Awards en el servidor sin leer antes; el enhanced client te da un bean mapeado e ignoreNulls para decidir si los campos ausentes se eliminan, y nada que compile a ADD. Leer-modificar-escribir a través de un bean es una carrera de actualización perdida bajo concurrencia, que es precisamente lo que evita el fragmento de esta página.
Ejemplos relacionados
- UpdateItem de DynamoDB en Go — la misma actualización con el SDK de AWS para Go v2.
- PutItem de DynamoDB en Java — reemplazar el Item entero en su lugar.
- Expresiones de actualización de DynamoDB —
SET,ADD,REMOVE,DELETEe idiomas. - Entender ReturnValues — qué te da cada opción de
ReturnValues. - «Attribute name is a reserved keyword» — por qué el mapa de alias de aquí no es opcional.
- Errores de sintaxis «Invalid UpdateExpression» — los fallos de sintaxis habituales de SET/ADD, descifrados.
Referencias
- UpdateItem — Amazon DynamoDB API Reference
- Use UpdateItem with an AWS SDK or CLI — Amazon DynamoDB Developer Guide
- DynamoDbClient — AWS SDK for Java 2.x API Reference
- UpdateItemRequest — AWS SDK for Java 2.x API Reference
- Update expressions — Amazon DynamoDB Developer Guide
Verificado por última vez el 2026-07-28 contra la documentación oficial de AWS enlazada arriba.