

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
<a name="ddb-en-client-use"></a>

In diesem Thema werden die grundlegenden Funktionen der DynamoDB Enhanced Client API beschrieben und sie mit der [standardmäßigen DynamoDB-Client-API](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/package-summary.html) verglichen.

Wenn Sie mit der DynamoDB Enhanced Client API noch nicht vertraut sind, empfehlen wir Ihnen, das [Einführungstutorial](ddb-en-client-getting-started.md) zu lesen, um sich mit den grundlegenden Klassen vertraut zu machen.

## DynamoDB-Elemente in Java
<a name="ddb-en-client-use-usecase"></a>

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](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean) oder verwenden Sie einen [Builder](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-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` `DocumentTableSchema`ist Teil der [Enhanced Document API](ddb-en-client-doc-api.md) 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.](ddb-en-client-doc-api.md)

## Attributtypen für Datenmodellklassen
<a name="ddb-en-client-use-types"></a>

Obwohl DynamoDB im Vergleich zum Rich-Type-System [von Java eine geringe Anzahl von Attributtypen](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes) 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](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/BigDecimalAttributeConverter.html)](https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html), [String](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html) und Instant.](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/InstantAsStringAttributeConverter.html) Die Liste wird in den [bekannten Implementierungsklassen der AttributeConverter Schnittstelle](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverter.html) 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](ddb-en-client-adv-features-conversion.md#ddb-en-client-adv-features-conversion-example) 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](ddb-en-client-use-immut.md)) 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](ddb-en-client-adv-features-nested.md#ddb-en-client-adv-features-nested-map-anno) 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](ddb-en-client-adv-features-nested.md#ddb-en-client-adv-features-nested-map-builder), das die Verwendung verschachtelter Schemas zeigt.

### Null-Werte
<a name="ddb-en-client-use-types-nulls"></a>

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

Das Standardverhalten des SDK für `updateItem` Anfragen entfernt Attribute aus dem Element in DynamoDB, die in dem Objekt, das Sie in der Methode einreichen, auf Null gesetzt sind. `updateItem` Wenn Sie beabsichtigen, einige Attributwerte zu aktualisieren und die anderen unverändert zu lassen, haben Sie zwei Möglichkeiten.
+ Rufen Sie das Element (mithilfe von`getItem`) ab, bevor Sie Änderungen an den Werten vornehmen. Mit diesem Ansatz übermittelt das SDK alle aktualisierten und alten Werte an DynamoDB.
+ Verwenden Sie entweder `[IgnoreNullsMode](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/IgnoreNullsMode.html).SCALAR_ONLY` oder, `IgnoreNullsMode.MAPS_ONLY` wenn Sie die Anfrage erstellen, um das Element zu aktualisieren. Beide Modi ignorieren nullwertige Eigenschaften im Objekt, die skalare Attribute in DynamoDB darstellen. Das [Aktualisieren Sie Elemente, die komplexe Typen enthalten](ddb-en-client-adv-features-nested.md#ddb-en-client-adv-features-nested-updates) Thema in diesem Handbuch enthält weitere Informationen zu den `IgnoreNullsMode` Werten und zur Arbeit mit komplexen Typen.

Das folgende `ignoreNullsMode()` Beispiel zeigt die `updateItem()` Methode.

```
    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]
```

## Grundlegende Methoden des DynamoDB Enhanced Client
<a name="ddb-en-client-use-basic-ops"></a>

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](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust) 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
<a name="ddb-en-client-use-compare"></a>

Sowohl der [standardmäßige](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/package-summary.html) als auch der [erweiterte](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/package-summary.html) DynamoDB-Client APIs ermöglichen es Ihnen, mit DynamoDB-Tabellen zu arbeiten, um CRUD-Operationen (Create, Read, Update and Delete) auf Datenebene durchzuführen. Der Unterschied zwischen den Clients besteht darin, wie dies erreicht wird. APIs 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.

Während beide Clients Operationen auf Datenebene APIs unterstützen, unterstützt der standardmäßige DynamoDB-Client 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 Clients zu veranschaulichen APIs, zeigen die folgenden Codebeispiele die Erstellung derselben `ProductCatalog` Tabelle mit dem Standard-Client und dem erweiterten Client.

### Vergleichen: Erstellen Sie eine Tabelle mit dem standardmäßigen DynamoDB-Client
<a name="ddb-en-client-use-compare-cs1"></a>

```
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
<a name="ddb-en-client-use-compare-cs2"></a>

```
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. `ProductCatalog`ist 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](ddb-en-client-use-immut.md).

### `ProductCatalog`-Klasse
<a name="ddb-en-client-use-compare-cs3"></a>

```
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.

### Vergleich: Batch-Schreiben mit dem standardmäßigen DynamoDB-Client
<a name="ddb-en-client-use-compare-cs4"></a>

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

### Vergleich: Batch-Schreiben mit dem DynamoDB Enhanced Client
<a name="ddb-en-client-use-compare-cs5"></a>

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

# Arbeiten Sie mit unveränderlichen Datenklassen
<a name="ddb-en-client-use-immut"></a>

Die Mapping-Funktion der DynamoDB Enhanced Client API funktioniert mit unveränderlichen Datenklassen. Eine unveränderliche Klasse hat nur Getter und erfordert eine Builder-Klasse, die das SDK verwendet, um Instances der Klasse zu erstellen. Anstatt die `@DynamoDbBean` Annotation zu verwenden, wie in der [Customer-Klasse](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust) gezeigt, verwenden unveränderliche Klassen die `@DynamoDbImmutable` Annotation, die einen Parameter verwendet, der angibt, welche Builder-Klasse verwendet werden soll.

Die folgende Klasse ist eine unveränderliche Version von. `Customer`

```
package org.example.tests.model.immutable;

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.DynamoDbSecondarySortKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;

import java.time.Instant;

@DynamoDbImmutable(builder = CustomerImmutable.Builder.class)
public class CustomerImmutable {
    private final String id;
    private final String name;
    private final String email;
    private final Instant regDate;

    private CustomerImmutable(Builder b) {
        this.id = b.id;
        this.email = b.email;
        this.name = b.name;
        this.regDate = b.regDate;
    }

    // This method will be automatically discovered and used by the TableSchema.
    public static Builder builder() { return new Builder(); }

    @DynamoDbPartitionKey
    public String id() { return this.id; }

    @DynamoDbSortKey
    public String email() { return this.email; }

    @DynamoDbSecondaryPartitionKey(indexNames = "customers_by_name")
    public String name() { return this.name; }

    @DynamoDbSecondarySortKey(indexNames = {"customers_by_date", "customers_by_name"})
    public Instant regDate() { return this.regDate; }

    public static final class Builder {
        private String id;
        private String email;
        private String name;
        private Instant regDate;

        // The private Builder constructor is visible to the enclosing CustomerImmutable class.
        private Builder() {}

        public Builder id(String id) { this.id = id; return this; }
        public Builder email(String email) { this.email = email; return this; }
        public Builder name(String name) { this.name = name; return this; }
        public Builder regDate(Instant regDate) { this.regDate = regDate; return this; }

        // This method will be automatically discovered and used by the TableSchema.
        public CustomerImmutable build() { return new CustomerImmutable(this); }
    }
}
```

Sie müssen die folgenden Anforderungen erfüllen, wenn Sie eine Datenklasse mit annotieren. `@DynamoDbImmutable`

1. Jede Methode, bei der es sich nicht um eine Überschreibung von handelt `Object.class` und die nicht mit einer Anmerkung versehen wurde, `@DynamoDbIgnore` muss ein Getter für ein Attribut der DynamoDB-Tabelle sein.

1. Jeder Getter muss einen entsprechenden Setter in der Builder-Klasse haben, bei der Groß- und Kleinschreibung berücksichtigt wird.

1. Nur eine der folgenden Konstruktionsbedingungen muss erfüllt sein.
   + Die Builder-Klasse muss über einen öffentlichen Standardkonstruktor verfügen.
   + Die Datenklasse muss eine öffentliche statische Methode mit dem Namen haben`builder()`, die keine Parameter akzeptiert und eine Instanz der Builder-Klasse zurückgibt. Diese Option wird in der unveränderlichen `Customer` Klasse angezeigt.

1.  Die Builder-Klasse muss eine öffentliche Methode mit dem Namen haben`build()`, die keine Parameter akzeptiert und eine Instanz der unveränderlichen Klasse zurückgibt. 

Um eine `TableSchema` für Ihre unveränderliche Klasse zu erstellen, verwenden Sie die `fromImmutableClass()` Methode on, `TableSchema` wie im folgenden Codeausschnitt gezeigt.

```
static final TableSchema<CustomerImmutable> customerImmutableTableSchema = 
                         TableSchema.fromImmutableClass(CustomerImmutable.class);
```

So wie Sie eine DynamoDB-Tabelle aus einer veränderbaren Klasse erstellen können, können Sie eine aus einer unveränderlichen Klasse mit einem *einmaligen* Aufruf von `createTable()` of erstellen, `DynamoDbTable` wie im folgenden Codefragmentbeispiel gezeigt.

```
static void createTableFromImmutable(DynamoDbEnhancedClient enhancedClient, String tableName, DynamoDbWaiter waiter){
    // First, create an in-memory representation of the table using the 'table()' method of the DynamoDb Enhanced Client.
    // 'table()' accepts a name for the table and a TableSchema instance that you created previously.
    DynamoDbTable<CustomerImmutable> customerDynamoDbTable = enhancedClient
            .table(tableName, TableSchema.fromImmutableClass(CustomerImmutable.class));
        
    // Second, call the 'createTable()' method on the DynamoDbTable instance.
    customerDynamoDbTable.createTable();
    waiter.waitUntilTableExists(b -> b.tableName(tableName));
}
```

## Verwenden Sie Bibliotheken von Drittanbietern wie Lombok
<a name="ddb-en-client-use-immut-lombok"></a>

