Lernen Sie die Grundlagen der DynamoDB Enhanced Client API kennen - AWS SDK for Java 2.x

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

Lernen Sie die Grundlagen der DynamoDB Enhanced Client API kennen

In diesem Thema werden die grundlegenden Funktionen der DynamoDB Enhanced Client API beschrieben und sie mit der standardmäßigen DynamoDB-Client-API verglichen.

Wenn Sie mit der DynamoDB Enhanced Client API noch nicht vertraut sind, empfehlen wir Ihnen, das Einführungstutorial zu lesen, um sich mit den grundlegenden Klassen vertraut zu machen.

DynamoDB-Elemente in Java

DynamoDB-Tabellen speichern Elemente. Abhängig von Ihrem Anwendungsfall können Elemente auf der Java-Seite die Form von statisch strukturierten Daten oder dynamisch erstellten Strukturen annehmen.

Wenn Ihr Anwendungsfall Elemente mit einem konsistenten Satz von Attributen erfordert, verwenden Sie annotierte Klassen oder verwenden Sie einen Builder, um die entsprechenden statisch typisierten Elemente zu generieren. TableSchema

Wenn Sie Elemente speichern müssen, die aus unterschiedlichen Strukturen bestehen, können Sie alternativ eine erstellen. DocumentTableSchema DocumentTableSchemaist Teil der Enhanced Document API und benötigt nur einen statisch typisierten Primärschlüssel und funktioniert mit EnhancedDocument Instanzen, die die Datenelemente enthalten. Die Enhanced Document API wird in einem anderen Thema behandelt.

Attributtypen für Datenmodellklassen

Obwohl DynamoDB im Vergleich zum Rich-Type-System von Java eine geringe Anzahl von Attributtypen unterstützt, bietet die DynamoDB Enhanced Client API Mechanismen zum Konvertieren von Mitgliedern einer Java-Klasse in und aus DynamoDB-Attributtypen.

Bei den Attributtypen (Eigenschaften) Ihrer Java-Datenklassen sollte es sich um Objekttypen und nicht um Primitive handeln. Verwenden Sie beispielsweise immer Datentypen Long und Integer Objekte, nicht long int Primitive.

Standardmäßig unterstützt die DynamoDB Enhanced Client-API Attributkonverter für eine Vielzahl von Typen, wie Integer BigDecimal, String und Instant. Die Liste wird in den bekannten Implementierungsklassen der AttributeConverter Schnittstelle angezeigt. Die Liste enthält viele Typen und Sammlungen wie Karten, Listen und Sets.

Um die Daten für einen Attributtyp zu speichern, der standardmäßig nicht unterstützt wird oder nicht der JavaBean Konvention entspricht, können Sie eine benutzerdefinierte AttributeConverter Implementierung für die Konvertierung schreiben. Ein Beispiel finden Sie im Abschnitt zur Attributkonvertierung.

Um die Daten für einen Attributtyp zu speichern, dessen Klasse der Java-Beans-Spezifikation (oder einer unveränderlichen Datenklasse) entspricht, können Sie zwei Ansätze wählen.

  • Wenn Sie Zugriff auf die Quelldatei haben, können Sie die Klasse mit @DynamoDbBean (oder) annotieren. @DynamoDbImmutable Der Abschnitt, der verschachtelte Attribute behandelt, zeigt Beispiele für die Verwendung von Klassen mit Anmerkungen.

  • Wenn Sie keinen Zugriff auf die Quelldatei der JavaBean Datenklasse für das Attribut haben (oder Sie die Quelldatei einer Klasse, auf die Sie Zugriff haben, nicht mit Anmerkungen versehen möchten), können Sie den Builder-Ansatz verwenden. Dadurch wird ein Tabellenschema erstellt, ohne die Schlüssel zu definieren. Anschließend können Sie dieses Tabellenschema in einem anderen Tabellenschema verschachteln, um die Zuordnung durchzuführen. Der Abschnitt mit verschachtelten Attributen enthält ein Beispiel, das die Verwendung verschachtelter Schemas zeigt.

