Scopri le basi del DynamoDB Enhanced Client API - 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à.

Scopri le basi del DynamoDB Enhanced Client API

Questo argomento illustra le funzionalità di base del DynamoDB Enhanced API Client e lo confronta con il client DynamoDB standard. API

Se non conosci DynamoDB Enhanced API Client, ti consigliamo di seguire il tutorial introduttivo per familiarizzare con le classi fondamentali.

Elementi DynamoDB in Java

Le tabelle DynamoDB memorizzano gli elementi. A seconda del caso d'uso, gli elementi sul lato Java possono assumere la forma di dati strutturati staticamente o di strutture create dinamicamente.

Se il tuo caso d'uso richiede elementi con un set coerente di attributi, usa classi annotate o usa un builder per generare i tipi statici appropriati. TableSchema

In alternativa, se devi archiviare elementi costituiti da strutture diverse, crea un. DocumentTableSchema DocumentTableSchemafa parte del Documento avanzato API e richiede solo una chiave primaria tipizzata staticamente e funziona con le EnhancedDocument istanze per contenere gli elementi di dati. Il Documento API avanzato è trattato in un altro argomento.

Tipi di attributi per le classi del modello di dati

Sebbene DynamoDB supporti un numero limitato di tipi di attributi rispetto al rich type system di Java, DynamoDB Enhanced API Client fornisce meccanismi per convertire i membri di una classe Java da e verso i tipi di attributi DynamoDB.

I tipi di attributi (proprietà) delle classi di dati Java devono essere tipi di oggetti, non primitivi. Ad esempio, utilizzate Long sempre tipi di dati Integer oggetto, non long e int primitivi.

Per impostazione predefinita, il DynamoDB Enhanced API Client supporta convertitori di attributi per un gran numero di tipi, come Integer, String e Instant. BigDecimal L'elenco viene visualizzato nelle classi di implementazione note dell'interfaccia. AttributeConverter L'elenco include molti tipi e raccolte come mappe, elenchi e set.

Per memorizzare i dati per un tipo di attributo che non è supportato di default o non è conforme alla JavaBean convenzione, puoi scrivere un'AttributeConverterimplementazione personalizzata per eseguire la conversione. Per un esempio, consulta la sezione sulla conversione degli attributi.

Per memorizzare i dati per un tipo di attributo la cui classe è conforme alla specifica Java beans (o una classe di dati immutabile), puoi adottare due approcci.

  • Se avete accesso al file sorgente, potete annotare la classe con @DynamoDbBean (o). @DynamoDbImmutable La sezione che illustra gli attributi annidati mostra esempi di utilizzo di classi annotate.

  • Se non hai accesso al file sorgente della classe di JavaBean dati per l'attributo (o non vuoi annotare il file sorgente di una classe a cui hai accesso), puoi usare l'approccio builder. Questo crea uno schema tabellare senza definire le chiavi. Quindi, puoi annidare questo schema di tabella all'interno di un altro schema di tabella per eseguire la mappatura. La sezione degli attributi nidificati contiene un esempio che mostra l'uso di schemi nidificati.

Valori nulli

Quando si utilizza il putItem metodo, il client avanzato non include gli attributi con valori nulli di un oggetto dati mappato nella richiesta a DynamoDB.

Il SDK comportamento predefinito per updateItem le richieste rimuove gli attributi dall'elemento in DynamoDB che sono impostati su null nell'oggetto inviato nel metodo. updateItem Se intendete aggiornare alcuni valori degli attributi e mantenere invariati gli altri, avete due opzioni.

  • Recuperate l'elemento (utilizzandogetItem) prima di apportare modifiche ai valori. Utilizzando questo approccio, SDK invia tutti i valori vecchi e aggiornati a DynamoDB.

  • Usa IgnoreNullsMode.SCALAR_ONLY o IgnoreNullsMode.MAPS_ONLY quando crei la richiesta per aggiornare l'elemento. Entrambe le modalità ignorano le proprietà con valori nulli nell'oggetto che rappresentano gli attributi scalari in DynamoDB. L'Aggiorna gli elementi che contengono tipi complessiargomento di questa guida contiene ulteriori informazioni sui IgnoreNullsMode valori e su come lavorare con tipi complessi.

L'esempio seguente illustra ignoreNullsMode() il updateItem() metodo.

