UpdateItem DynamoDB in Java (AWS SDK v2)
L'aggiornamento in sé è una sola chiamata al builder. Quello che costa tempo agli sviluppatori Java è tutto ciò che gli sta attorno: un oggetto di risposta che non restituisce mai null, una gerarchia di eccezioni in cui il fallimento interessante è una sottoclasse di quella che probabilmente hai catturato, e un client di alto livello che non riesce a esprimere affatto questa operazione.
Codice
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());
}
}
}Spiegazione
AttributeValue.builder().n("1994")prende unaString, e così fa anche la più breveAttributeValue.fromN("1994"). Non esiste un overloadn(int), perché i numeri DynamoDB reggono 38 cifre significative e nessun primitivo Java lo fa. In lettura, ancheattributes().get("Awards").n()è unaString; l'accessor del tipo sbagliato restituisce null invece di sollevare un'eccezione, quindi.s()su un numero è un null silenzioso, e.type()ti dice quale è impostato.response.attributes()non restituisce mai null. ConReturnValue.NONErestituisce unaDefaultSdkAutoConstructMapvuota ma non nulla, quindi un controllo su null non scatta mai e un controllo conisEmpty()non può distinguere "il servizio non ha mandato nulla" da "l'Item non ha attributi". Il generatohasAttributes()è l'accessor che conosce la differenza. Ogni membro collection in questo SDK ne ha uno.Il builder controlla i tipi di tutto tranne la parte che conta.
updateExpression(String)accetta qualsiasi stringa; il compilatore non distingueSETda un refuso, quindi gli errori di espressione sono 400 a runtime.ADD #upd2 :updValue2è l'incremento atomico, unaconditionExpressionpari aattribute_exists(Artist)rende la chiamata di solo aggiornamento, e la grammatica è nelle update expression.Preferisci l'espressione alla vecchia mappa
attributeUpdates. Gli esempi più datati la mostrano ancora; non può esprimere più tipi di clausola, alias o una condizione in una sola richiesta.getMessage()non è il messaggio del servizio. L'SDK vi accoda il proprio dettaglio di trasporto:The conditional request failed (Service: DynamoDb, Status Code: 400, Request ID: d99b117c-edd6-4dc9-8d3a-a5fa4fe9666c) (SDK Attempt Count: 1)Logga quello se vuoi il request ID per un ticket di supporto. Per i confronti usa invece
awsErrorDetails().errorCode(), e usaawsErrorDetails().errorMessage()quando vuoi la stringa nuda.
Qui l'ordine dei catch conta più del solito
ConditionalCheckFailedException extends DynamoDbException, quindi un catch (DynamoDbException e) messo per primo si mangia l'unico fallimento su cui quasi certamente volevi ramificare. Cattura prima il tipo specifico, e già che ci sei prenditi l'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
}Su questa e.retryable() è false, ed è corretto: riprovare una condizione fallita fallisce di nuovo.
L'asimmetria da ricordare è che ValidationException non ha una classe in questo SDK. Cerca in dynamodb-2.35.9.jar e non c'è nulla da catturare. Una parola riservata, un'espressione malformata, una chiave parziale: arrivano tutte come una semplice DynamoDbException il cui awsErrorDetails().errorCode() per caso recita ValidationException. In un linguaggio tipizzato staticamente è una lacuna stridente, e significa che gli errori di espressione sono confronti di stringhe a runtime.
Ecco perché le parole riservate di DynamoDB meritano una passata prima di rilasciare invece che dopo: la lista arriva a 573 voci e include Year, Name e Status, nessuna delle quali sembra pericolosa in un bean Java. Per sfogliare la tabella grezza invece della mappatura a bean che le sta sopra, scarica DynoTable.
L'enhanced client non riesce a esprimere questo
Se il resto del tuo accesso ai dati passa da DynamoDbEnhancedClient e da bean annotati, questa operazione è quella che ti riporta a DynamoDbClient. Usando la reflection su UpdateItemEnhancedRequest.Builder saltano fuori item, conditionExpression, ignoreNulls, ignoreNullsMode, returnValues, returnValuesOnConditionCheckFailure, returnConsumedCapacity e returnItemCollectionMetrics. Non c'è alcun metodo che accetti una update expression.
La conseguenza pratica è il contatore atomico. ADD #upd2 :updValue2 incrementa Awards lato server senza una lettura preliminare; l'enhanced client ti dà un bean mappato e ignoreNulls per decidere se i campi assenti vengono rimossi, e niente che compili fino ad ADD. Il read-modify-write attraverso un bean è una race da lost update in concorrenza, che è esattamente ciò che lo snippet di questa pagina evita.
Esempi correlati
- UpdateItem DynamoDB in Go — lo stesso aggiornamento con AWS SDK for Go v2.
- PutItem DynamoDB in Java — sostituisci invece l'Item intero.
- Update expression DynamoDB —
SET,ADD,REMOVE,DELETEe gli idiomi. - Capire ReturnValues — cosa ti dà ciascuna opzione di
ReturnValues. - "Attribute name is a reserved keyword" — perché qui la mappa di alias non è facoltativa.
- Errori di sintassi "Invalid UpdateExpression" — i comuni errori di sintassi SET/ADD, decodificati.
Riferimenti
- 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
Ultima verifica 2026-07-28 rispetto alla documentazione ufficiale AWS collegata sopra.