Bibliotheken von Drittanbietern, wie [Project Lombok](https://projectlombok.org/), helfen dabei, Standardcode zu generieren, der unveränderlichen Objekten zugeordnet ist. Die DynamoDB Enhanced Client API funktioniert mit diesen Bibliotheken, solange die Datenklassen den in diesem Abschnitt beschriebenen Konventionen entsprechen. 

Das folgende Beispiel zeigt die unveränderliche `CustomerImmutable` Klasse mit Lombok-Anmerkungen. Beachten Sie, wie die `onMethod` Funktion von Lombok attributbasierte DynamoDB-Anmerkungen, wie z. B., in den generierten Code kopiert. `@DynamoDbPartitionKey`

```
@Value
@Builder
@DynamoDbImmutable(builder = Customer.CustomerBuilder.class)
public class Customer {
    @Getter(onMethod_=@DynamoDbPartitionKey)
    private String id;

    @Getter(onMethod_=@DynamoDbSortKey)
    private String email;

    @Getter(onMethod_=@DynamoDbSecondaryPartitionKey(indexNames = "customers_by_name"))
    private String name;

    @Getter(onMethod_=@DynamoDbSecondarySortKey(indexNames = {"customers_by_date", "customers_by_name"}))
    private Instant createdDate;
}
```

# Verwenden Sie Ausdrücke und Bedingungen
<a name="ddb-en-client-expressions"></a>

Ausdrücke in der DynamoDB Enhanced Client API sind Java-Repräsentationen von [DynamoDB-Ausdrücken](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.html).

Die DynamoDB Enhanced Client API verwendet drei Arten von Ausdrücken:

[Expression](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Expression.html)  
Die `Expression` Klasse wird verwendet, wenn Sie Bedingungen und Filter definieren.

[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html)  
Dieser Ausdruckstyp stellt [wichtige Bedingungen](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.KeyConditionExpressions) für Abfrageoperationen dar.

[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/update/UpdateExpression.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/update/UpdateExpression.html)  
Diese Klasse hilft Ihnen beim Schreiben von [DynamoDB-Aktualisierungsausdrücken](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html) und wird derzeit im Erweiterungs-Framework verwendet, wenn Sie ein Element aktualisieren.

## Anatomie des Ausdrucks
<a name="ddb-en-client-expressions-compoonents"></a>

Ein Ausdruck setzt sich wie folgt zusammen:
+ Ein Zeichenkettenausdruck (erforderlich). Die Zeichenfolge enthält einen DynamoDB-Logikausdruck mit Platzhalternamen für Attributnamen und Attributwerte.
+ Eine Zuordnung von Ausdruckswerten (normalerweise erforderlich).
+ Eine Zuordnung von Ausdrucksnamen (optional).

Verwenden Sie einen Builder, um ein `Expression` Objekt zu generieren, das die folgende allgemeine Form annimmt.

```
Expression expression = Expression.builder()
                            .expression(<String>)
                            .expressionNames(<Map>)
                            .expressionValues(<Map>)
                           .build()
```

`Expression`s erfordern normalerweise eine Zuordnung von Ausdruckswerten. Die Map stellt die Werte für die Platzhalter im Zeichenkettenausdruck bereit. Der Map-Schlüssel besteht aus dem Platzhalternamen, dem ein Doppelpunkt (`:`) vorangestellt ist, und der Zuordnungswert ist eine Instanz von. [AttributeValue](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/AttributeValue.html) Die [AttributeValues](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/AttributeValues.html)Klasse verfügt über praktische Methoden zum Generieren einer `AttributeValue` Instanz aus einem Literal. Alternativ können Sie die verwenden, `AttributeValue.Builder` um eine `AttributeValue` Instanz zu generieren.

Das folgende Snippet zeigt eine Map mit zwei Einträgen nach Kommentarzeile 2. Die an die `expression()` Methode übergebene Zeichenfolge, die nach Kommentarzeile 1 angezeigt wird, enthält die Platzhalter, die DynamoDB vor der Ausführung des Vorgangs auflöst. Dieser Ausschnitt enthält keine Zuordnung von Ausdrucksnamen, da *Preis* ein zulässiger Attributname ist.

```
    public static void scanAsync(DynamoDbAsyncTable productCatalog) {
        ScanEnhancedRequest request = ScanEnhancedRequest.builder()
                .consistentRead(true)
                .attributesToProject("id", "title", "authors", "price")
                .filterExpression(Expression.builder()
                        // 1. :min_value and :max_value are placeholders for the values provided by the map
                        .expression("price >= :min_value AND price <= :max_value")
                        // 2. Two values are needed for the expression and each is supplied as a map entry.
                        .expressionValues(
                                Map.of( ":min_value", numberValue(8.00),
                                        ":max_value", numberValue(400_000.00)))
                        .build())
                .build();
```

Wenn ein Attributname in der DynamoDB-Tabelle ein reserviertes Wort ist, mit einer Zahl beginnt oder ein Leerzeichen enthält, ist eine Zuordnung von Ausdrucksnamen für erforderlich. `Expression`

Wenn der Attributname beispielsweise `1price` anstelle von `price` im vorherigen Codebeispiel verwendet wurde, müsste das Beispiel wie im folgenden Beispiel geändert werden.

```
        ScanEnhancedRequest request = ScanEnhancedRequest.builder()
                .filterExpression(Expression.builder()
                        .expression("#price >= :min_value AND #price <= :max_value")
                        .expressionNames( Map.of("#price", "1price") )
                        .expressionValues(
                                Map.of(":min_value", numberValue(8.00),
                                        ":max_value", numberValue(400_000.00)))
                        .build())
                .build();
```

Ein Platzhalter für einen Ausdrucksnamen beginnt mit dem Rautenzeichen (`#`). Ein Eintrag für die Zuordnung von Ausdrucksnamen verwendet den Platzhalter als Schlüssel und den Attributnamen als Wert. Die Map wird dem Ausdrucks-Generator mit der `expressionNames()` Methode hinzugefügt. DynamoDB löst den Attributnamen auf, bevor es den Vorgang ausführt.

Ausdruckswerte sind nicht erforderlich, wenn eine Funktion im Zeichenkettenausdruck verwendet wird. Ein Beispiel für eine Ausdrucksfunktion ist`attribute_exists(<attribute_name>)`.

Im folgenden Beispiel wird eine erstellt`Expression`, die eine [DynamoDB-Funktion](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions) verwendet. Die Ausdruckszeichenfolge in diesem Beispiel verwendet keine Platzhalter. Dieser Ausdruck könnte bei einer `putItem` Operation verwendet werden, um zu überprüfen, ob in der Datenbank bereits ein Element vorhanden ist, dessen Wert dem `movie` Attribut des Datenobjekts `movie` entspricht.

```
Expression exp = Expression.builder().expression("attribute_not_exists (movie)").build();
```

Das DynamoDB Developer Guide enthält vollständige Informationen zu den [Low-Level-Ausdrücken, die mit](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.html) DynamoDB verwendet werden.

## Bedingungsausdrücke und Bedingungen
<a name="ddb-en-client-expressions-cond"></a>

Wenn Sie die `deleteItem()` Methoden `putItem()``updateItem()`, und sowie Transaktions- und Batchoperationen verwenden, verwenden Sie `[Expression](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Expression.html)` Objekte, um Bedingungen anzugeben, die DynamoDB erfüllen muss, um mit dem Vorgang fortzufahren. Diese Ausdrücke sind benannte Bedingungsausdrücke. Ein Beispiel finden Sie in dem Bedingungsausdruck, der in der `addDeleteItem()` Methode (nach Kommentarzeile 1) des [Transaktionsbeispiels](ddb-en-client-use-multiop-trans.md#ddb-en-client-use-multiop-trans-writeitems-opcondition) in dieser Anleitung verwendet wurde.

Wenn Sie mit den `query()` Methoden arbeiten, wird eine Bedingung als ausgedrückt [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html). Die `QueryConditional` Klasse verfügt über mehrere statische praktische Methoden, mit deren Hilfe Sie die Kriterien schreiben können, die bestimmen, welche Elemente aus DynamoDB gelesen werden sollen.

Beispiele `QueryConditionals` dafür finden Sie im ersten Codebeispiel des [`Query`Beispiele für Methoden](ddb-en-client-use-multirecord.md#ddb-en-client-use-multirecord-query-example) Abschnitts dieses Handbuchs.

## Filterausdrücke
<a name="ddb-en-client-expressions-filter"></a>

Filterausdrücke werden bei Scan- und Abfragevorgängen verwendet, um die zurückgegebenen Elemente zu filtern. 

Ein Filterausdruck wird angewendet, nachdem alle Daten aus der Datenbank gelesen wurden, sodass die Lesekosten dieselben sind, als ob es keinen Filter gäbe. Im *Amazon DynamoDB Developer Guide* finden Sie weitere Informationen zur Verwendung von Filterausdrücken für [Abfrage](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.FilterExpression) - und [Scanvorgänge](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.FilterExpression).

Das folgende Beispiel zeigt einen Filterausdruck, der einer Scananforderung hinzugefügt wurde. Die Kriterien beschränken die Anzahl der zurückgesendeten Artikel auf Artikel mit einem Preis zwischen 8,00 und einschließlich 80,00€.

```
        Map<String, AttributeValue> expressionValues = Map.of(
                ":min_value", numberValue(8.00),
                ":max_value", numberValue(80.00));

        ScanEnhancedRequest request = ScanEnhancedRequest.builder()
                .consistentRead(true)
                // 1. the 'attributesToProject()' method allows you to specify which values you want returned.
                .attributesToProject("id", "title", "authors", "price")
                // 2. Filter expression limits the items returned that match the provided criteria.
                .filterExpression(Expression.builder()
                        .expression("price >= :min_value AND price <= :max_value")
                        .expressionValues(expressionValues)
                        .build())
                .build();
```

## Aktualisierungsausdrücke
<a name="ddb-en-client-expressions-update"></a>

Die Methode des DynamoDB Enhanced Client bietet eine `updateItem()` Standardmethode zum Aktualisieren von Elementen in DynamoDB. Wenn Sie jedoch mehr Funktionen benötigen, [UpdateExpressions](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/update/UpdateExpression.html)stellen Sie eine typsichere Darstellung der Syntax von [DynamoDB-Aktualisierungsausdrücken](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html) bereit. Sie können es beispielsweise verwenden, um Werte `UpdateExpressions` zu erhöhen, ohne zuerst Elemente aus DynamoDB zu lesen, oder um einzelne Mitglieder zu einer Liste hinzuzufügen. Aktualisierungsausdrücke sind derzeit in benutzerdefinierten Erweiterungen für die `updateItem()` Methode verfügbar.

Ein Beispiel, das Aktualisierungsausdrücke verwendet, finden Sie im [Beispiel für eine benutzerdefinierte Erweiterung](ddb-en-client-extensions-custom.md) in diesem Handbuch.

Weitere Informationen zu Aktualisierungsausdrücken finden Sie im [Amazon DynamoDB Developer Guide](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html).

# Arbeiten Sie mit paginierten Ergebnissen: Scans und Abfragen
<a name="ddb-en-client-use-multirecord"></a>

*Die `scan` `batch` Methoden `query` und der DynamoDB Enhanced Client API geben Antworten mit einer oder mehreren Seiten zurück.* Eine Seite enthält ein oder mehrere Elemente. Ihr Code kann die Antwort pro Seite oder einzelne Elemente verarbeiten.

Eine vom synchronen Client zurückgegebene paginierte Antwort gibt ein [PageIterable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/PageIterable.html)Objekt zurück, wohingegen eine vom asynchronen `DynamoDbEnhancedClient` `DynamoDbEnhancedAsyncClient` Client zurückgegebene Antwort ein Objekt zurückgibt. [PagePublisher](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/PagePublisher.html)

Dieser Abschnitt befasst sich mit der Verarbeitung paginierter Ergebnisse und enthält Beispiele für die Verwendung von Scan und Abfrage. APIs

## Scannen einer Tabelle
<a name="ddb-en-client-use-multirecord-scan"></a>

Die [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbAsyncTable.html#scan(java.util.function.Consumer)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbAsyncTable.html#scan(java.util.function.Consumer))Methode des SDK entspricht der gleichnamigen [DynamoDB-Operation](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html). Die DynamoDB Enhanced Client API bietet dieselben Optionen, verwendet jedoch ein vertrautes Objektmodell und übernimmt die Paginierung für Sie.

Zunächst untersuchen wir die `PageIterable` Schnittstelle, indem wir uns die `scan` Methode der synchronen Mapping-Klasse ansehen. [DynamoDbTable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html)

### Verwenden Sie die synchrone API
<a name="ddb-en-client-use-multirecord-scan-sync"></a>

Das folgende Beispiel zeigt die `scan` Methode, die einen [Ausdruck](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Expression.html) verwendet, um die zurückgegebenen Elemente zu filtern. Das [ProductCatalog](ddb-en-client-use.md#ddb-en-client-use-compare-cs3)ist das Modellobjekt, das zuvor gezeigt wurde.

Der nach Kommentarzeile 2 angezeigte Filterausdruck beschränkt die Anzahl der zurückgegebenen `ProductCatalog` Artikel auf Artikel mit einem Preiswert zwischen 8,00 und 80,00 (einschließlich).

In diesem Beispiel werden auch die `isbn` Werte ausgeschlossen, indem die `attributesToProject` Methode verwendet wird, die nach Kommentarzeile 1 gezeigt wird.

Nach Kommentarzeile 3 wird das `PageIterable` Objekt,`pagedResults`, von der `scan` Methode zurückgegeben. Die `stream` Methode von `PageIterable` gibt ein [https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html)Objekt zurück, mit dem Sie die Seiten bearbeiten können. In diesem Beispiel wird die Anzahl der Seiten gezählt und protokolliert.

Beginnend mit Kommentarzeile 4 zeigt das Beispiel zwei Varianten des Zugriffs auf die `ProductCatalog` Elemente. Die Version nach der Kommentarzeile 4a durchläuft jede Seite und sortiert und protokolliert die Elemente auf jeder Seite. Die Version nach der Kommentarzeile 4b überspringt die Seiteniteration und greift direkt auf die Elemente zu.

Die `PageIterable` Schnittstelle bietet aufgrund ihrer beiden übergeordneten Schnittstellen — und — mehrere Möglichkeiten zur Verarbeitung von Ergebnissen. [https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html](https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html) `Iterable`bringt `forEach` die `spliterator` Methoden `iterator` und und `SdkIterable` bringt die `stream` Methode.

```
    public static void scanSync(DynamoDbTable<ProductCatalog> productCatalog) {

        Map<String, AttributeValue> expressionValues = Map.of(
                ":min_value", numberValue(8.00),
                ":max_value", numberValue(80.00));

        ScanEnhancedRequest request = ScanEnhancedRequest.builder()
                .consistentRead(true)
                // 1. the 'attributesToProject()' method allows you to specify which values you want returned.
                .attributesToProject("id", "title", "authors", "price")
                // 2. Filter expression limits the items returned that match the provided criteria.
                .filterExpression(Expression.builder()
                        .expression("price >= :min_value AND price <= :max_value")
                        .expressionValues(expressionValues)
                        .build())
                .build();

        // 3. A PageIterable object is returned by the scan method.
        PageIterable<ProductCatalog> pagedResults = productCatalog.scan(request);
        logger.info("page count: {}", pagedResults.stream().count());

        // 4. Log the returned ProductCatalog items using two variations.
        // 4a. This version sorts and logs the items of each page.
        pagedResults.stream().forEach(p -> p.items().stream()
                .sorted(Comparator.comparing(ProductCatalog::price))
                .forEach(
                        item -> logger.info(item.toString())
                ));
        // 4b. This version sorts and logs all items for all pages.
        pagedResults.items().stream()
                .sorted(Comparator.comparing(ProductCatalog::price))
                .forEach(
                        item -> logger.info(item.toString())
                );
    }
```

### Verwenden Sie die asynchrone API
<a name="ddb-en-client-use-multirecord-scan-async"></a>

Die asynchrone `scan` Methode gibt Ergebnisse als `PagePublisher` Objekt zurück. Die `PagePublisher` Schnittstelle verfügt über zwei `subscribe` Methoden, mit denen Sie Antwortseiten verarbeiten können. Eine `subscribe` Methode stammt von der `org.reactivestreams.Publisher` übergeordneten Schnittstelle. Um Seiten mit dieser ersten Option zu verarbeiten, übergeben Sie der `subscribe` Methode eine `[Subscriber](https://www.reactive-streams.org/reactive-streams-1.0.0-javadoc/org/reactivestreams/Subscriber.html)` Instanz. Das erste Beispiel, das folgt, zeigt die Verwendung der `subscribe` Methode.

Die zweite `subscribe` Methode stammt von der [SdkPublisher](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/SdkPublisher.html)Schnittstelle. Diese Version von `subscribe` akzeptiert [https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html](https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html)eher a als `Subscriber` a. Diese `subscribe` Methodenvariante wird im zweiten Beispiel gezeigt, das folgt.

Das folgende Beispiel zeigt die asynchrone Version der `scan` Methode, die denselben Filterausdruck wie im vorherigen Beispiel verwendet. 

Gibt nach Kommentarzeile 3 ein `PagePublisher` Objekt `DynamoDbAsyncTable.scan` zurück. In der nächsten Zeile erstellt der Code eine Instanz der `org.reactivestreams.Subscriber` Schnittstelle`ProductCatalogSubscriber`, die die vierte Zeile `PagePublisher` nach dem Kommentar abonniert.

Das `Subscriber` Objekt sammelt die `ProductCatalog` Elemente von jeder Seite in der `onNext` Methode nach der Kommentarzeile 8 im `ProductCatalogSubscriber` Klassenbeispiel. Die Elemente werden in der privaten `List` Variablen gespeichert und im aufrufenden Code mit der `ProductCatalogSubscriber.getSubscribedItems()` Methode aufgerufen. Dies wird nach Kommentarzeile 5 aufgerufen.

Nachdem die Liste abgerufen wurde, sortiert der Code alle `ProductCatalog` Artikel nach Preis und protokolliert jeden Artikel.

Die Klasse [CountDownLatch](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html)in der `ProductCatalogSubscriber` Klasse blockiert den aufrufenden Thread, bis alle Elemente zur Liste hinzugefügt wurden, bevor sie nach Kommentarzeile 5 weitermacht. 

```
    public static void scanAsync(DynamoDbAsyncTable productCatalog) {
        ScanEnhancedRequest request = ScanEnhancedRequest.builder()
                .consistentRead(true)
                .attributesToProject("id", "title", "authors", "price")
                .filterExpression(Expression.builder()
                        // 1. :min_value and :max_value are placeholders for the values provided by the map
                        .expression("price >= :min_value AND price <= :max_value")
                        // 2. Two values are needed for the expression and each is supplied as a map entry.
                        .expressionValues(
                                Map.of( ":min_value", numberValue(8.00),
                                        ":max_value", numberValue(400_000.00)))
                        .build())
                .build();

        // 3. A PagePublisher object is returned by the scan method.
        PagePublisher<ProductCatalog> pagePublisher = productCatalog.scan(request);
        ProductCatalogSubscriber subscriber = new ProductCatalogSubscriber();
        // 4. Subscribe the ProductCatalogSubscriber to the PagePublisher.
        pagePublisher.subscribe(subscriber);
        // 5. Retrieve all collected ProductCatalog items accumulated by the subscriber.
        subscriber.getSubscribedItems().stream()
                .sorted(Comparator.comparing(ProductCatalog::price))
                .forEach(item ->
                        logger.info(item.toString()));
        // 6. Use a Consumer to work through each page.
        pagePublisher.subscribe(page -> page
                        .items().stream()
                        .sorted(Comparator.comparing(ProductCatalog::price))
                        .forEach(item ->
                                logger.info(item.toString())))
                .join(); // If needed, blocks the subscribe() method thread until it is finished processing.
        // 7. Use a Consumer to work through each ProductCatalog item.
        pagePublisher.items()
                .subscribe(product -> logger.info(product.toString()))
                .exceptionally(failure -> {
                    logger.error("ERROR  - ", failure);
                    return null;
                })
                .join(); // If needed, blocks the subscribe() method thread until it is finished processing.
    }
```

```
    private static class ProductCatalogSubscriber implements Subscriber<Page<ProductCatalog>> {
        private CountDownLatch latch = new CountDownLatch(1);
        private Subscription subscription;
        private List<ProductCatalog> itemsFromAllPages = new ArrayList<>();

        @Override
        public void onSubscribe(Subscription sub) {
            subscription = sub;
            subscription.request(1L);
            try {
                latch.await(); // Called by main thread blocking it until latch is released.
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public void onNext(Page<ProductCatalog> productCatalogPage) {
            // 8. Collect all the ProductCatalog instances in the page, then ask the publisher for one more page.
            itemsFromAllPages.addAll(productCatalogPage.items());
            subscription.request(1L);
        }

        @Override
        public void onError(Throwable throwable) {
        }

        @Override
        public void onComplete() {
            latch.countDown(); // Call by subscription thread; latch releases.
        }

        List<ProductCatalog> getSubscribedItems() {
            return this.itemsFromAllPages;
        }
    }
```

Das folgende Codefragmentbeispiel verwendet die Version der `PagePublisher.subscribe` Methode, die eine Eingabe `Consumer` nach der Kommentarzeile 6 akzeptiert. Der Java-Lambda-Parameter verbraucht Seiten, die jedes Element weiterverarbeiten. In diesem Beispiel wird jede Seite verarbeitet und die Elemente auf jeder Seite werden sortiert und anschließend protokolliert.

```
        // 6. Use a Consumer to work through each page.
        pagePublisher.subscribe(page -> page
                        .items().stream()
                        .sorted(Comparator.comparing(ProductCatalog::price))
                        .forEach(item ->
                                logger.info(item.toString())))
                .join(); // If needed, blocks the subscribe() method thread until it is finished processing.
```

Die `items` Methode `PagePublisher` entpackt die Modellinstanzen, sodass Ihr Code die Elemente direkt verarbeiten kann. Dieser Ansatz wird im folgenden Snippet gezeigt.

```
        // 7. Use a Consumer to work through each ProductCatalog item.
        pagePublisher.items()
                .subscribe(product -> logger.info(product.toString()))
                .exceptionally(failure -> {
                    logger.error("ERROR  - ", failure);
                    return null;
                })
                .join(); // If needed, blocks the subscribe() method thread until it is finished processing.
```

## Tabellen abfragen
<a name="ddb-en-client-use-multirecord-query"></a>

Sie können den DynamoDB Enhanced Client verwenden, um Ihre Tabelle abzufragen und mehrere Elemente abzurufen, die bestimmten Kriterien entsprechen. Die [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html#query(software.amazon.awssdk.enhanced.dynamodb.model.QueryEnhancedRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html#query(software.amazon.awssdk.enhanced.dynamodb.model.QueryEnhancedRequest))Methode findet Elemente auf der Grundlage von Primärschlüsselwerten anhand der `@DynamoDbPartitionKey` und optionaler `@DynamoDbSortKey` Anmerkungen, die in Ihrer Datenklasse definiert sind.

Die `query()` Methode erfordert einen Partitionsschlüsselwert und akzeptiert optional Sortierschlüsselbedingungen, um die Ergebnisse weiter zu verfeinern. Wie die `scan` API geben Abfragen a `PageIterable` für synchrone Aufrufe und a `PagePublisher` für asynchrone Aufrufe zurück.

### `Query`Beispiele für Methoden
<a name="ddb-en-client-use-multirecord-query-example"></a>

Das folgende `query()` Methodencodebeispiel verwendet die `MovieActor` Klasse. Die Datenklasse definiert einen zusammengesetzten Primärschlüssel, der aus dem **`movie`**Attribut als Partitionsschlüssel und dem **`actor`**Attribut als Sortierschlüssel besteht. 

#### `MovieActor`-Klasse
<a name="ddb-en-client-use-movieactor-class"></a>

```
package org.example.tests.model;

import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbAttribute;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
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.DynamoDbSecondarySortKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;

import java.util.Objects;

@DynamoDbBean
public class MovieActor implements Comparable<MovieActor> {

    private String movieName;
    private String actorName;
    private String actingAward;
    private Integer actingYear;
    private String actingSchoolName;

    @DynamoDbPartitionKey
    @DynamoDbAttribute("movie")
    public String getMovieName() {
        return movieName;
    }

    public void setMovieName(String movieName) {
        this.movieName = movieName;
    }

    @DynamoDbSortKey
    @DynamoDbAttribute("actor")
    public String getActorName() {
        return actorName;
    }

    public void setActorName(String actorName) {
        this.actorName = actorName;
    }

    @DynamoDbSecondaryPartitionKey(indexNames = "acting_award_year")
    @DynamoDbAttribute("actingaward")
    public String getActingAward() {
        return actingAward;
    }

    public void setActingAward(String actingAward) {
        this.actingAward = actingAward;
    }

    @DynamoDbSecondarySortKey(indexNames = {"acting_award_year", "movie_year"})
    @DynamoDbAttribute("actingyear")
    public Integer getActingYear() {
        return actingYear;
    }

    public void setActingYear(Integer actingYear) {
        this.actingYear = actingYear;
    }

    @DynamoDbAttribute("actingschoolname")
    public String getActingSchoolName() {
        return actingSchoolName;
    }

    public void setActingSchoolName(String actingSchoolName) {
        this.actingSchoolName = actingSchoolName;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("MovieActor{");
        sb.append("movieName='").append(movieName).append('\'');
        sb.append(", actorName='").append(actorName).append('\'');
        sb.append(", actingAward='").append(actingAward).append('\'');
        sb.append(", actingYear=").append(actingYear);
        sb.append(", actingSchoolName='").append(actingSchoolName).append('\'');
        sb.append('}');
        return sb.toString();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MovieActor that = (MovieActor) o;
        return Objects.equals(movieName, that.movieName) && Objects.equals(actorName, that.actorName) && Objects.equals(actingAward, that.actingAward) && Objects.equals(actingYear, that.actingYear) && Objects.equals(actingSchoolName, that.actingSchoolName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(movieName, actorName, actingAward, actingYear, actingSchoolName);
    }

    @Override
    public int compareTo(MovieActor o) {
        if (this.movieName.compareTo(o.movieName) != 0){
            return this.movieName.compareTo(o.movieName);
        } else {
            return this.actorName.compareTo(o.actorName);
        }
    }
}
```

In den folgenden Codebeispielen werden die folgenden Elemente abgefragt.

#### Elemente in der `MovieActor` Tabelle
<a name="ddb-en-client-use-movieactor-items"></a>

```
MovieActor{movieName='movie01', actorName='actor0', actingAward='actingaward0', actingYear=2001, actingSchoolName='null'}
MovieActor{movieName='movie01', actorName='actor1', actingAward='actingaward1', actingYear=2001, actingSchoolName='actingschool1'}
MovieActor{movieName='movie01', actorName='actor2', actingAward='actingaward2', actingYear=2001, actingSchoolName='actingschool2'}
MovieActor{movieName='movie01', actorName='actor3', actingAward='actingaward3', actingYear=2001, actingSchoolName='null'}
MovieActor{movieName='movie01', actorName='actor4', actingAward='actingaward4', actingYear=2001, actingSchoolName='actingschool4'}
MovieActor{movieName='movie02', actorName='actor0', actingAward='actingaward0', actingYear=2002, actingSchoolName='null'}
MovieActor{movieName='movie02', actorName='actor1', actingAward='actingaward1', actingYear=2002, actingSchoolName='actingschool1'}
MovieActor{movieName='movie02', actorName='actor2', actingAward='actingaward2', actingYear=2002, actingSchoolName='actingschool2'}
MovieActor{movieName='movie02', actorName='actor3', actingAward='actingaward3', actingYear=2002, actingSchoolName='null'}
MovieActor{movieName='movie02', actorName='actor4', actingAward='actingaward4', actingYear=2002, actingSchoolName='actingschool4'}
MovieActor{movieName='movie03', actorName='actor0', actingAward='actingaward0', actingYear=2003, actingSchoolName='null'}
MovieActor{movieName='movie03', actorName='actor1', actingAward='actingaward1', actingYear=2003, actingSchoolName='actingschool1'}
MovieActor{movieName='movie03', actorName='actor2', actingAward='actingaward2', actingYear=2003, actingSchoolName='actingschool2'}
MovieActor{movieName='movie03', actorName='actor3', actingAward='actingaward3', actingYear=2003, actingSchoolName='null'}
MovieActor{movieName='movie03', actorName='actor4', actingAward='actingaward4', actingYear=2003, actingSchoolName='actingschool4'}
```

Der folgende Code definiert zwei `QueryConditional` Instanzen: `keyEqual` (nach Kommentarzeile 1) und `sortGreaterThanOrEqualTo` (nach Kommentarzeile 1a).

#### Fragen Sie Elemente anhand des Partitionsschlüssels ab
<a name="keyEqual-query-conditional-example"></a>

Die `keyEqual` Instanz gleicht Elementen mit einem Partitionsschlüsselwert von ab **`movie01`**. 

In diesem Beispiel wird auch ein Filterausdruck nach Kommentarzeile 2 definiert, der alle Elemente herausfiltert, die keinen **`actingschoolname`**Wert haben.

Der `QueryEnhancedRequest` kombiniert die Schlüsselbedingung und den Filterausdruck für die Abfrage.

```
    public static void query(DynamoDbTable movieActorTable) {

        // 1. Define a QueryConditional instance to return items matching a partition value.
        QueryConditional keyEqual = QueryConditional.keyEqualTo(b -> b.partitionValue("movie01"));
        // 1a. Define a QueryConditional that adds a sort key criteria to the partition value criteria.
        QueryConditional sortGreaterThanOrEqualTo = QueryConditional.sortGreaterThanOrEqualTo(b -> b.partitionValue("movie01").sortValue("actor2"));
        // 2. Define a filter expression that filters out items whose attribute value is null.
        final Expression filterOutNoActingschoolname = Expression.builder().expression("attribute_exists(actingschoolname)").build();

        // 3. Build the query request.
        QueryEnhancedRequest tableQuery = QueryEnhancedRequest.builder()
                .queryConditional(keyEqual)
                .filterExpression(filterOutNoActingschoolname)
                .build();
        // 4. Perform the query using the "keyEqual" conditional and filter expression.
        PageIterable<MovieActor> pagedResults = movieActorTable.query(tableQuery);
        logger.info("page count: {}", pagedResults.stream().count()); // Log  number of pages.

        pagedResults.items().stream()
                .sorted()
                .forEach(
                        item -> logger.info(item.toString()) // Log the sorted list of items.
                );
```

**Example — Ausgabe unter Verwendung der `keyEqual` Abfragebedingung**  
Das Folgende ist die Ausgabe der Ausführung der Methode. In der Ausgabe werden Elemente mit dem `movieName` Wert **movie01** und keine Elemente mit dem Wert `actingSchoolName` gleich angezeigt. **`null`**  

```
2023-03-05 13:11:05 [main] INFO  org.example.tests.QueryDemo:46 - page count: 1
2023-03-05 13:11:05 [main] INFO  org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor1', actingAward='actingaward1', actingYear=2001, actingSchoolName='actingschool1'}
2023-03-05 13:11:05 [main] INFO  org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor2', actingAward='actingaward2', actingYear=2001, actingSchoolName='actingschool2'}
2023-03-05 13:11:05 [main] INFO  org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor4', actingAward='actingaward4', actingYear=2001, actingSchoolName='actingschool4'}
```

#### Fragen Sie Elemente nach Partitionsschlüssel und Sortierschlüssel ab
<a name="sort-type-query-conditional-example"></a>

**Der `sortGreaterThanOrEqualTo` `QueryConditional` verfeinert die Übereinstimmung mit den Partitionsschlüsseln (**movie01**), indem eine Sortierschlüsselbedingung für Werte hinzugefügt wird, die größer oder gleich actor2 sind.**

[`QueryConditional`Methoden](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html), die mit beginnen, `sort` erfordern, dass ein Partitionsschlüsselwert übereinstimmt und die Abfrage durch einen Vergleich auf der Grundlage des Sortierschlüsselwerts weiter verfeinert wird. `Sort`bedeutet im Methodennamen nicht, dass die Ergebnisse sortiert sind, sondern dass ein Sortierschlüsselwert für den Vergleich verwendet wird.

Im folgenden Snippet ändern wir die Abfrageanfrage, die zuvor nach Kommentarzeile 3 angezeigt wurde. Dieses Snippet ersetzt die Abfragebedingung „keyEqual“ durch die Abfragebedingung "sortGreaterThanOrEqualTo", die nach der Kommentarzeile 1a definiert wurde. Der folgende Code entfernt auch den Filterausdruck.

```
        QueryEnhancedRequest tableQuery = QueryEnhancedRequest.builder()
                .queryConditional(sortGreaterThanOrEqualTo).build();
```

**Example — Ausgabe unter Verwendung der `sortGreaterThanOrEqualTo` Abfragebedingung**  
In der folgenden Ausgabe werden die Ergebnisse der Abfrage angezeigt. **Die Abfrage gibt Elemente zurück, deren `movieName` Wert **movie01** entspricht, und nur Elemente, deren `actorName` Wert größer oder gleich actor2 ist.** Da wir den Filter entfernen, gibt die Abfrage Elemente zurück, die keinen Wert für das Attribut haben. `actingSchoolName`  

```
2023-03-05 13:15:00 [main] INFO  org.example.tests.QueryDemo:46 - page count: 1
2023-03-05 13:15:00 [main] INFO  org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor2', actingAward='actingaward2', actingYear=2001, actingSchoolName='actingschool2'}
2023-03-05 13:15:00 [main] INFO  org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor3', actingAward='actingaward3', actingYear=2001, actingSchoolName='null'}
2023-03-05 13:15:00 [main] INFO  org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor4', actingAward='actingaward4', actingYear=2001, actingSchoolName='actingschool4'}
```

# Führen Sie Batch-Operationen durch
<a name="ddb-en-client-use-multiop-batch"></a>

Die DynamoDB Enhanced Client API bietet zwei Batch-Methoden, [`batchGetItem`()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#batchGetItem(java.util.function.Consumer)) und [`batchWriteItem`(](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#batchWriteItem(java.util.function.Consumer))).

## `batchGetItem()`Beispiel für
<a name="ddb-en-client-use-multiop-batch-get"></a>

Mit [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#batchGetItem(java.util.function.Consumer)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#batchGetItem(java.util.function.Consumer))dieser Methode können Sie bis zu 100 einzelne Elemente aus mehreren Tabellen in einer Gesamtanforderung abrufen. Im folgenden Beispiel werden die zuvor gezeigten [`MovieActor`](ddb-en-client-use-multirecord.md#ddb-en-client-use-movieactor-class)Datenklassen [`Customer`](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust)und verwendet.

Im Beispiel nach den Zeilen 1 und 2 erstellen Sie `[ReadBatch](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/ReadBatch.html)` Objekte, die Sie später nach Kommentarzeile 3 als Parameter `batchGetItem()` zur Methode hinzufügen. 

Der Code nach der ersten Kommentarzeile erstellt den Batch, der aus der `Customer` Tabelle gelesen werden soll. Der Code nach der Kommentarzeile 1a zeigt die Verwendung eines `[GetItemEnhancedRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/GetItemEnhancedRequest.Builder.html)` Builders, der einen Primärschlüsselwert und einen Sortierschlüsselwert verwendet, um das zu lesende Element anzugeben. Wenn die Datenklasse über einen zusammengesetzten Schlüssel verfügt, müssen Sie sowohl den Partitionsschlüsselwert als auch den Sortierschlüsselwert angeben. 

Im Gegensatz zur Angabe von Schlüsselwerten für die Anforderung eines Elements können Sie eine Datenklasse verwenden, um ein Element anzufordern, wie nach Kommentarzeile 1b gezeigt. Das SDK extrahiert die Schlüsselwerte hinter den Kulissen, bevor die Anfrage gesendet wird.

[Wenn Sie das Element mithilfe des schlüsselbasierten Ansatzes angeben, wie in den beiden Anweisungen nach 2a gezeigt, können Sie auch angeben, dass DynamoDB einen stark konsistenten Lesevorgang durchführen soll.](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html) Wenn die `consistentRead()` Methode verwendet wird, muss sie für alle angeforderten Elemente für dieselbe Tabelle verwendet werden.

Verwenden Sie die `[resultsForTable() ](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchGetResultPage.html#resultsForTable(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource))` Methode, die nach Kommentarzeile 4 angezeigt wird, um die von DynamoDB gefundenen Elemente abzurufen. Rufen Sie die Methode für jede Tabelle auf, die in der Anforderung gelesen wurde. `resultsForTable()`gibt eine Liste der gefundenen Elemente zurück, die Sie mit einer beliebigen `java.util.List` Methode verarbeiten können. In diesem Beispiel wird jedes Element protokolliert.

Um Elemente zu finden, die DynamoDB nicht verarbeitet hat, verwenden Sie den Ansatz nach Kommentarzeile 5. Die `BatchGetResultPage` Klasse verfügt über die `[unprocessedKeysForTable()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchGetResultPage.html#unprocessedKeysForTable(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource))` Methode, mit der Sie auf jeden Schlüssel zugreifen können, der nicht verarbeitet wurde. Die [BatchGetItem API-Referenz](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchGetItem.html) enthält weitere Informationen zu Situationen, die zu unverarbeiteten Elementen führen.

```
    public static void batchGetItemExample(DynamoDbEnhancedClient enhancedClient,
                                           DynamoDbTable<Customer> customerTable,
                                           DynamoDbTable<MovieActor> movieActorTable) {

        Customer customer2 = new Customer();
        customer2.setId("2");
        customer2.setEmail("cust2@example.org");

        // 1. Build a batch to read from the Customer table.
        ReadBatch customerBatch = ReadBatch.builder(Customer.class)
                .mappedTableResource(customerTable)
                // 1a. Specify the primary key value and sort key value for the item.
                .addGetItem(b -> b.key(k -> k.partitionValue("1").sortValue("cust1@orgname.org")))
                // 1b. Alternatively, supply a data class instances to provide the primary key values.
                .addGetItem(customer2)
                .build();

        // 2. Build a batch to read from the MovieActor table.
        ReadBatch moveActorBatch = ReadBatch.builder(MovieActor.class)
                .mappedTableResource(movieActorTable)
                // 2a. Call consistentRead(Boolean.TRUE) for each item for the same table.
                .addGetItem(b -> b.key(k -> k.partitionValue("movie01").sortValue("actor1")).consistentRead(Boolean.TRUE))
                .addGetItem(b -> b.key(k -> k.partitionValue("movie01").sortValue("actor4")).consistentRead(Boolean.TRUE))
                .build();

        // 3. Add ReadBatch objects to the request.
        BatchGetResultPageIterable resultPages = enhancedClient.batchGetItem(b -> b.readBatches(customerBatch, moveActorBatch));

        // 4. Retrieve the successfully requested items from each table.
        resultPages.resultsForTable(customerTable).forEach(item -> logger.info(item.toString()));
        resultPages.resultsForTable(movieActorTable).forEach(item -> logger.info(item.toString()));

        // 5. Retrieve the keys of the items requested but not processed by the service.
        resultPages.forEach((BatchGetResultPage pageResult) -> {
            pageResult.unprocessedKeysForTable(customerTable).forEach(key -> logger.info("Unprocessed item key: " + key.toString()));
            pageResult.unprocessedKeysForTable(movieActorTable).forEach(key -> logger.info("Unprocessed item key: " + key.toString()));
        });
    }
```

Gehen Sie davon aus, dass sich die folgenden Elemente in den beiden Tabellen befinden, bevor Sie den Beispielcode ausführen.

### Elemente in Tabellen
<a name="ddb-en-client-use-multiop-batch-get-tableitems"></a>

```
Customer [id=1, name=CustName1, email=cust1@example.org, regDate=2023-03-31T15:46:27.688Z]
Customer [id=2, name=CustName2, email=cust2@example.org, regDate=2023-03-31T15:46:28.688Z]
Customer [id=3, name=CustName3, email=cust3@example.org, regDate=2023-03-31T15:46:29.688Z]
Customer [id=4, name=CustName4, email=cust4@example.org, regDate=2023-03-31T15:46:30.688Z]
Customer [id=5, name=CustName5, email=cust5@example.org, regDate=2023-03-31T15:46:31.689Z]
MovieActor{movieName='movie01', actorName='actor0', actingAward='actingaward0', actingYear=2001, actingSchoolName='null'}
MovieActor{movieName='movie01', actorName='actor1', actingAward='actingaward1', actingYear=2001, actingSchoolName='actingschool1'}
MovieActor{movieName='movie01', actorName='actor2', actingAward='actingaward2', actingYear=2001, actingSchoolName='actingschool2'}
MovieActor{movieName='movie01', actorName='actor3', actingAward='actingaward3', actingYear=2001, actingSchoolName='null'}
MovieActor{movieName='movie01', actorName='actor4', actingAward='actingaward4', actingYear=2001, actingSchoolName='actingschool4'}
```

Die folgende Ausgabe zeigt die zurückgegebenen und protokollierten Elemente nach Kommentarzeile 4.

```
Customer [id=1, name=CustName1, email=cust1@example.org, regDate=2023-03-31T15:46:27.688Z]
Customer [id=2, name=CustName2, email=cust2@example.org, regDate=2023-03-31T15:46:28.688Z]
MovieActor{movieName='movie01', actorName='actor4', actingAward='actingaward4', actingYear=2001, actingSchoolName='actingschool4'}
MovieActor{movieName='movie01', actorName='actor1', actingAward='actingaward1', actingYear=2001, actingSchoolName='actingschool1'}
```

## `batchWriteItem()`Beispiel für
<a name="ddb-en-client-use-multiop-batch-write"></a>

Die `batchWriteItem()` Methode fügt mehrere Elemente in eine oder mehrere Tabellen ein oder löscht sie. Sie können bis zu 25 einzelne Put- oder Löschvorgänge in der Anfrage angeben. Im folgenden Beispiel werden die zuvor gezeigten Klassen [`ProductCatalog`](ddb-en-client-use.md#ddb-en-client-use-compare-cs3)und [`MovieActor`](ddb-en-client-use-multirecord.md#ddb-en-client-use-movieactor-class)Modellklassen verwendet.

`WriteBatch`Objekte werden nach den Kommentarzeilen 1 und 2 erstellt. Für die `ProductCatalog` Tabelle fügt der Code ein Element ein und löscht ein Element. Für die `MovieActor` Tabelle nach Kommentarzeile 2 fügt der Code zwei Elemente ein und löscht eines.

Die `batchWriteItem` Methode wird nach Kommentarzeile 3 aufgerufen. Der `[builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchWriteItemEnhancedRequest.Builder.html)` Parameter stellt die Batch-Anfragen für jede Tabelle bereit.

Das zurückgegebene `[BatchWriteResult](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchWriteResult.html)` Objekt bietet separate Methoden für jeden Vorgang, um unverarbeitete Anfragen anzuzeigen. Der Code nach der Kommentarzeile 4a stellt die Schlüssel für unverarbeitete Löschanfragen bereit und der Code nach der Kommentarzeile 4b stellt die unverarbeiteten PUT-Elemente bereit.

```
    public static void batchWriteItemExample(DynamoDbEnhancedClient enhancedClient,
                                             DynamoDbTable<ProductCatalog> catalogTable,
                                             DynamoDbTable<MovieActor> movieActorTable) {

        // 1. Build a batch to write to the ProductCatalog table.
        WriteBatch products = WriteBatch.builder(ProductCatalog.class)
                .mappedTableResource(catalogTable)
                .addPutItem(b -> b.item(getProductCatItem1()))
                .addDeleteItem(b -> b.key(k -> k
                        .partitionValue(getProductCatItem2().id())
                        .sortValue(getProductCatItem2().title())))
                .build();

        // 2. Build a batch to write to the MovieActor table.
        WriteBatch movies = WriteBatch.builder(MovieActor.class)
                .mappedTableResource(movieActorTable)
                .addPutItem(getMovieActorYeoh())
                .addPutItem(getMovieActorBlanchettPartial())
                .addDeleteItem(b -> b.key(k -> k
                        .partitionValue(getMovieActorStreep().getMovieName())
                        .sortValue(getMovieActorStreep().getActorName())))
                .build();

        // 3. Add WriteBatch objects to the request.
        BatchWriteResult batchWriteResult = enhancedClient.batchWriteItem(b -> b.writeBatches(products, movies));
        // 4. Retrieve keys for items the service did not process.
        // 4a. 'unprocessedDeleteItemsForTable()' returns keys for delete requests that did not process.
        if (batchWriteResult.unprocessedDeleteItemsForTable(movieActorTable).size() > 0) {
            batchWriteResult.unprocessedDeleteItemsForTable(movieActorTable).forEach(key ->
                    logger.info(key.toString()));
        }
        // 4b. 'unprocessedPutItemsForTable()' returns keys for put requests that did not process.
        if (batchWriteResult.unprocessedPutItemsForTable(catalogTable).size() > 0) {
            batchWriteResult.unprocessedPutItemsForTable(catalogTable).forEach(key ->
                    logger.info(key.toString()));
        }
    }
```

Die folgenden Hilfsmethoden stellen die Modellobjekte für die Put- und Delete-Operationen bereit.

### Hilfsmethoden
<a name="ddb-en-client-use-multiop-batch-write-helpers"></a>

```
 1.     public static ProductCatalog getProductCatItem1() {
 2.         return ProductCatalog.builder()
 3.                 .id(2)
 4.                 .isbn("1-565-85698")
 5.                 .authors(new HashSet<>(Arrays.asList("a", "b")))
 6.                 .price(BigDecimal.valueOf(30.22))
 7.                 .title("Title 55")
 8.                 .build();
 9.     }
10. 
11.     public static ProductCatalog getProductCatItem2() {
12.         return ProductCatalog.builder()
13.                 .id(4)
14.                 .price(BigDecimal.valueOf(40.00))
15.                 .title("Title 1")
16.                 .build();
17.     }  
18. 
19.     public static MovieActor getMovieActorBlanchettPartial() {
20.         MovieActor movieActor = new MovieActor();
21.         movieActor.setActorName("Cate Blanchett");
22.         movieActor.setMovieName("Blue Jasmine");
23.         movieActor.setActingYear(2023);
24.         movieActor.setActingAward("Best Actress");
25.         return movieActor;
26.     }
27. 
28.     public static MovieActor getMovieActorStreep() {
29.         MovieActor movieActor = new MovieActor();
30.         movieActor.setActorName("Meryl Streep");
31.         movieActor.setMovieName("Sophie's Choice");
32.         movieActor.setActingYear(1982);
33.         movieActor.setActingAward("Best Actress");
34.         movieActor.setActingSchoolName("Yale School of Drama");
35.         return movieActor;
36.     }
37. 
38.     public static MovieActor getMovieActorYeoh(){
39.         MovieActor movieActor = new MovieActor();
40.         movieActor.setActorName("Michelle Yeoh");
41.         movieActor.setMovieName("Everything Everywhere All at Once");
42.         movieActor.setActingYear(2023);
43.         movieActor.setActingAward("Best Actress");
44.         movieActor.setActingSchoolName("Royal Academy of Dance");
45.         return movieActor;
46.     }
```

Gehen Sie davon aus, dass die Tabellen die folgenden Elemente enthalten, bevor Sie den Beispielcode ausführen.

```
MovieActor{movieName='Blue Jasmine', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2013, actingSchoolName='National Institute of Dramatic Art'}
MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
```

Nach Abschluss des Beispielcodes enthalten die Tabellen die folgenden Elemente.

```
MovieActor{movieName='Blue Jasmine', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2013, actingSchoolName='null'}
MovieActor{movieName='Everything Everywhere All at Once', actorName='Michelle Yeoh', actingAward='Best Actress', actingYear=2023, actingSchoolName='Royal Academy of Dance'}
ProductCatalog{id=2, title='Title 55', isbn='1-565-85698', authors=[a, b], price=30.22}
```

Beachten Sie in der `MovieActor` Tabelle, dass das `Blue Jasmine` Filmelement durch das Element ersetzt wurde, das in der Put-Anfrage verwendet wurde, die mit der `getMovieActorBlanchettPartial()` Helper-Methode abgerufen wurde. Wenn kein Data-Bean-Attributwert angegeben wurde, wird der Wert in der Datenbank entfernt. Aus diesem Grund ist das Ergebnis `actingSchoolName` für das `Blue Jasmine` Filmelement Null.

**Anmerkung**  
In der API-Dokumentation wird zwar darauf hingewiesen, dass Bedingungsausdrücke verwendet werden können und dass verbrauchte Kapazität und Erfassungsmetriken mit einzelnen [Put](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/PutItemEnhancedRequest.html) - und [Löschanforderungen](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/DeleteItemEnhancedRequest.html) zurückgegeben werden können, dies ist jedoch in einem Batch-Write-Szenario nicht der Fall. Um die Leistung von Batch-Vorgängen zu verbessern, werden diese einzelnen Optionen ignoriert.

# Führen Sie Transaktionsoperationen durch
<a name="ddb-en-client-use-multiop-trans"></a>

Die DynamoDB Enhanced Client API stellt die `transactGetItems()` und die `transactWriteItems()` Methoden bereit. Die Transaktionsmethoden des SDK for Java sorgen für Atomizität, Konsistenz, Isolierung und Haltbarkeit (ACID) in DynamoDB-Tabellen und helfen Ihnen so, die Datenkorrektheit in Ihren Anwendungen aufrechtzuerhalten.

## `transactGetItems()`Beispiel für
<a name="ddb-en-client-use-multiop-trans-getitems"></a>

Die `[transactGetItems()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#transactGetItems(java.util.function.Consumer))` Methode akzeptiert bis zu 100 individuelle Anfragen für Elemente. Alle Elemente werden in einer einzigen atomaren Transaktion gelesen. Das *Amazon DynamoDB DynamoDB-Entwicklerhandbuch* enthält Informationen zu den [Bedingungen, die dazu führen, dass eine `transactGetItems()` Methode fehlschlägt](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-txgetitems), sowie über die Isolationsstufe, die beim Aufrufen verwendet wird. `[transactGetItem()](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-isolation)`

Nach Kommentarzeile 1 im folgenden Beispiel ruft der Code die `transactGetItems()` Methode mit einem `[builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactGetItemsEnhancedRequest.Builder.html)` Parameter auf. Der Builder `[addGetItem()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactGetItemsEnhancedRequest.Builder.html#addGetItem(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource,T))` wird dreimal mit einem Datenobjekt aufgerufen, das die Schlüsselwerte enthält, die das SDK verwendet, um die endgültige Anfrage zu generieren.

Die Anfrage gibt nach Kommentarzeile 2 eine Liste von `[Document](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Document.html)` Objekten zurück. Die zurückgegebene Dokumentenliste enthält [Dokumentinstanzen](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Document.html) von Elementdaten, die nicht Null sind, und zwar in derselben Reihenfolge wie angefordert. Die `[Document.getItem(MappedTableResource<T> mappedTableResource)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Document.html#getItem(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource))` Methode konvertiert ein untypisiertes `Document` Objekt in ein typisiertes Java-Objekt, wenn Elementdaten zurückgegeben wurden, andernfalls gibt die Methode Null zurück.

```
    public static void transactGetItemsExample(DynamoDbEnhancedClient enhancedClient,
                                               DynamoDbTable<ProductCatalog> catalogTable,
                                               DynamoDbTable<MovieActor> movieActorTable) {

        // 1. Request three items from two tables using a builder.
        final List<Document> documents = enhancedClient.transactGetItems(b -> b
                .addGetItem(catalogTable, Key.builder().partitionValue(2).sortValue("Title 55").build())
                .addGetItem(movieActorTable, Key.builder().partitionValue("Sophie's Choice").sortValue("Meryl Streep").build())
                .addGetItem(movieActorTable, Key.builder().partitionValue("Blue Jasmine").sortValue("Cate Blanchett").build())
                .build());

        // 2. A list of Document objects is returned in the same order as requested.
        ProductCatalog title55 = documents.get(0).getItem(catalogTable);
        if (title55 != null) {
            logger.info(title55.toString());
        }

        MovieActor sophiesChoice = documents.get(1).getItem(movieActorTable);
        if (sophiesChoice != null) {
            logger.info(sophiesChoice.toString());
        }

        // 3. The getItem() method returns null if the Document object contains no item from DynamoDB.
        MovieActor blueJasmine = documents.get(2).getItem(movieActorTable);
        if (blueJasmine != null) {
            logger.info(blueJasmine.toString());
        }
    }
```

Die DynamoDB-Tabellen enthalten die folgenden Elemente, bevor das Codebeispiel ausgeführt wird.

```
ProductCatalog{id=2, title='Title 55', isbn='orig_isbn', authors=[b, g], price=10}
MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
```

Die folgende Ausgabe wird protokolliert. Wenn ein Element angefordert, aber nicht gefunden wird, wird es nicht zurückgegeben, wie es bei der Anfrage für den genannten Film der Fall ist`Blue Jasmine`.

```
ProductCatalog{id=2, title='Title 55', isbn='orig_isbn', authors=[b, g], price=10}
MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
```

## Beispiele für `transactWriteItems()`
<a name="ddb-en-client-use-multiop-trans-writeitems"></a>

Der `[transactWriteItems()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#transactWriteItems(java.util.function.Consumer))` akzeptiert bis zu 100 Put-, Aktualisierungs- oder Löschaktionen in einer einzigen atomaren Transaktion über mehrere Tabellen hinweg. Das *Amazon DynamoDB DynamoDB-Entwicklerhandbuch* enthält Einzelheiten zu Einschränkungen und Fehlerbedingungen des [zugrunde liegenden DynamoDB-Servicebetriebs](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-txwriteitems).

### Einfaches Beispiel
<a name="ddb-en-client-use-multiop-trans-writeitems-basic"></a>

Im folgenden Beispiel werden vier Operationen für zwei Tabellen angefordert. Die entsprechenden Modellklassen [`ProductCatalog`](ddb-en-client-use.md#ddb-en-client-use-compare-cs3)und [`MovieActor`](ddb-en-client-use-multirecord.md#ddb-en-client-use-movieactor-class)wurden bereits gezeigt.

Jede der drei möglichen Operationen — Put, Update und Delete — verwendet einen speziellen Anforderungsparameter, um die Details anzugeben. 

Der Code nach der ersten Kommentarzeile zeigt die einfache Variante der Methode. `addPutItem()` Die Methode akzeptiert ein `[MappedTableResource](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/MappedTableResource.html)` Objekt und die zu setzende Datenobjektinstanz. Die Anweisung nach Kommentarzeile 2 zeigt die Variante, die eine `[TransactPutItemEnhancedRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactPutItemEnhancedRequest.html)` Instanz akzeptiert. Mit dieser Variante können Sie der Anfrage weitere Optionen hinzufügen, z. B. einen Bedingungsausdruck. Ein nachfolgendes [Beispiel](#ddb-en-client-use-multiop-trans-writeitems-opcondition) zeigt einen Bedingungsausdruck für eine einzelne Operation.

Nach Kommentarzeile 3 wird ein Aktualisierungsvorgang angefordert. `[TransactUpdateItemEnhancedRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactUpdateItemEnhancedRequest.Builder.html)`hat eine `ignoreNulls()` Methode, mit der Sie konfigurieren können, was das SDK mit `null` Werten im Modellobjekt macht. Wenn die `ignoreNulls()` Methode true zurückgibt, entfernt das SDK nicht die Attributwerte der Tabelle für Datenobjektattribute, die `null` Wenn die `ignoreNulls()` Methode false zurückgibt, fordert das SDK den DynamoDB-Dienst auf, die Attribute aus dem Element in der Tabelle zu entfernen. Der Standardwert für `ignoreNulls` ist False.

Die Aussage nach Kommentarzeile 4 zeigt die Variante einer Löschanforderung, die ein Datenobjekt akzeptiert. Der erweiterte Client extrahiert die Schlüsselwerte, bevor er die endgültige Anfrage sendet.

```
    public static void transactWriteItems(DynamoDbEnhancedClient enhancedClient,
                                          DynamoDbTable<ProductCatalog> catalogTable,
                                          DynamoDbTable<MovieActor> movieActorTable) {

        enhancedClient.transactWriteItems(b -> b
                // 1. Simplest variation of put item request.
                .addPutItem(catalogTable, getProductCatId2())
                // 2. Put item request variation that accommodates condition expressions.
                .addPutItem(movieActorTable, TransactPutItemEnhancedRequest.builder(MovieActor.class)
                        .item(getMovieActorStreep())
                        .conditionExpression(Expression.builder().expression("attribute_not_exists (movie)").build())
                        .build())
                // 3. Update request that does not remove attribute values on the table if the data object's value is null.
                .addUpdateItem(catalogTable, TransactUpdateItemEnhancedRequest.builder(ProductCatalog.class)
                        .item(getProductCatId4ForUpdate())
                        .ignoreNulls(Boolean.TRUE)
                        .build())
                // 4. Variation of delete request that accepts a data object. The key values are extracted for the request.
                .addDeleteItem(movieActorTable, getMovieActorBlanchett())
        );
    }
```

Die folgenden Hilfsmethoden stellen die Datenobjekte für die `add*Item` Parameter bereit.

#### Hilfsmethoden
<a name="ddb-en-client-use-multiop-trans-writeitems-basic-helpers"></a>

```
    public static ProductCatalog getProductCatId2() {
        return ProductCatalog.builder()
                .id(2)
                .isbn("1-565-85698")
                .authors(new HashSet<>(Arrays.asList("a", "b")))
                .price(BigDecimal.valueOf(30.22))
                .title("Title 55")
                .build();
    }

    public static ProductCatalog getProductCatId4ForUpdate() {
        return ProductCatalog.builder()
                .id(4)
                .price(BigDecimal.valueOf(40.00))
                .title("Title 1")
                .build();
    }

    public static MovieActor getMovieActorBlanchett() {
        MovieActor movieActor = new MovieActor();
        movieActor.setActorName("Cate Blanchett");
        movieActor.setMovieName("Tar");
        movieActor.setActingYear(2022);
        movieActor.setActingAward("Best Actress");
        movieActor.setActingSchoolName("National Institute of Dramatic Art");
        return movieActor;
    }

    public static MovieActor getMovieActorStreep() {
        MovieActor movieActor = new MovieActor();
        movieActor.setActorName("Meryl Streep");
        movieActor.setMovieName("Sophie's Choice");
        movieActor.setActingYear(1982);
        movieActor.setActingAward("Best Actress");
        movieActor.setActingSchoolName("Yale School of Drama");
        return movieActor;
    }
```

Die DynamoDB-Tabellen enthalten die folgenden Elemente, bevor das Codebeispiel ausgeführt wird.

```
1 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
2 | MovieActor{movieName='Tar', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2022, actingSchoolName='National Institute of Dramatic Art'}
```

Die folgenden Elemente befinden sich in den Tabellen, nachdem die Ausführung des Codes abgeschlossen ist.

```
3 | ProductCatalog{id=2, title='Title 55', isbn='1-565-85698', authors=[a, b], price=30.22}
4 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=40.0}
5 | MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
```

Das Element in Zeile 2 wurde gelöscht und die Zeilen 3 und 5 zeigen die Elemente, die eingefügt wurden. Zeile 4 zeigt die Aktualisierung von Zeile 1. Der `price` Wert ist der einzige Wert, der sich für den Artikel geändert hat. Wenn False zurückgegeben `ignoreNulls()` worden wäre, würde Zeile 4 wie die folgende Zeile aussehen.

```
ProductCatalog{id=4, title='Title 1', isbn='null', authors=null, price=40.0}
```

### Beispiel für eine Zustandsprüfung
<a name="ddb-en-client-use-multiop-trans-writeitems-checkcond"></a>

Das folgende Beispiel zeigt die Verwendung einer Zustandsprüfung. Eine Zustandsprüfung wird verwendet, um zu überprüfen, ob ein Element vorhanden ist, oder um den Zustand bestimmter Attribute eines Elements in der Datenbank zu überprüfen. Der Artikel, der bei der Zustandsprüfung geprüft wurde, kann nicht für einen anderen Vorgang in der Transaktion verwendet werden.

**Anmerkung**  
Sie können nicht in derselben Transaktion mit mehreren Operationen auf das gleiche Element abzielen. Sie können beispielsweise nicht eine Zustandsprüfung durchführen und gleichzeitig versuchen, denselben Artikel in derselben Transaktion zu aktualisieren.

Das Beispiel zeigt einen von jedem Operationstyp in einer transaktionalen Anforderung zum Schreiben von Elementen. Nach Kommentarzeile 2 gibt die `addConditionCheck()` Methode die Bedingung an, dass die Transaktion fehlschlägt, wenn der `conditionExpression` Parameter als 0 ausgewertet wird. `false` Der Bedingungsausdruck, der von der im Block Helper-Methoden gezeigten Methode zurückgegeben wird, prüft, ob das Preisjahr für den Film ungleich `Sophie's Choice` ist. `1982` Ist dies der Fall, wird der Ausdruck als 0 ausgewertet `false` und die Transaktion schlägt fehl.

In diesem Handbuch werden [Ausdrücke](ddb-en-client-expressions.md) in einem anderen Thema ausführlich behandelt.

```
    public static void conditionCheckFailExample(DynamoDbEnhancedClient enhancedClient,
                                                 DynamoDbTable<ProductCatalog> catalogTable,
                                                 DynamoDbTable<MovieActor> movieActorTable) {

        try {
            enhancedClient.transactWriteItems(b -> b
                    // 1. Perform one of each type of operation with the next three methods.
                    .addPutItem(catalogTable, TransactPutItemEnhancedRequest.builder(ProductCatalog.class)
                            .item(getProductCatId2()).build())
                    .addUpdateItem(catalogTable, TransactUpdateItemEnhancedRequest.builder(ProductCatalog.class)
                            .item(getProductCatId4ForUpdate())
                            .ignoreNulls(Boolean.TRUE).build())
                    .addDeleteItem(movieActorTable, TransactDeleteItemEnhancedRequest.builder()
                            .key(b1 -> b1
                                    .partitionValue(getMovieActorBlanchett().getMovieName())
                                    .sortValue(getMovieActorBlanchett().getActorName())).build())
                    // 2. Add a condition check on a table item that is not involved in another operation in this request.
                    .addConditionCheck(movieActorTable, ConditionCheck.builder()
                            .conditionExpression(buildConditionCheckExpression())
                            .key(k -> k
                                    .partitionValue("Sophie's Choice")
                                    .sortValue("Meryl Streep"))
                            // 3. Specify the request to return existing values from the item if the condition evaluates to true.
                            .returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD)
                            .build())
                    .build());
        // 4. Catch the exception if the transaction fails and log the information.
        } catch (TransactionCanceledException ex) {
            ex.cancellationReasons().stream().forEach(cancellationReason -> {
                logger.info(cancellationReason.toString());
            });
        }
    }
```

Die folgenden Hilfsmethoden wurden im vorherigen Codebeispiel verwendet.

#### Hilfsmethoden
<a name="ddb-en-client-use-multiop-trans-writeitems-checkcond-helpers"></a>

```
    private static Expression buildConditionCheckExpression() {
        Map<String, AttributeValue> expressionValue = Map.of(
                ":year", numberValue(1982));

        return Expression.builder()
                .expression("actingyear <> :year")
                .expressionValues(expressionValue)
                .build();
    }

    public static ProductCatalog getProductCatId2() {
        return ProductCatalog.builder()
                .id(2)
                .isbn("1-565-85698")
                .authors(new HashSet<>(Arrays.asList("a", "b")))
                .price(BigDecimal.valueOf(30.22))
                .title("Title 55")
                .build();
    }

    public static ProductCatalog getProductCatId4ForUpdate() {
        return ProductCatalog.builder()
                .id(4)
                .price(BigDecimal.valueOf(40.00))
                .title("Title 1")
                .build();
    }

    public static MovieActor getMovieActorBlanchett() {
        MovieActor movieActor = new MovieActor();
        movieActor.setActorName("Cate Blanchett");
        movieActor.setMovieName("Blue Jasmine");
        movieActor.setActingYear(2013);
        movieActor.setActingAward("Best Actress");
        movieActor.setActingSchoolName("National Institute of Dramatic Art");
        return movieActor;
    }
```

Die DynamoDB-Tabellen enthalten die folgenden Elemente, bevor das Codebeispiel ausgeführt wird.

```
1 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
2 | MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
3 | MovieActor{movieName='Tar', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2022, actingSchoolName='National Institute of Dramatic Art'}
```

Die folgenden Elemente befinden sich in den Tabellen, nachdem die Ausführung des Codes abgeschlossen ist.

```
ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
MovieActor{movieName='Tar', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2022, actingSchoolName='National Institute of Dramatic Art'}
```

Die Elemente in den Tabellen bleiben unverändert, da die Transaktion fehlgeschlagen ist. Der `actingYear` Wert für den Film `Sophie's Choice` entspricht dem Wert`1982`, der in Zeile 2 der Elemente in der Tabelle vor dem Aufruf der `transactWriteItem()` Methode angegeben wurde.

Um die Stornierungsinformationen für die Transaktion zu erfassen, schließen Sie den `transactWriteItems()` Methodenaufruf in einen `try` Block ein und fügen Sie `catch` den [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/TransactionCanceledException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/TransactionCanceledException.html). Nach Kommentarzeile 4 des Beispiels protokolliert der Code jedes `[CancellationReason](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/CancellationReason.html)` Objekt. Da der Code nach der dritten Kommentarzeile des Beispiels angibt, dass Werte für das Element zurückgegeben werden sollen, das zum Fehlschlagen der Transaktion geführt hat, zeigt das Protokoll die unformatierten Datenbankwerte für das `Sophie's Choice` Filmelement an.

```
CancellationReason(Code=None)
CancellationReason(Code=None)
CancellationReason(Code=None)
CancellationReason(Item={actor=AttributeValue(S=Meryl Streep), movie=AttributeValue(S=Sophie's Choice), actingaward=AttributeValue(S=Best Actress), actingyear=AttributeValue(N=1982), actingschoolname=AttributeValue(S=Yale School of Drama)}, ¬
    Code=ConditionalCheckFailed, Message=The conditional request failed.)
```

### Beispiel für eine einzelne Betriebsbedingung
<a name="ddb-en-client-use-multiop-trans-writeitems-opcondition"></a>

Das folgende Beispiel zeigt die Verwendung einer Bedingung für eine einzelne Operation in einer Transaktionsanforderung. Der Löschvorgang nach der ersten Kommentarzeile enthält eine Bedingung, die den Wert des Zielelements der Operation mit der Datenbank vergleicht. In diesem Beispiel gibt der Bedingungsausdruck, der mit der Hilfsmethode nach Kommentarzeile 2 erstellt wurde, an, dass das Element aus der Datenbank gelöscht werden soll, wenn das Schauspieljahr des Films nicht 2013 entspricht.

[Ausdrücke](ddb-en-client-expressions.md) werden später in diesem Handbuch behandelt.

```
    public static void singleOperationConditionFailExample(DynamoDbEnhancedClient enhancedClient,
                                                           DynamoDbTable<ProductCatalog> catalogTable,
                                                           DynamoDbTable<MovieActor> movieActorTable) {
        try {
            enhancedClient.transactWriteItems(b -> b
                    .addPutItem(catalogTable, TransactPutItemEnhancedRequest.builder(ProductCatalog.class)
                            .item(getProductCatId2())
                            .build())
                    .addUpdateItem(catalogTable, TransactUpdateItemEnhancedRequest.builder(ProductCatalog.class)
                            .item(getProductCatId4ForUpdate())
                            .ignoreNulls(Boolean.TRUE).build())
                    // 1. Delete operation that contains a condition expression
                    .addDeleteItem(movieActorTable, TransactDeleteItemEnhancedRequest.builder()
                            .key((Key.Builder k) -> {
                                MovieActor blanchett = getMovieActorBlanchett();
                                k.partitionValue(blanchett.getMovieName())
                                        .sortValue(blanchett.getActorName());
                            })
                            .conditionExpression(buildDeleteItemExpression())
                            .returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD)
                            .build())
                    .build());
        } catch (TransactionCanceledException ex) {
            ex.cancellationReasons().forEach(cancellationReason -> logger.info(cancellationReason.toString()));
        }
    }

    // 2. Provide condition expression to check if 'actingyear' is not equal to 2013.
    private static Expression buildDeleteItemExpression() {
        Map<String, AttributeValue> expressionValue = Map.of(
                ":year", numberValue(2013));

        return Expression.builder()
                .expression("actingyear <> :year")
                .expressionValues(expressionValue)
                .build();
    }
```

Die folgenden Hilfsmethoden wurden im vorherigen Codebeispiel verwendet.

#### Hilfsmethoden
<a name="ddb-en-client-use-multiop-trans-writeitems-opcondition-helpers"></a>

```
    public static ProductCatalog getProductCatId2() {
        return ProductCatalog.builder()
                .id(2)
                .isbn("1-565-85698")
                .authors(new HashSet<>(Arrays.asList("a", "b")))
                .price(BigDecimal.valueOf(30.22))
                .title("Title 55")
                .build();
    }

    public static ProductCatalog getProductCatId4ForUpdate() {
        return ProductCatalog.builder()
                .id(4)
                .price(BigDecimal.valueOf(40.00))
                .title("Title 1")
                .build();
    }
    public static MovieActor getMovieActorBlanchett() {
        MovieActor movieActor = new MovieActor();
        movieActor.setActorName("Cate Blanchett");
        movieActor.setMovieName("Blue Jasmine");
        movieActor.setActingYear(2013);
        movieActor.setActingAward("Best Actress");
        movieActor.setActingSchoolName("National Institute of Dramatic Art");
        return movieActor;
    }
```

Die DynamoDB-Tabellen enthalten die folgenden Elemente, bevor das Codebeispiel ausgeführt wird.

```
1 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
2 | MovieActor{movieName='Blue Jasmine', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2013, actingSchoolName='National Institute of Dramatic Art'}
```

Die folgenden Elemente befinden sich in den Tabellen, nachdem die Ausführung des Codes abgeschlossen ist.

```
ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
2023-03-15 11:29:07 [main] INFO  org.example.tests.TransactDemoTest:168 - MovieActor{movieName='Blue Jasmine', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2013, actingSchoolName='National Institute of Dramatic Art'}
```

Die Elemente in den Tabellen bleiben unverändert, da die Transaktion fehlgeschlagen ist. Der `actingYear` Wert für den Film `Blue Jasmine` entspricht `2013` in Zeile 2 der Elementliste, bevor das Codebeispiel ausgeführt wird.

Die folgenden Zeilen werden in der Konsole protokolliert.

```
CancellationReason(Code=None)
CancellationReason(Code=None)
CancellationReason(Item={actor=AttributeValue(S=Cate Blanchett), movie=AttributeValue(S=Blue Jasmine), actingaward=AttributeValue(S=Best Actress), actingyear=AttributeValue(N=2013), actingschoolname=AttributeValue(S=National Institute of Dramatic Art)}, 
    Code=ConditionalCheckFailed, Message=The conditional request failed)
```

# Verwenden Sie sekundäre Indizes
<a name="ddb-en-client-use-secindex"></a>

Sekundäre Indizes verbessern den Datenzugriff, indem sie alternative Schlüssel definieren, die Sie bei Abfrage- und Scanvorgängen verwenden. Globale Sekundärindizes (GSI) haben einen Partitionsschlüssel und einen Sortierschlüssel, die sich von denen in der Basistabelle unterscheiden können. Im Gegensatz dazu verwenden lokale Sekundärindizes (LSI) den Partitionsschlüssel des Primärindexes.

## Kommentieren Sie die Datenklasse mit Anmerkungen zum sekundären Index
<a name="ddb-en-client-use-secindex-annomodel"></a>

Für Attribute, die an sekundären Indizes beteiligt sind, ist entweder die `@DynamoDbSecondarySortKey` Anmerkung `@DynamoDbSecondaryPartitionKey` oder erforderlich.

Die folgende Klasse zeigt Anmerkungen für zwei Indizes. Die angegebene GSI *SubjectLastPostedDateIndex*verwendet das `Subject` Attribut für den Partitionsschlüssel und das `LastPostedDateTime` für den Sortierschlüssel. Die benannte LSI *ForumLastPostedDateIndex*verwendet den `ForumName` als Partitionsschlüssel und `LastPostedDateTime` als Sortierschlüssel.

Beachten Sie, dass das `Subject` Attribut eine doppelte Rolle spielt. Es ist der Sortierschlüssel des Primärschlüssels und der Partitionsschlüssel der genannten *SubjectLastPostedDateIndex*GSI.

### `MessageThread`-Klasse
<a name="ddb-en-client-use-secindex-class"></a>

Die `MessageThread` Klasse eignet sich als Datenklasse für die [Thread-Beispieltabelle](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AppendixSampleTables.html) im *Amazon DynamoDB Developer Guide*.

#### Importe
<a name="ddb-en-client-use-secindex-classimports"></a>

```
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
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.DynamoDbSecondarySortKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;

import java.util.List;
```

```
@DynamoDbBean
public class MessageThread {
    private String ForumName;
    private String Subject;
    private String Message;
    private String LastPostedBy;
    private String LastPostedDateTime;
    private Integer Views;
    private Integer Replies;
    private Integer Answered;
    private List<String> Tags;

    @DynamoDbPartitionKey
    public String getForumName() {
        return ForumName;
    }

    public void setForumName(String forumName) {
        ForumName = forumName;
    }

    // Sort key for primary index and partition key for GSI "SubjectLastPostedDateIndex".
    @DynamoDbSortKey
    @DynamoDbSecondaryPartitionKey(indexNames = "SubjectLastPostedDateIndex")
    public String getSubject() {
        return Subject;
    }

    public void setSubject(String subject) {
        Subject = subject;
    }

    // Sort key for GSI "SubjectLastPostedDateIndex" and sort key for LSI "ForumLastPostedDateIndex".
    @DynamoDbSecondarySortKey(indexNames = {"SubjectLastPostedDateIndex", "ForumLastPostedDateIndex"})
    public String getLastPostedDateTime() {
        return LastPostedDateTime;
    }

    public void setLastPostedDateTime(String lastPostedDateTime) {
        LastPostedDateTime = lastPostedDateTime;
    }
    public String getMessage() {
        return Message;
    }

    public void setMessage(String message) {
        Message = message;
    }

    public String getLastPostedBy() {
        return LastPostedBy;
    }

    public void setLastPostedBy(String lastPostedBy) {
        LastPostedBy = lastPostedBy;
    }

    public Integer getViews() {
        return Views;
    }

    public void setViews(Integer views) {
        Views = views;
    }

    @DynamoDbSecondaryPartitionKey(indexNames = "ForumRepliesIndex")
    public Integer getReplies() {
        return Replies;
    }

    public void setReplies(Integer replies) {
        Replies = replies;
    }

    public Integer getAnswered() {
        return Answered;
    }

    public void setAnswered(Integer answered) {
        Answered = answered;
    }

    public List<String> getTags() {
        return Tags;
    }

    public void setTags(List<String> tags) {
        Tags = tags;
    }

    public MessageThread() {
        this.Answered = 0;
        this.LastPostedBy = "";
        this.ForumName = "";
        this.Message = "";
        this.LastPostedDateTime = "";
        this.Replies = 0;
        this.Views = 0;
        this.Subject = "";
    }

    @Override
    public String toString() {
        return "MessageThread{" +
                "ForumName='" + ForumName + '\'' +
                ", Subject='" + Subject + '\'' +
                ", Message='" + Message + '\'' +
                ", LastPostedBy='" + LastPostedBy + '\'' +
                ", LastPostedDateTime='" + LastPostedDateTime + '\'' +
                ", Views=" + Views +
                ", Replies=" + Replies +
                ", Answered=" + Answered +
                ", Tags=" + Tags +
                '}';
    }
}
```

## Erstellen Sie den Index
<a name="ddb-en-client-use-secindex-confindex"></a>

Ab Version 2.20.86 des SDK for Java generiert die `createTable()` Methode automatisch Sekundärindizes aus Datenklassenanmerkungen. Standardmäßig werden alle Attribute aus der Basistabelle in einen Index kopiert, und die bereitgestellten Durchsatzwerte betragen 20 Lesekapazitätseinheiten und 20 Schreibkapazitätseinheiten.

Wenn Sie jedoch eine SDK-Version vor 2.20.86 verwenden, müssen Sie den Index zusammen mit der Tabelle erstellen, wie im folgenden Beispiel gezeigt. In diesem Beispiel werden die beiden Indizes für die Tabelle erstellt. `Thread` Der [Builder-Parameter](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/CreateTableEnhancedRequest.Builder.html) verfügt über Methoden zur Konfiguration beider Indextypen, wie nach den Kommentarzeilen 1 und 2 gezeigt. Sie verwenden die `indexName()` Methode des Indexgenerators, um die in den Datenklassenanmerkungen angegebenen Indexnamen dem gewünschten Indextyp zuzuordnen.

Dieser Code konfiguriert alle Tabellenattribute so, dass sie nach den Kommentarzeilen 3 und 4 in beiden Indizes landen. Weitere Informationen zu [Attributprojektionen](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSI.html#LSI.Projections) finden Sie im *Amazon DynamoDB Developer Guide*.

```
    public static void createMessageThreadTable(DynamoDbTable<MessageThread> messageThreadDynamoDbTable, DynamoDbClient dynamoDbClient) {
        messageThreadDynamoDbTable.createTable(b -> b
                // 1. Generate the GSI.
                .globalSecondaryIndices(gsi -> gsi.indexName("SubjectLastPostedDateIndex")
                        // 3. Populate the GSI with all attributes.
                        .projection(p -> p
                                .projectionType(ProjectionType.ALL))
                )
                // 2. Generate the LSI.
                .localSecondaryIndices(lsi -> lsi.indexName("ForumLastPostedDateIndex")
                        // 4. Populate the LSI with all attributes.
                        .projection(p -> p
                                .projectionType(ProjectionType.ALL))
                )
        );
```

## Abfragen mithilfe eines Indexes
<a name="ddb-en-client-use-secindex-query"></a>

Im folgenden Beispiel wird der lokale sekundäre Index *ForumLastPostedDateIndex* abgefragt.

Nach Kommentarzeile 2 erstellen Sie ein [QueryConditional](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html)Objekt, das für den Aufruf der [DynamoDbIndex.query ()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbIndex.html#query(java.util.function.Consumer)) -Methode erforderlich ist. 

Sie erhalten nach Kommentarzeile 3 einen Verweis auf den Index, den Sie abfragen möchten, indem Sie den Namen des Indexes übergeben. Nach Kommentarzeile 4 rufen Sie die `query()` Methode für den Index auf und übergeben das `QueryConditional` Objekt. 

Sie konfigurieren die Abfrage auch so, dass sie drei Attributwerte zurückgibt, wie nach Kommentarzeile 5 gezeigt. Wenn nicht aufgerufen `attributesToProject()` wird, gibt die Abfrage alle Attributwerte zurück. Beachten Sie, dass die angegebenen Attributnamen mit Kleinbuchstaben beginnen. Diese Attributnamen stimmen mit den in der Tabelle verwendeten überein, nicht unbedingt mit den Attributnamen der Datenklasse.

Folgen Sie der Kommentarzeile 6 und wiederholen Sie die Ergebnisse, protokollieren Sie jedes von der Abfrage zurückgegebene Element und speichern Sie es auch in der Liste, um zum Aufrufer zurückzukehren.

```
public class IndexScanExamples {
    private static Logger logger = LoggerFactory.getLogger(IndexScanExamples.class);

    public static List<MessageThread> queryUsingSecondaryIndices(String lastPostedDate,
                                                                 DynamoDbTable<MessageThread> threadTable) {
        // 1. Log the parameter value.
        logger.info("lastPostedDate value: {}", lastPostedDate);

        // 2. Create a QueryConditional whose sort key value must be greater than or equal to the parameter value.
        QueryConditional queryConditional = QueryConditional.sortGreaterThanOrEqualTo(qc ->
                qc.partitionValue("Forum02").sortValue(lastPostedDate));

        // 3. Specify the index name to query.
        final DynamoDbIndex<MessageThread> forumLastPostedDateIndex = threadTable.index("ForumLastPostedDateIndex");

        // 4. Perform the query using the QueryConditional object.
        final SdkIterable<Page<MessageThread>> pagedResult = forumLastPostedDateIndex.query(q -> q
                .queryConditional(queryConditional)
                // 5. Request three attribute in the results.
                .attributesToProject("forumName", "subject", "lastPostedDateTime"));

        List<MessageThread> collectedItems = new ArrayList<>();
        // 6. Iterate through pages response and sort the items.
        pagedResult.stream().forEach(page -> page.items().stream()
                .sorted(Comparator.comparing(MessageThread::getLastPostedDateTime))
                .forEach(mt -> {
                    // 7. Log the returned items and add the collection to return to the caller.
                    logger.info(mt.toString());
                    collectedItems.add(mt);
                }));
        return collectedItems;
    }
```

Die folgenden Elemente sind in der Datenbank vorhanden, bevor die Abfrage ausgeführt wird.

```
MessageThread{ForumName='Forum01', Subject='Subject01', Message='Message01', LastPostedBy='', LastPostedDateTime='2023.03.28', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum02', Subject='Subject02', Message='Message02', LastPostedBy='', LastPostedDateTime='2023.03.29', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum02', Subject='Subject04', Message='Message04', LastPostedBy='', LastPostedDateTime='2023.03.31', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum02', Subject='Subject08', Message='Message08', LastPostedBy='', LastPostedDateTime='2023.04.04', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum02', Subject='Subject10', Message='Message10', LastPostedBy='', LastPostedDateTime='2023.04.06', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum03', Subject='Subject03', Message='Message03', LastPostedBy='', LastPostedDateTime='2023.03.30', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum03', Subject='Subject06', Message='Message06', LastPostedBy='', LastPostedDateTime='2023.04.02', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum03', Subject='Subject09', Message='Message09', LastPostedBy='', LastPostedDateTime='2023.04.05', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum05', Subject='Subject05', Message='Message05', LastPostedBy='', LastPostedDateTime='2023.04.01', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum07', Subject='Subject07', Message='Message07', LastPostedBy='', LastPostedDateTime='2023.04.03', Views=0, Replies=0, Answered=0, Tags=null}
```

Die Protokollierungsanweisungen in den Zeilen 1 und 6 führen zu der folgenden Konsolenausgabe.

```
lastPostedDate value: 2023.03.31
MessageThread{ForumName='Forum02', Subject='Subject04', Message='', LastPostedBy='', LastPostedDateTime='2023.03.31', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum02', Subject='Subject08', Message='', LastPostedBy='', LastPostedDateTime='2023.04.04', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum02', Subject='Subject10', Message='', LastPostedBy='', LastPostedDateTime='2023.04.06', Views=0, Replies=0, Answered=0, Tags=null}
```

Die Abfrage hat Elemente mit dem `forumName` Wert *Forum02* und einem `lastPostedDateTime` Wert größer oder gleich *2023.03.31* zurückgegeben. Die Ergebnisse zeigen `message` Werte mit einer leeren Zeichenfolge, obwohl die `message` Attribute Werte im Index haben. Das liegt daran, dass das Nachrichtenattribut im Code nach Kommentarzeile 5 nicht projiziert wurde. 