public static void updateItemNullsExample() { Customer customer = new Customer(); customer.setCustName("CustomerName"); customer.setEmail("email"); customer.setId("1"); customer.setRegistrationDate(Instant.now()); logger.info("Original customer: {}", customer); // Put item with values for all attributes. try { customerAsyncDynamoDbTable.putItem(customer).join(); } catch (RuntimeException rte) { logger.error("A exception occurred during putItem: {}", rte.getCause().getMessage(), rte); return; } // Create a Customer instance with the same 'id' and 'email' values, but a different 'name' value. // Do not set the 'registrationDate' attribute. Customer customerForUpdate = new Customer(); customerForUpdate.setCustName("NewName"); customerForUpdate.setEmail("email"); customerForUpdate.setId("1"); // Update item without setting the 'registrationDate' property and set IgnoreNullsMode to SCALAR_ONLY. try { Customer updatedWithNullsIgnored = customerAsyncDynamoDbTable.updateItem(b -> b .item(customerForUpdate) .ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY)) .join(); logger.info("Customer updated with nulls ignored: {}", updatedWithNullsIgnored.toString()); } catch (RuntimeException rte) { logger.error("An exception occurred during updateItem: {}", rte.getCause().getMessage(), rte); return; } // Update item without setting the registrationDate attribute and not setting ignoreNulls to true. try { Customer updatedWithNullsUsed = customerAsyncDynamoDbTable.updateItem(customerForUpdate) .join(); logger.info("Customer updated with nulls used: {}", updatedWithNullsUsed.toString()); } catch (RuntimeException rte) { logger.error("An exception occurred during updateItem: {}", rte.getCause().getMessage(), rte); } } // Logged lines. Original customer: Customer [id=1, name=CustomerName, email=email, regDate=2024-10-11T14:12:30.222858Z] Customer updated with nulls ignored: Customer [id=1, name=NewName, email=email, regDate=2024-10-11T14:12:30.222858Z] Customer updated with nulls used: Customer [id=1, name=NewName, email=email, regDate=null]

Metodi di base di DynamoDB Enhanced Client

I metodi di base del client avanzato si associano alle operazioni del servizio DynamoDB da cui prendono il nome. Gli esempi seguenti mostrano la variante più semplice di ciascun metodo. È possibile personalizzare ogni metodo passando un oggetto di richiesta avanzato. Gli oggetti di richiesta avanzati offrono la maggior parte delle funzionalità disponibili nel client DynamoDB standard. Sono ampiamente documentati nel Reference. AWS SDK for Java 2.x API

L'esempio utilizza quanto Classe Customer mostrato in precedenza.

// CreateTable customerTable.createTable(); // GetItem Customer customer = customerTable.getItem(Key.builder().partitionValue("a123").build()); // UpdateItem Customer updatedCustomer = customerTable.updateItem(customer); // PutItem customerTable.putItem(customer); // DeleteItem Customer deletedCustomer = customerTable.deleteItem(Key.builder().partitionValue("a123").sortValue(456).build()); // Query PageIterable<Customer> customers = customerTable.query(keyEqualTo(k -> k.partitionValue("a123"))); // Scan PageIterable<Customer> customers = customerTable.scan(); // BatchGetItem BatchGetResultPageIterable batchResults = enhancedClient.batchGetItem(r -> r.addReadBatch(ReadBatch.builder(Customer.class) .mappedTableResource(customerTable) .addGetItem(key1) .addGetItem(key2) .addGetItem(key3) .build())); // BatchWriteItem batchResults = enhancedClient.batchWriteItem(r -> r.addWriteBatch(WriteBatch.builder(Customer.class) .mappedTableResource(customerTable) .addPutItem(customer) .addDeleteItem(key1) .addDeleteItem(key1) .build())); // TransactGetItems transactResults = enhancedClient.transactGetItems(r -> r.addGetItem(customerTable, key1) .addGetItem(customerTable, key2)); // TransactWriteItems enhancedClient.transactWriteItems(r -> r.addConditionCheck(customerTable, i -> i.key(orderKey) .conditionExpression(conditionExpression)) .addUpdateItem(customerTable, customer) .addDeleteItem(customerTable, key));

Confronta DynamoDB Enhanced Client con il client DynamoDB standard

Entrambi i APIs client DynamoDB, standard e avanzato, consentono di lavorare con le tabelle DynamoDB per CRUD eseguire (creazione, lettura, aggiornamento ed eliminazione) operazioni a livello di dati. La differenza tra i client sta nel modo in cui ciò viene realizzato. APIs Utilizzando il client standard, si lavora direttamente con attributi di dati di basso livello. Il client avanzato API utilizza classi Java familiari e mappa il API dietro le quinte di basso livello.

Sebbene entrambi i client APIs supportino operazioni a livello di dati, il client DynamoDB standard supporta anche operazioni a livello di risorsa. Le operazioni a livello di risorsa gestiscono il database, come la creazione di backup, l'elenco delle tabelle e l'aggiornamento delle tabelle. Il client avanzato API supporta un numero selezionato di operazioni a livello di risorsa come la creazione, la descrizione e l'eliminazione di tabelle.

Per illustrare i diversi approcci utilizzati dai due clientAPIs, i seguenti esempi di codice mostrano la creazione della stessa ProductCatalog tabella utilizzando il client standard e il client avanzato.

Confronto: crea una tabella utilizzando il client DynamoDB standard

DependencyFactory.dynamoDbClient().createTable(builder -> builder .tableName(TABLE_NAME) .attributeDefinitions( b -> b.attributeName("id").attributeType(ScalarAttributeType.N), b -> b.attributeName("title").attributeType(ScalarAttributeType.S), b -> b.attributeName("isbn").attributeType(ScalarAttributeType.S) ) .keySchema( builder1 -> builder1.attributeName("id").keyType(KeyType.HASH), builder2 -> builder2.attributeName("title").keyType(KeyType.RANGE) ) .globalSecondaryIndexes(builder3 -> builder3 .indexName("products_by_isbn") .keySchema(builder2 -> builder2 .attributeName("isbn").keyType(KeyType.HASH)) .projection(builder2 -> builder2 .projectionType(ProjectionType.INCLUDE) .nonKeyAttributes("price", "authors")) .provisionedThroughput(builder4 -> builder4 .writeCapacityUnits(5L).readCapacityUnits(5L)) ) .provisionedThroughput(builder1 -> builder1 .readCapacityUnits(5L).writeCapacityUnits(5L)) );

Confronta: crea una tabella utilizzando il DynamoDB Enhanced Client

DynamoDbEnhancedClient enhancedClient = DependencyFactory.enhancedClient(); productCatalog = enhancedClient.table(TABLE_NAME, TableSchema.fromImmutableClass(ProductCatalog.class)); productCatalog.createTable(b -> b .provisionedThroughput(b1 -> b1.readCapacityUnits(5L).writeCapacityUnits(5L)) .globalSecondaryIndices(b2 -> b2.indexName("products_by_isbn") .projection(b4 -> b4 .projectionType(ProjectionType.INCLUDE) .nonKeyAttributes("price", "authors")) .provisionedThroughput(b3 -> b3.writeCapacityUnits(5L).readCapacityUnits(5L)) ) );

Il client avanzato utilizza la seguente classe di dati annotati. Il DynamoDB Enhanced Client mappa i tipi di dati Java ai tipi di dati DynamoDB per un codice meno dettagliato e più facile da seguire. ProductCatalogè un esempio di utilizzo di una classe immutabile con DynamoDB Enhanced Client. L'uso delle classi Immutabili per le classi di dati mappate viene discusso più avanti in questo argomento.

package org.example.tests.model; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbIgnore; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbImmutable; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondaryPartitionKey; import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey; import java.math.BigDecimal; import java.util.Objects; import java.util.Set; @DynamoDbImmutable(builder = ProductCatalog.Builder.class) public class ProductCatalog implements Comparable<ProductCatalog> { private Integer id; private String title; private String isbn; private Set<String> authors; private BigDecimal price; private ProductCatalog(Builder builder){ this.authors = builder.authors; this.id = builder.id; this.isbn = builder.isbn; this.price = builder.price; this.title = builder.title; } public static Builder builder(){ return new Builder(); } @DynamoDbPartitionKey public Integer id() { return id; } @DynamoDbSortKey public String title() { return title; } @DynamoDbSecondaryPartitionKey(indexNames = "products_by_isbn") public String isbn() { return isbn; } public Set<String> authors() { return authors; } public BigDecimal price() { return price; } public static final class Builder { private Integer id; private String title; private String isbn; private Set<String> authors; private BigDecimal price; private Builder(){} public Builder id(Integer id) { this.id = id; return this; } public Builder title(String title) { this.title = title; return this; } public Builder isbn(String ISBN) { this.isbn = ISBN; return this; } public Builder authors(Set<String> authors) { this.authors = authors; return this; } public Builder price(BigDecimal price) { this.price = price; return this; } public ProductCatalog build() { return new ProductCatalog(this); } } @Override public String toString() { final StringBuffer sb = new StringBuffer("ProductCatalog{"); sb.append("id=").append(id); sb.append(", title='").append(title).append('\''); sb.append(", isbn='").append(isbn).append('\''); sb.append(", authors=").append(authors); sb.append(", price=").append(price); sb.append('}'); return sb.toString(); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ProductCatalog that = (ProductCatalog) o; return id.equals(that.id) && title.equals(that.title) && Objects.equals(isbn, that.isbn) && Objects.equals(authors, that.authors) && Objects.equals(price, that.price); } @Override public int hashCode() { return Objects.hash(id, title, isbn, authors, price); } @Override @DynamoDbIgnore public int compareTo(ProductCatalog other) { if (this.id.compareTo(other.id) != 0){ return this.id.compareTo(other.id); } else { return this.title.compareTo(other.title); } } }

I due esempi di codice seguenti di scrittura in batch illustrano la verbosità e la mancanza di sicurezza dei tipi quando si utilizza il client standard anziché il client avanzato.

public static void batchWriteStandard(DynamoDbClient dynamoDbClient, String tableName) { Map<String, AttributeValue> catalogItem = Map.of( "authors", AttributeValue.builder().ss("a", "b").build(), "id", AttributeValue.builder().n("1").build(), "isbn", AttributeValue.builder().s("1-565-85698").build(), "title", AttributeValue.builder().s("Title 1").build(), "price", AttributeValue.builder().n("52.13").build()); Map<String, AttributeValue> catalogItem2 = Map.of( "authors", AttributeValue.builder().ss("a", "b", "c").build(), "id", AttributeValue.builder().n("2").build(), "isbn", AttributeValue.builder().s("1-208-98073").build(), "title", AttributeValue.builder().s("Title 2").build(), "price", AttributeValue.builder().n("21.99").build()); Map<String, AttributeValue> catalogItem3 = Map.of( "authors", AttributeValue.builder().ss("g", "k", "c").build(), "id", AttributeValue.builder().n("3").build(), "isbn", AttributeValue.builder().s("7-236-98618").build(), "title", AttributeValue.builder().s("Title 3").build(), "price", AttributeValue.builder().n("42.00").build()); Set<WriteRequest> writeRequests = Set.of( WriteRequest.builder().putRequest(b -> b.item(catalogItem)).build(), WriteRequest.builder().putRequest(b -> b.item(catalogItem2)).build(), WriteRequest.builder().putRequest(b -> b.item(catalogItem3)).build()); Map<String, Set<WriteRequest>> productCatalogItems = Map.of( "ProductCatalog", writeRequests); BatchWriteItemResponse response = dynamoDbClient.batchWriteItem(b -> b.requestItems(productCatalogItems)); logger.info("Unprocessed items: " + response.unprocessedItems().size()); }
public static void batchWriteEnhanced(DynamoDbTable<ProductCatalog> productCatalog) { ProductCatalog prod = ProductCatalog.builder() .id(1) .isbn("1-565-85698") .authors(new HashSet<>(Arrays.asList("a", "b"))) .price(BigDecimal.valueOf(52.13)) .title("Title 1") .build(); ProductCatalog prod2 = ProductCatalog.builder() .id(2) .isbn("1-208-98073") .authors(new HashSet<>(Arrays.asList("a", "b", "c"))) .price(BigDecimal.valueOf(21.99)) .title("Title 2") .build(); ProductCatalog prod3 = ProductCatalog.builder() .id(3) .isbn("7-236-98618") .authors(new HashSet<>(Arrays.asList("g", "k", "c"))) .price(BigDecimal.valueOf(42.00)) .title("Title 3") .build(); BatchWriteResult batchWriteResult = DependencyFactory.enhancedClient() .batchWriteItem(b -> b.writeBatches( WriteBatch.builder(ProductCatalog.class) .mappedTableResource(productCatalog) .addPutItem(prod).addPutItem(prod2).addPutItem(prod3) .build() )); logger.info("Unprocessed items: " + batchWriteResult.unprocessedPutItemsForTable(productCatalog).size()); }