Scopri le basi dell'API DynamoDB Enhanced Client - 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 dell'API DynamoDB Enhanced Client

Questo argomento illustra le funzionalità di base dell'API DynamoDB Enhanced Client e la confronta con l'API client DynamoDB standard.

Se non conosci l'API DynamoDB Enhanced 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 dell'API Enhanced Document e richiede solo una chiave primaria tipizzata staticamente e funziona con le EnhancedDocument istanze per contenere gli elementi di dati. L'API Enhanced Document è trattata 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, l'API DynamoDB Enhanced 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, l'API DynamoDB Enhanced 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 l'putItemAPI, il client avanzato non include gli attributi con valori nulli di un oggetto dati mappato nella richiesta a DynamoDB.

Per updateItem le richieste, gli attributi con valore nullo vengono rimossi dall'elemento del database. Se intendete aggiornare alcuni valori di attributo e mantenere invariati gli altri, copiate i valori di altri attributi che non devono essere modificati o utilizzate il metodo ignoreNull () sul generatore di aggiornamenti.

L'esempio seguente mostra il metodo for. ignoreNulls() the updateItem()

public void updateItemNullsExample(){ Customer customer = new Customer(); customer.setCustName("CustName"); customer.setEmail("email"); customer.setId("1"); customer.setRegistrationDate(Instant.now()); // Put item with values for all attributes. customerDynamoDbTable.putItem(customer); // Create a Customer instance with the same id value, but a different name value. // Do not set the 'registrationDate' attribute. Customer custForUpdate = new Customer(); custForUpdate.setCustName("NewName"); custForUpdate.setEmail("email"); custForUpdate.setId("1"); // Update item without setting the registrationDate attribute. customerDynamoDbTable.updateItem(b -> b .item(custForUpdate) .ignoreNulls(Boolean.TRUE)); Customer updatedWithNullsIgnored = customerDynamoDbTable.getItem(customer); // registrationDate value is unchanged. logger.info(updatedWithNullsIgnored.toString()); customerDynamoDbTable.updateItem(custForUpdate); Customer updatedWithNulls = customerDynamoDbTable.getItem(customer); // registrationDate value is null because ignoreNulls() was not used. logger.info(updatedWithNulls.toString()); } } // Logged lines. Customer [id=1, custName=NewName, email=email, registrationDate=2023-04-05T16:32:32.056Z] Customer [id=1, custName=NewName, email=email, registrationDate=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 completamente documentati nell'API Reference. AWS SDK for Java 2.x

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

Entrambe le API client DynamoDB, standard e avanzate, consentono di lavorare con le tabelle DynamoDB per eseguire operazioni CRUD (creazione, lettura, aggiornamento ed eliminazione) a livello di dati. La differenza tra le API client sta nel modo in cui ciò viene realizzato. Utilizzando il client standard, lavori direttamente con attributi di dati di basso livello. L'API client avanzata utilizza classi Java familiari e si collega all'API di basso livello dietro le quinte.

Sebbene entrambe le API client 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. L'API client avanzata 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 dalle due API client, 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()); }