Null-Werte

Wenn Sie die putItem API verwenden, nimmt der erweiterte Client keine nullwertigen Attribute eines zugewiesenen Datenobjekts in die Anforderung an DynamoDB auf.

Bei updateItem Anfragen werden Attribute mit Nullwerten aus dem Element in der Datenbank entfernt. Wenn Sie beabsichtigen, einige Attributwerte zu aktualisieren und die anderen unverändert zu lassen, kopieren Sie entweder die Werte anderer Attribute, die nicht geändert werden sollen, oder verwenden Sie die Methode ignoreNull () im Update Builder.

Das folgende Beispiel demonstriert die MethodeignoreNulls(). 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]

Grundlegende Methoden des DynamoDB Enhanced Client

Die grundlegenden Methoden des erweiterten Clients sind den DynamoDB-Dienstoperationen zugeordnet, nach denen sie benannt sind. Die folgenden Beispiele zeigen die einfachste Variante der einzelnen Methoden. Sie können jede Methode anpassen, indem Sie ein erweitertes Anforderungsobjekt übergeben. Verbesserte Anforderungsobjekte bieten die meisten Funktionen, die im Standard-DynamoDB-Client verfügbar sind. Sie sind in der AWS SDK for Java 2.x API-Referenz vollständig dokumentiert.

Das Beispiel verwendet das zuvor Customer-Klasse Gezeigte.

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

DynamoDB Enhanced Client mit dem Standard-DynamoDB-Client vergleichen

Mit beiden DynamoDB-Client-APIs — Standard und erweitert — können Sie mit DynamoDB-Tabellen arbeiten, um CRUD-Operationen (Create, Read, Update and Delete) auf Datenebene durchzuführen. Der Unterschied zwischen den Client-APIs besteht darin, wie dies erreicht wird. Mit dem Standard-Client arbeiten Sie direkt mit Datenattributen auf niedriger Ebene. Die erweiterte Client-API verwendet vertraute Java-Klassen und ist der Low-Level-API im Hintergrund zugeordnet.

Beide Client-APIs unterstützen zwar Operationen auf Datenebene, der standardmäßige DynamoDB-Client unterstützt jedoch auch Operationen auf Ressourcenebene. Operationen auf Ressourcenebene verwalten die Datenbank, z. B. das Erstellen von Backups, das Auflisten von Tabellen und das Aktualisieren von Tabellen. Die erweiterte Client-API unterstützt eine bestimmte Anzahl von Vorgängen auf Ressourcenebene, z. B. das Erstellen, Beschreiben und Löschen von Tabellen.

Um die unterschiedlichen Ansätze der beiden Client-APIs zu veranschaulichen, zeigen die folgenden Codebeispiele die Erstellung derselben ProductCatalog Tabelle mit dem Standardclient und dem erweiterten Client.

Vergleichen: Erstellen Sie eine Tabelle mit dem standardmäßigen DynamoDB-Client

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

Vergleichen: Erstellen Sie eine Tabelle mit dem 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)) ) );

Der erweiterte Client verwendet die folgende Datenklasse mit Anmerkungen. Der DynamoDB Enhanced Client ordnet Java-Datentypen DynamoDB-Datentypen zu und sorgt so für weniger ausführlichen Code, der leichter nachzuvollziehen ist. ProductCatalogist ein Beispiel für die Verwendung einer unveränderlichen Klasse mit dem DynamoDB Enhanced Client. Die Verwendung unveränderlicher Klassen für zugeordnete Datenklassen wird später in diesem Thema erörtert.

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

Die folgenden beiden Codebeispiele für Batch-Schreibvorgänge veranschaulichen die Ausführlichkeit und die mangelnde Typsicherheit bei der Verwendung des Standardclients im Gegensatz zum erweiterten Client.

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