

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

# Scopri le basi dell'API DynamoDB Enhanced Client
<a name="ddb-en-client-use"></a>

[Questo argomento illustra le funzionalità di base dell'API DynamoDB Enhanced Client e la confronta con l'API client DynamoDB standard.](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/package-summary.html)

Se non conosci l'API DynamoDB Enhanced Client, ti consigliamo di seguire il tutorial [introduttivo per familiarizzare con le](ddb-en-client-getting-started.md) classi fondamentali.

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

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

Se il tuo caso d'uso richiede elementi con un set coerente di attributi, usa [classi annotate](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean) o usa un [builder](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-builder) per generare i tipi statici appropriati. `TableSchema` 

In alternativa, se devi archiviare elementi costituiti da strutture diverse, crea un. `DocumentTableSchema` `DocumentTableSchema`fa parte dell'[API Enhanced Document](ddb-en-client-doc-api.md) e richiede solo una chiave primaria tipizzata staticamente e funziona con le `EnhancedDocument` istanze per contenere gli elementi di dati. [L'API Enhanced Document è trattata in un altro argomento.](ddb-en-client-doc-api.md)

## Tipi di attributi per le classi del modello di dati
<a name="ddb-en-client-use-types"></a>

Sebbene DynamoDB [supporti un numero limitato di tipi di attributi rispetto al rich type system di](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes) Java, l'API DynamoDB Enhanced Client fornisce meccanismi per convertire i membri di una classe Java da e verso i tipi di attributi DynamoDB.

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

[Per impostazione predefinita, l'API DynamoDB Enhanced Client supporta convertitori di attributi per un gran numero di tipi, [come Integer[,](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html) String](https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html) e Instant. [BigDecimal](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/BigDecimalAttributeConverter.html)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/InstantAsStringAttributeConverter.html) L'elenco viene visualizzato nelle [classi di implementazione note](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverter.html) dell'interfaccia. AttributeConverter L'elenco include molti tipi e raccolte come mappe, elenchi e set.

Per memorizzare i dati per un tipo di attributo che non è supportato di default o non è conforme alla JavaBean convenzione, puoi scrivere un'`AttributeConverter`implementazione personalizzata per eseguire la conversione. Vedi la sezione sulla conversione degli attributi per un [esempio](ddb-en-client-adv-features-conversion.md#ddb-en-client-adv-features-conversion-example).

Per memorizzare i dati per un tipo di attributo la cui classe è conforme alla specifica Java beans (o una [classe di dati immutabile](ddb-en-client-use-immut.md)), puoi adottare due approcci. 
+ Se avete accesso al file sorgente, potete annotare la classe con `@DynamoDbBean` (o). `@DynamoDbImmutable` La sezione che illustra gli attributi annidati mostra [esempi](ddb-en-client-adv-features-nested.md#ddb-en-client-adv-features-nested-map-anno) di utilizzo di classi annotate.
+ Se non hai accesso al file sorgente della classe di JavaBean dati per l'attributo (o non vuoi annotare il file sorgente di una classe a cui hai accesso), puoi usare l'approccio builder. Questo crea uno schema tabellare senza definire le chiavi. Quindi, puoi annidare questo schema di tabella all'interno di un altro schema di tabella per eseguire la mappatura. La sezione degli attributi nidificati contiene un [esempio](ddb-en-client-adv-features-nested.md#ddb-en-client-adv-features-nested-map-builder) che mostra l'uso di schemi nidificati.

### Valori nulli
<a name="ddb-en-client-use-types-nulls"></a>

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

Il comportamento predefinito dell'SDK per `updateItem` le richieste rimuove gli attributi dall'elemento in DynamoDB che sono impostati su null nell'oggetto inviato nel metodo. `updateItem` Se intendi aggiornare alcuni valori degli attributi e mantenere invariati gli altri, hai due opzioni.
+ Recuperate l'elemento (utilizzando`getItem`) prima di apportare modifiche ai valori. Utilizzando questo approccio, l'SDK invia tutti i valori vecchi e aggiornati a DynamoDB.
+ Usa `[IgnoreNullsMode](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/IgnoreNullsMode.html).SCALAR_ONLY` o `IgnoreNullsMode.MAPS_ONLY` quando crei la richiesta per aggiornare l'elemento. Entrambe le modalità ignorano le proprietà con valori nulli nell'oggetto che rappresentano gli attributi scalari in DynamoDB. L'[Aggiorna gli elementi che contengono tipi complessi](ddb-en-client-adv-features-nested.md#ddb-en-client-adv-features-nested-updates)argomento di questa guida contiene ulteriori informazioni sui `IgnoreNullsMode` valori e su come lavorare con tipi complessi.

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

```
    public static void updateItemNullsExample() {
        Customer customer = new Customer();
        customer.setCustName("CustomerName");
        customer.setEmail("email");
        customer.setId("1");
        customer.setRegistrationDate(Instant.now());

        logger.info("Original customer: {}", customer);

        // Put item with values for all attributes.
        try {
            customerAsyncDynamoDbTable.putItem(customer).join();
        } catch (RuntimeException rte) {
            logger.error("A exception occurred during putItem: {}", rte.getCause().getMessage(), rte);
            return;
        }

        // Create a Customer instance with the same 'id' and 'email' values, but a different 'name' value.
        // Do not set the 'registrationDate' attribute.
        Customer customerForUpdate = new Customer();
        customerForUpdate.setCustName("NewName");
        customerForUpdate.setEmail("email");
        customerForUpdate.setId("1");

        // Update item without setting the 'registrationDate' property and set IgnoreNullsMode to SCALAR_ONLY.
        try {
            Customer updatedWithNullsIgnored = customerAsyncDynamoDbTable.updateItem(b -> b
                            .item(customerForUpdate)
                            .ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY))
                    .join();
            logger.info("Customer updated with nulls ignored: {}", updatedWithNullsIgnored.toString());
        } catch (RuntimeException rte) {
            logger.error("An exception occurred during updateItem: {}", rte.getCause().getMessage(), rte);
            return;
        }

        // Update item without setting the registrationDate attribute and not setting ignoreNulls to true.
        try {
            Customer updatedWithNullsUsed = customerAsyncDynamoDbTable.updateItem(customerForUpdate)
                    .join();
            logger.info("Customer updated with nulls used: {}", updatedWithNullsUsed.toString());
        } catch (RuntimeException rte) {
            logger.error("An exception occurred during updateItem: {}", rte.getCause().getMessage(), rte);
        }
    }


// Logged lines. 
Original customer: Customer [id=1, name=CustomerName, email=email, regDate=2024-10-11T14:12:30.222858Z]
Customer updated with nulls ignored: Customer [id=1, name=NewName, email=email, regDate=2024-10-11T14:12:30.222858Z]
Customer updated with nulls used: Customer [id=1, name=NewName, email=email, regDate=null]
```

## Metodi di base di DynamoDB Enhanced Client
<a name="ddb-en-client-use-basic-ops"></a>

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

L'esempio utilizza quanto [Classe `Customer`](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust) mostrato in precedenza.

```
// CreateTable
customerTable.createTable();

// GetItem
Customer customer = customerTable.getItem(Key.builder().partitionValue("a123").build());

// UpdateItem
Customer updatedCustomer = customerTable.updateItem(customer);

// PutItem
customerTable.putItem(customer);

// DeleteItem
Customer deletedCustomer = customerTable.deleteItem(Key.builder().partitionValue("a123").sortValue(456).build());

// Query
PageIterable<Customer> customers = customerTable.query(keyEqualTo(k -> k.partitionValue("a123")));

// Scan
PageIterable<Customer> customers = customerTable.scan();

// BatchGetItem
BatchGetResultPageIterable batchResults = 
    enhancedClient.batchGetItem(r -> r.addReadBatch(ReadBatch.builder(Customer.class)
                                      .mappedTableResource(customerTable)
                                      .addGetItem(key1)
                                      .addGetItem(key2)
                                      .addGetItem(key3)
                                      .build()));

// BatchWriteItem
batchResults = enhancedClient.batchWriteItem(r -> r.addWriteBatch(WriteBatch.builder(Customer.class)
                                                   .mappedTableResource(customerTable)
                                                   .addPutItem(customer)
                                                   .addDeleteItem(key1)
                                                   .addDeleteItem(key1)
                                                   .build()));

// TransactGetItems
transactResults = enhancedClient.transactGetItems(r -> r.addGetItem(customerTable, key1)
                                                        .addGetItem(customerTable, key2));

// TransactWriteItems
enhancedClient.transactWriteItems(r -> r.addConditionCheck(customerTable, 
                                                           i -> i.key(orderKey)
                                                                 .conditionExpression(conditionExpression))
                                        .addUpdateItem(customerTable, customer)
                                        .addDeleteItem(customerTable, key));
```

## Confronta DynamoDB Enhanced Client con il client DynamoDB standard
<a name="ddb-en-client-use-compare"></a>

[Entrambi i APIs client DynamoDB, standard [e](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/package-summary.html) avanzato, consentono di lavorare con le tabelle DynamoDB per eseguire operazioni CRUD (creazione, lettura, aggiornamento ed eliminazione) a livello di dati.](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/package-summary.html) La differenza tra i client sta nel modo in cui ciò viene realizzato. APIs Utilizzando il client standard, si lavora direttamente con attributi di dati di basso livello. L'API client avanzata utilizza classi Java familiari e si collega all'API di basso livello dietro le quinte.

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

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

### Confronto: crea una tabella utilizzando il client DynamoDB standard
<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))
);
```

### Confronta: crea una tabella utilizzando il 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))
        )
);
```

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

### Classe `ProductCatalog`
<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);
        }
    }
}
```

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

### Confronto: Scrittura in batch utilizzando il client DynamoDB standard
<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());
    }
```

### Confronto: Scrittura in batch utilizzando il 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());
    }
```

# Lavora con classi di dati immutabili
<a name="ddb-en-client-use-immut"></a>

La funzionalità di mappatura dell'API DynamoDB Enhanced Client funziona con classi di dati immutabili. Una classe immutabile ha solo getter e richiede una classe builder che l’SDK utilizza per creare istanze della classe. Invece di utilizzare l'`@DynamoDbBean`annotazione come mostrato nella [classe Customer](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust), le classi immutabili utilizzano l'`@DynamoDbImmutable`annotazione, che accetta un parametro che indica la classe builder da utilizzare.

La classe seguente è una versione immutabile di. `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); }
    }
}
```

È necessario soddisfare i seguenti requisiti quando si annota una classe di dati con. `@DynamoDbImmutable`

1. Ogni metodo che non sostituisce `Object.class` e con cui non è stato annotato `@DynamoDbIgnore` deve essere un getter per un attributo della tabella DynamoDB.

1. Ogni getter deve avere un setter corrispondente con distinzione tra maiuscole e minuscole nella classe builder.

1. Deve essere soddisfatta solo una delle seguenti condizioni di costruzione.
   + La classe builder deve avere un costruttore pubblico predefinito.
   + La classe di dati deve avere un metodo statico pubblico denominato `builder()` che non accetta parametri e restituisce un'istanza della classe builder. Questa opzione è mostrata nella classe immutable`Customer`.

1.  La classe builder deve avere un metodo pubblico denominato `build()` che non accetta parametri e restituisce un'istanza della classe immutabile. 

Per crearne uno `TableSchema` per la tua classe immutabile, usa il `fromImmutableClass()` metodo on `TableSchema` come mostrato nel seguente frammento.

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

Proprio come è possibile creare una tabella DynamoDB da una classe mutabile, è possibile crearne una da una classe immutabile con *una* chiamata singola a of, come illustrato nel seguente esempio `DynamoDbTable` di `createTable()` snippet.

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

## Usa librerie di terze parti, come Lombok
<a name="ddb-en-client-use-immut-lombok"></a>

Le librerie di terze parti, come [Project Lombok](https://projectlombok.org/), aiutano a generare codice standard associato a oggetti immutabili. L'API DynamoDB Enhanced Client funziona con queste librerie purché le classi di dati seguano le convenzioni descritte in questa sezione. 

L'esempio seguente mostra la classe immutabile `CustomerImmutable` con annotazioni Lombok. Nota come la `onMethod` funzionalità di Lombok copia le annotazioni DynamoDB basate su attributi, ad esempio, sul codice generato. `@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;
}
```

# Usa espressioni e condizioni
<a name="ddb-en-client-expressions"></a>

[Le espressioni nell'API DynamoDB Enhanced Client sono rappresentazioni Java delle espressioni DynamoDB.](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.html)

L'API DynamoDB Enhanced Client utilizza tre tipi di espressioni:

[Expression](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Expression.html)  
La `Expression` classe viene utilizzata quando si definiscono condizioni e filtri.

[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)  
Questo tipo di espressione rappresenta [le condizioni chiave per le](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.KeyConditionExpressions) operazioni di interrogazione.

[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)  
Questa classe consente di scrivere espressioni di [aggiornamento di DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html) ed è attualmente utilizzata nel framework di estensione quando si aggiorna un elemento.

## Anatomia delle espressioni
<a name="ddb-en-client-expressions-compoonents"></a>

Un'espressione è composta dai seguenti elementi:
+ Un'espressione stringa (obbligatoria). La stringa contiene un'espressione logica DynamoDB con nomi segnaposto per i nomi e i valori degli attributi.
+ Una mappa dei valori delle espressioni (in genere richiesta).
+ Una mappa dei nomi delle espressioni (opzionale).

Utilizzate un builder per generare un `Expression` oggetto che assuma la seguente forma generale.

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

`Expression`Di solito richiede una mappa di valori di espressione. La mappa fornisce i valori per i segnaposto nell'espressione stringa. La chiave della mappa è costituita dal nome del segnaposto preceduto da due punti (`:`) e il valore della mappa è un'istanza di. [AttributeValue](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/AttributeValue.html) La [AttributeValues](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/AttributeValues.html)classe dispone di metodi pratici per generare un'`AttributeValue`istanza da un valore letterale. In alternativa, è possibile utilizzare il `AttributeValue.Builder` per generare un'`AttributeValue`istanza.

Il seguente frammento mostra una mappa con due voci dopo la riga di commento 2. La stringa passata al `expression()` metodo, mostrata dopo la riga di commento 1, contiene i segnaposto che DynamoDB risolve prima di eseguire l'operazione. *Questo frammento non contiene una mappa di nomi di espressioni, perché price è un nome di attributo consentito.*

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

Se il nome di un attributo nella tabella DynamoDB è una parola riservata, inizia con un numero o contiene uno spazio, è necessaria una mappa dei nomi delle espressioni per. `Expression`

Ad esempio, se il nome dell'attributo era `1price` al posto dell'`price`esempio di codice precedente, l'esempio dovrebbe essere modificato come mostrato nell'esempio seguente.

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

Un segnaposto per il nome di un'espressione inizia con il simbolo del cancelletto ()`#`. Una voce per la mappa dei nomi delle espressioni utilizza il segnaposto come chiave e il nome dell'attributo come valore. La mappa viene aggiunta al generatore di espressioni con il metodo. `expressionNames()` DynamoDB risolve il nome dell'attributo prima di eseguire l'operazione.

I valori di espressione non sono necessari se una funzione viene utilizzata nell'espressione stringa. Un esempio di funzione di espressione è`attribute_exists(<attribute_name>)`.

L'esempio seguente crea un file `Expression` che utilizza una funzione [DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions). La stringa di espressione in questo esempio non utilizza segnaposto. Questa espressione può essere utilizzata in un'`putItem`operazione per verificare se nel database esiste già un elemento con un valore dell'`movie`attributo uguale all'attributo dell'`movie`oggetto dati.

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

La DynamoDB Developer Guide contiene informazioni complete sulle espressioni di [basso livello utilizzate](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.html) con DynamoDB.

## Espressioni condizionali e condizionali
<a name="ddb-en-client-expressions-cond"></a>

Quando si utilizzano i `deleteItem()` metodi`putItem()`,`updateItem()`, e anche quando si utilizzano operazioni di transazione e batch, si utilizzano `[Expression](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Expression.html)` oggetti per specificare le condizioni che DynamoDB deve soddisfare per procedere con l'operazione. Queste espressioni sono denominate espressioni condizionali. Per un esempio, vedi l'espressione di condizione utilizzata nel `addDeleteItem()` metodo (dopo la riga di commento 1) dell'[esempio di transazione](ddb-en-client-use-multiop-trans.md#ddb-en-client-use-multiop-trans-writeitems-opcondition) mostrato in questa guida.

Quando si utilizzano i `query()` metodi, una condizione viene espressa come a [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). La `QueryConditional` classe dispone di diversi metodi statici di convenienza che aiutano a scrivere i criteri che determinano quali elementi leggere da DynamoDB.

Per alcuni esempi di`QueryConditionals`, consultate il primo esempio di codice della [`Query`esempi di metodi](ddb-en-client-use-multirecord.md#ddb-en-client-use-multirecord-query-example) sezione di questa guida.

## Espressioni filtro
<a name="ddb-en-client-expressions-filter"></a>

Le espressioni di filtro vengono utilizzate nelle operazioni di scansione e interrogazione per filtrare gli elementi restituiti. 

Un'espressione di filtro viene applicata dopo che tutti i dati sono stati letti dal database, quindi il costo di lettura è lo stesso che se non ci fosse alcun filtro. [La *Amazon DynamoDB Developer* Guide contiene ulteriori informazioni sull'uso delle espressioni di filtro per le operazioni di interrogazione [e scansione](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.FilterExpression).](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.FilterExpression)

L'esempio seguente mostra un'espressione di filtro aggiunta a una richiesta di scansione. I criteri limitano gli articoli restituiti agli articoli con un prezzo compreso tra 8,00 e 80,00€ inclusi.

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

## Espressioni di aggiornamento
<a name="ddb-en-client-expressions-update"></a>

Il metodo di DynamoDB Enhanced Client `updateItem()` fornisce un modo standard per aggiornare gli elementi in DynamoDB. [Tuttavia, quando hai bisogno di maggiori funzionalità, [UpdateExpressions](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/update/UpdateExpression.html)fornisci una rappresentazione sicura dei tipi della sintassi delle espressioni di aggiornamento di DynamoDB.](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html) Ad esempio, è possibile utilizzare `UpdateExpressions` per aumentare i valori senza prima leggere elementi da DynamoDB o aggiungere singoli membri a un elenco. Le espressioni di aggiornamento sono attualmente disponibili nelle estensioni personalizzate per il `updateItem()` metodo.

Per un esempio che utilizza le espressioni di aggiornamento, consultate l'[esempio di estensione personalizzata](ddb-en-client-extensions-custom.md) in questa guida.

Ulteriori informazioni sulle espressioni di aggiornamento sono disponibili nella [Amazon DynamoDB Developer Guide](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html).

# Lavora con risultati impaginati: scansioni e interrogazioni
<a name="ddb-en-client-use-multirecord"></a>

*I `scan` `batch` metodi `query` e dell'API DynamoDB Enhanced Client restituiscono risposte con una o più pagine.* Una pagina contiene uno o più elementi. Il codice può elaborare la risposta per pagina o elaborare singoli elementi.

Una risposta impaginata restituita dal client sincrono restituisce un [PageIterable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/PageIterable.html)oggetto, mentre una risposta restituita dal `DynamoDbEnhancedClient` client asincrono restituisce un oggetto. `DynamoDbEnhancedAsyncClient` [PagePublisher](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/PagePublisher.html)

Questa sezione esamina l'elaborazione dei risultati impaginati e fornisce esempi che utilizzano la scansione e l'interrogazione. APIs

## Esegui la scansione di una tabella
<a name="ddb-en-client-use-multirecord-scan"></a>

Il [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))metodo dell'SDK corrisponde all'operazione [DynamoDB con lo stesso nome](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html). L'API DynamoDB Enhanced Client offre le stesse opzioni, ma utilizza un modello a oggetti familiare e gestisce l'impaginazione al posto tuo.

Innanzitutto, esploriamo l'`PageIterable`interfaccia esaminando il `scan` metodo della classe di mappatura sincrona,. [DynamoDbTable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html)

### Usa l'API sincrona
<a name="ddb-en-client-use-multirecord-scan-sync"></a>

L'esempio seguente mostra il `scan` metodo che utilizza un'[espressione](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Expression.html) per filtrare gli elementi restituiti. [ProductCatalog](ddb-en-client-use.md#ddb-en-client-use-compare-cs3)È l'oggetto modello che è stato mostrato in precedenza.

L'espressione di filtro mostrata dopo la riga di commento 2 limita gli `ProductCatalog` articoli che vengono restituiti a quelli con un valore di prezzo compreso tra 8,00 e 80,00 inclusi.

Questo esempio esclude anche `isbn` i valori utilizzando il `attributesToProject` metodo mostrato dopo la riga di commento 1.

Dopo la riga di commento 3, l'`PageIterable`oggetto,`pagedResults`, viene restituito dal `scan` metodo. Il `stream` metodo di `PageIterable` restituisce un [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)oggetto, che è possibile utilizzare per elaborare le pagine. In questo esempio, il numero di pagine viene contato e registrato.

A partire dalla riga di commento 4, l'esempio mostra due varianti di accesso agli `ProductCatalog` elementi. La versione successiva alla riga di commento 4a scorre attraverso ogni pagina e ordina e registra gli elementi su ogni pagina. La versione successiva alla riga di commento 4b salta l'iterazione della pagina e accede direttamente agli elementi.

L'`PageIterable`interfaccia offre diversi modi per elaborare i risultati grazie alle sue due interfacce principali: e. [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`porta i `spliterator` metodi`forEach`, `iterator` e `SdkIterable` porta il `stream` metodo.

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

### Usa l'API asincrona
<a name="ddb-en-client-use-multirecord-scan-async"></a>

Il `scan` metodo asincrono restituisce i risultati come oggetto. `PagePublisher` L'`PagePublisher`interfaccia dispone di due `subscribe` metodi che è possibile utilizzare per elaborare le pagine di risposta. Un `subscribe` metodo proviene dall'interfaccia `org.reactivestreams.Publisher` principale. Per elaborare le pagine utilizzando questa prima opzione, passate un'`[Subscriber](https://www.reactive-streams.org/reactive-streams-1.0.0-javadoc/org/reactivestreams/Subscriber.html)`istanza al `subscribe` metodo. Il primo esempio che segue mostra l'uso del `subscribe` metodo.

Il secondo `subscribe` metodo proviene dall'[SdkPublisher](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/SdkPublisher.html)interfaccia. Questa versione di `subscribe` accetta a [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)anziché a`Subscriber`. Questa variazione di `subscribe` metodo è illustrata nel secondo esempio che segue.

L'esempio seguente mostra la versione asincrona del `scan` metodo che utilizza la stessa espressione di filtro mostrata nell'esempio precedente. 

Dopo la riga di commento 3, `DynamoDbAsyncTable.scan` restituisce un oggetto. `PagePublisher` Nella riga successiva, il codice crea un'istanza dell'`org.reactivestreams.Subscriber`interfaccia`ProductCatalogSubscriber`, che sottoscrive la riga 4 `PagePublisher` dopo il commento.

L'`Subscriber`oggetto raccoglie gli `ProductCatalog` elementi da ogni pagina del `onNext` metodo dopo la riga di commento 8 nell'esempio di `ProductCatalogSubscriber` classe. Gli elementi sono memorizzati nella `List` variabile privata e vi si accede nel codice di chiamata con il `ProductCatalogSubscriber.getSubscribedItems()` metodo. Viene chiamato dopo la riga di commento 5.

Dopo aver recuperato l'elenco, il codice ordina tutti gli `ProductCatalog` articoli in base al prezzo e registra ogni articolo.

La `ProductCatalogSubscriber` classe [CountDownLatch](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html)in the blocca il thread chiamante fino a quando tutti gli elementi non sono stati aggiunti all'elenco prima di continuare dopo la riga di commento 5. 

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

Il seguente esempio di frammento utilizza la versione del `PagePublisher.subscribe` metodo che accetta una riga `Consumer` successiva al commento 6. Il parametro Java lambda utilizza le pagine, che elaborano ulteriormente ogni elemento. In questo esempio, ogni pagina viene elaborata e gli elementi di ogni pagina vengono ordinati e quindi registrati.

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

Il `items` metodo di `PagePublisher` decodifica le istanze del modello in modo che il codice possa elaborare direttamente gli elementi. Questo approccio è illustrato nel frammento seguente.

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

## Eseguire una query su una tabella
<a name="ddb-en-client-use-multirecord-query"></a>

È possibile utilizzare il DynamoDB Enhanced Client per interrogare la tabella e recuperare più elementi che soddisfano criteri specifici. Il [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))metodo trova gli elementi in base ai valori della chiave primaria utilizzando le `@DynamoDbSortKey` annotazioni `@DynamoDbPartitionKey` opzionali definite nella classe di dati.

Il `query()` metodo richiede un valore di chiave di partizione e accetta facoltativamente le condizioni della chiave di ordinamento per perfezionare ulteriormente i risultati. Come l'`scan`API, le query restituiscono un `PageIterable` per le chiamate sincrone e un per le chiamate asincrone. `PagePublisher`

### `Query`esempi di metodi
<a name="ddb-en-client-use-multirecord-query-example"></a>

L'esempio di codice del `query()` metodo che segue utilizza la `MovieActor` classe. La classe di dati definisce una chiave primaria composita composta dall'**`movie`**attributo come chiave di partizione e dall'**`actor`**attributo come chiave di ordinamento. 

#### Classe `MovieActor`
<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);
        }
    }
}
```

Gli esempi di codice che seguono eseguono una query sui seguenti elementi.

#### Elementi nella `MovieActor` tabella
<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'}
```

Il codice seguente definisce due `QueryConditional` istanze: `keyEqual` (dopo la riga di commento 1) e `sortGreaterThanOrEqualTo` (dopo la riga di commento 1a).

#### Interroga gli elementi per chiave di partizione
<a name="keyEqual-query-conditional-example"></a>

L'`keyEqual`istanza abbina gli elementi con un valore della chiave di partizione pari a. **`movie01`** 

Questo esempio definisce anche un'espressione di filtro dopo la riga di commento 2 che filtra qualsiasi elemento che non ha un **`actingschoolname`**valore.

`QueryEnhancedRequest`Combina la condizione chiave e l'espressione di filtro per la query.

```
    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 — Emissione utilizzando l'`keyEqual`interrogazione condizionale**  
Di seguito è riportato l'output dell'esecuzione del metodo. L'output mostra gli elementi con un `movieName` valore di **movie01** e non visualizza alcun elemento `actingSchoolName` uguale a. **`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'}
```

#### Interroga gli elementi per chiave di partizione e chiave di ordinamento
<a name="sort-type-query-conditional-example"></a>

`sortGreaterThanOrEqualTo``QueryConditional`**Perfeziona la corrispondenza della chiave di partizione (**movie01**) aggiungendo una condizione di chiave di ordinamento per valori maggiori o uguali a actor2.**

[`QueryConditional`i metodi](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html) che iniziano con `sort` richiedono che il valore della chiave di partizione corrisponda e perfezionano ulteriormente la query mediante un confronto basato sul valore della chiave di ordinamento. `Sort`nel nome del metodo non significa che i risultati siano ordinati, ma che per il confronto verrà utilizzato un valore di chiave di ordinamento.

Nel frammento seguente, modifichiamo la richiesta di query mostrata in precedenza dopo la riga di commento 3. Questo frammento sostituisce la query condizionale «KeyEqual» con la query condizionale "sortGreaterThanOrEqualTo" definita dopo la riga di commento 1a. Il codice seguente rimuove anche l'espressione del filtro.

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

**Example — Emissione utilizzando la `sortGreaterThanOrEqualTo` query condizionale**  
L'output seguente mostra i risultati dell'interrogazione. **La query restituisce gli elementi con un `movieName` valore uguale a **movie01** e solo gli elementi che hanno un `actorName` valore maggiore o uguale a actor2.** Poiché rimuoviamo il filtro, la query restituisce gli elementi che non hanno alcun valore per l'attributo. `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'}
```

# Esegui operazioni in batch
<a name="ddb-en-client-use-multiop-batch"></a>

[L'API DynamoDB Enhanced Client offre due metodi batch [`batchGetItem`, () e (](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#batchGetItem(java.util.function.Consumer)))`batchWriteItem`.](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#batchWriteItem(java.util.function.Consumer))

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

Con [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))questo metodo, è possibile recuperare fino a 100 singoli elementi su più tabelle in un'unica richiesta complessiva. L'esempio seguente utilizza le classi di [`MovieActor`](ddb-en-client-use-multirecord.md#ddb-en-client-use-movieactor-class)dati [`Customer`](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust)e mostrate in precedenza.

Nell'esempio dopo le righe 1 e 2, create `[ReadBatch](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/ReadBatch.html)` oggetti che successivamente aggiungete come parametri al `batchGetItem()` metodo dopo la riga di commento 3. 

Il codice dopo la riga di commento 1 crea il batch da leggere dalla `Customer` tabella. Il codice dopo la riga di commento 1a mostra l'uso di un `[GetItemEnhancedRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/GetItemEnhancedRequest.Builder.html)` builder che accetta un valore di chiave primaria e un valore di chiave di ordinamento per specificare l'elemento da leggere. Se la classe di dati ha una chiave composita, è necessario fornire sia il valore della chiave di partizione che il valore della chiave di ordinamento. 

A differenza di specificare i valori chiave per richiedere un elemento, è possibile utilizzare una classe di dati per richiedere un elemento, come mostrato dopo la riga di commento 1b. L'SDK estrae i valori chiave dietro le quinte prima di inviare la richiesta.

[Quando si specifica l'elemento utilizzando l'approccio basato su chiavi, come mostrato nelle due istruzioni successive a 2a, è anche possibile specificare che DynamoDB deve eseguire una lettura fortemente coerente.](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html) Quando viene utilizzato, il `consistentRead()` metodo deve essere utilizzato su tutti gli elementi richiesti per la stessa tabella.

Per recuperare gli elementi trovati da DynamoDB, utilizzate `[resultsForTable() ](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchGetResultPage.html#resultsForTable(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource))` il metodo mostrato dopo la riga di commento 4. Chiamate il metodo per ogni tabella che è stata letta nella richiesta. `resultsForTable()`restituisce un elenco di elementi trovati che è possibile elaborare utilizzando qualsiasi `java.util.List` metodo. Questo esempio registra ogni elemento.

Per scoprire elementi che DynamoDB non ha elaborato, utilizzate l'approccio dopo la riga di commento 5. La `BatchGetResultPage` classe utilizza il `[unprocessedKeysForTable()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchGetResultPage.html#unprocessedKeysForTable(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource))` metodo che consente di accedere a ogni chiave non elaborata. Il [riferimento all'BatchGetItem API](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchGetItem.html) contiene ulteriori informazioni sulle situazioni che generano elementi non elaborati.

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

Si supponga che i seguenti elementi siano presenti nelle due tabelle prima di eseguire il codice di esempio.

### Elementi nelle tabelle
<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'}
```

L'output seguente mostra gli elementi restituiti e registrati dopo la riga di commento 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()` Esempio
<a name="ddb-en-client-use-multiop-batch-write"></a>

Il `batchWriteItem()` metodo inserisce o elimina più elementi in una o più tabelle. È possibile specificare fino a 25 singole operazioni di inserimento o eliminazione nella richiesta. L'esempio seguente utilizza le classi [`ProductCatalog`](ddb-en-client-use.md#ddb-en-client-use-compare-cs3)e [`MovieActor`](ddb-en-client-use-multirecord.md#ddb-en-client-use-movieactor-class)model mostrate in precedenza.

`WriteBatch`gli oggetti vengono creati dopo le righe di commento 1 e 2. Per la `ProductCatalog` tabella, il codice inserisce un elemento ed elimina un elemento. Per la `MovieActor` tabella dopo la riga di commento 2, il codice inserisce due elementi e ne elimina uno.

Il `batchWriteItem` metodo viene chiamato dopo la riga di commento 3. Il `[builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchWriteItemEnhancedRequest.Builder.html)` parametro fornisce le richieste batch per ogni tabella.

L'`[BatchWriteResult](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchWriteResult.html)`oggetto restituito fornisce metodi separati per ogni operazione per visualizzare le richieste non elaborate. Il codice dopo la riga di commento 4a fornisce le chiavi per le richieste di cancellazione non elaborate e il codice dopo la riga di commento 4b fornisce gli elementi put non elaborati.

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

I seguenti metodi di supporto forniscono gli oggetti del modello per le operazioni di inserimento ed eliminazione.

### Metodi di supporto
<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.     }
```

Si supponga che le tabelle contengano i seguenti elementi prima di eseguire il codice di esempio.

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

Al termine del codice di esempio, le tabelle contengono gli elementi seguenti.

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

Si noti nella `MovieActor` tabella che l'elemento del `Blue Jasmine` filmato è stato sostituito con l'elemento utilizzato nella richiesta put acquisita tramite il metodo `getMovieActorBlanchettPartial()` helper. Se non è stato fornito un valore dell'attributo data bean, il valore nel database viene rimosso. Questo è il motivo per cui il risultato `actingSchoolName` è nullo per l'elemento del `Blue Jasmine` filmato.

**Nota**  
[Sebbene la documentazione dell'API suggerisca che è possibile utilizzare espressioni condizionali e che la capacità consumata e le metriche di raccolta possano essere restituite con richieste di immissione ed [eliminazione](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/DeleteItemEnhancedRequest.html) individuali, questo non è il caso in uno scenario di scrittura in batch.](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/PutItemEnhancedRequest.html) Per migliorare le prestazioni delle operazioni in batch, queste singole opzioni vengono ignorate.

# Esegui operazioni di transazione
<a name="ddb-en-client-use-multiop-trans"></a>

L'API DynamoDB Enhanced Client fornisce `transactGetItems()` i metodi e. `transactWriteItems()` I metodi di transazione dell'SDK for Java forniscono atomicità, coerenza, isolamento e durabilità (ACID) nelle tabelle DynamoDB, aiutandoti a mantenere la correttezza dei dati nelle tue applicazioni.

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

Il `[transactGetItems()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#transactGetItems(java.util.function.Consumer))` metodo accetta fino a 100 richieste individuali di articoli. Tutti gli elementi vengono letti in un'unica transazione atomica. L'*Amazon DynamoDB Developer* Guide contiene informazioni sulle [condizioni che causano il fallimento di `transactGetItems()` un metodo](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-txgetitems) e anche sul livello di isolamento utilizzato durante le chiamate. `[transactGetItem()](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-isolation)`

Dopo la riga di commento 1 nell'esempio seguente, il codice chiama il `transactGetItems()` metodo con un `[builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactGetItemsEnhancedRequest.Builder.html)` parametro. Il 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))` viene richiamato tre volte con un oggetto dati che contiene i valori chiave che l'SDK utilizzerà per generare la richiesta finale.

La richiesta restituisce un elenco di `[Document](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Document.html)` oggetti dopo la riga di commento 2. L'elenco di documenti restituito contiene istanze di [Document](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Document.html) non nulle dei dati degli articoli nello stesso ordine richiesto. Il `[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))` metodo converte un oggetto non tipizzato in un `Document` oggetto Java digitato se sono stati restituiti i dati dell'elemento, altrimenti restituisce null.

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

Le tabelle DynamoDB contengono i seguenti elementi prima dell'esecuzione dell'esempio di codice.

```
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'}
```

Viene registrato il seguente output. Se un elemento viene richiesto ma non viene trovato, non viene restituito, come nel caso della richiesta per il film denominato`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'}
```

## Esempi di `transactWriteItems()`
<a name="ddb-en-client-use-multiop-trans-writeitems"></a>

`[transactWriteItems()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#transactWriteItems(java.util.function.Consumer))`Accetta fino a 100 azioni di inserimento, aggiornamento o eliminazione in una singola transazione atomica su più tabelle. La *Amazon DynamoDB Developer* Guide contiene dettagli sulle restrizioni e le condizioni di errore del funzionamento del servizio [DynamoDB sottostante](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-txwriteitems).

### Esempio di base
<a name="ddb-en-client-use-multiop-trans-writeitems-basic"></a>

Nell'esempio seguente, sono richieste quattro operazioni per due tabelle. Le classi del modello corrispondenti [`ProductCatalog`](ddb-en-client-use.md#ddb-en-client-use-compare-cs3)e [`MovieActor`](ddb-en-client-use-multirecord.md#ddb-en-client-use-movieactor-class)sono state mostrate in precedenza.

Ciascuna delle tre operazioni possibili (put, update ed delete) utilizza un parametro di richiesta dedicato per specificare i dettagli. 

Il codice dopo la riga di commento 1 mostra la semplice variazione del metodo. `addPutItem()` Il metodo accetta un `[MappedTableResource](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/MappedTableResource.html)` oggetto e l'istanza dell'oggetto dati da inserire. L'istruzione dopo la riga di commento 2 mostra la variante che accetta un'`[TransactPutItemEnhancedRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactPutItemEnhancedRequest.html)`istanza. Questa variante consente di aggiungere altre opzioni nella richiesta, ad esempio un'espressione di condizione. Un [esempio](#ddb-en-client-use-multiop-trans-writeitems-opcondition) successivo mostra un'espressione di condizione per una singola operazione.

Viene richiesta un'operazione di aggiornamento dopo la riga di commento 3. `[TransactUpdateItemEnhancedRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactUpdateItemEnhancedRequest.Builder.html)`ha un `ignoreNulls()` metodo che consente di configurare ciò che l'SDK fa con `null` i valori sull'oggetto del modello. Se il `ignoreNulls()` metodo restituisce true, l'SDK non rimuove i valori degli attributi della tabella per gli attributi degli oggetti dati che sono. `null` Se il `ignoreNulls()` metodo restituisce false, l'SDK richiede al servizio DynamoDB di rimuovere gli attributi dall'elemento nella tabella. Il valore predefinito per `ignoreNulls` è false.

L'istruzione dopo la riga di commento 4 mostra la variazione di una richiesta di eliminazione che richiede un oggetto dati. Il client avanzato estrae i valori chiave prima di inviare la richiesta finale.

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

I seguenti metodi di supporto forniscono gli oggetti dati per i parametri. `add*Item`

#### Metodi di supporto
<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;
    }
```

Le tabelle DynamoDB contengono i seguenti elementi prima dell'esecuzione dell'esempio di codice.

```
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'}
```

I seguenti elementi sono presenti nelle tabelle al termine dell'esecuzione del codice.

```
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'}
```

L'elemento sulla riga 2 è stato eliminato e le righe 3 e 5 mostrano gli elementi che sono stati inseriti. La riga 4 mostra l'aggiornamento della riga 1. Il `price` valore è l'unico valore che è cambiato sull'elemento. Se `ignoreNulls()` avesse restituito false, la riga 4 sarebbe simile alla riga seguente.

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

### Esempio di controllo delle condizioni
<a name="ddb-en-client-use-multiop-trans-writeitems-checkcond"></a>

L'esempio seguente mostra l'uso di un controllo delle condizioni. Un controllo delle condizioni viene utilizzato per verificare l'esistenza di un elemento o per verificare la condizione di attributi specifici di un elemento nel database. L'articolo registrato nel controllo delle condizioni non può essere utilizzato in un'altra operazione della transazione.

**Nota**  
Non è possibile fare riferimento allo stesso item con diverse operazioni all'interno della stessa transazione. Ad esempio, non è possibile eseguire un controllo delle condizioni e anche tentare di aggiornare lo stesso articolo nella stessa transazione.

L'esempio mostra una per ogni tipo di operazione in una richiesta transazionale di scrittura di elementi. Dopo la riga di commento 2, il `addConditionCheck()` metodo fornisce la condizione che fallisce la transazione se il `conditionExpression` parametro restituisce un risultato positivo. `false` L'espressione di condizione restituita dal metodo mostrato nel blocco Helper methods verifica se l'anno di premiazione del film non `Sophie's Choice` è uguale a. `1982` In caso affermativo, l'espressione restituisce `false` e la transazione ha esito negativo.

Questa guida illustra in modo approfondito [le espressioni](ddb-en-client-expressions.md) in un altro argomento.

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

I seguenti metodi di supporto vengono utilizzati nell'esempio di codice precedente.

#### Metodi di supporto
<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;
    }
```

Le tabelle DynamoDB contengono i seguenti elementi prima dell'esecuzione dell'esempio di codice.

```
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'}
```

I seguenti elementi sono presenti nelle tabelle al termine dell'esecuzione del codice.

```
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'}
```

Gli elementi rimangono invariati nelle tabelle perché la transazione non è riuscita. Il `actingYear` valore del filmato `Sophie's Choice` è`1982`, come mostrato nella riga 2 degli elementi della tabella prima della chiamata del `transactWriteItem()` metodo.

Per acquisire le informazioni di annullamento della transazione, racchiudi la chiamata al `transactWriteItems()` metodo in un `try` blocco e `catch` il [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). Dopo la riga di commento 4 dell'esempio, il codice registra ogni `[CancellationReason](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/CancellationReason.html)` oggetto. Poiché il codice che segue la riga di commento 3 dell'esempio specifica che devono essere restituiti i valori per l'elemento che ha causato il fallimento della transazione, nel registro vengono visualizzati i valori non elaborati del database per l'elemento del `Sophie's Choice` filmato.

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

### Esempio di condizione di operazione singola
<a name="ddb-en-client-use-multiop-trans-writeitems-opcondition"></a>

L'esempio seguente mostra l'uso di una condizione su una singola operazione in una richiesta di transazione. L'operazione di eliminazione dopo la riga di commento 1 contiene una condizione che verifica il valore dell'elemento di destinazione dell'operazione rispetto al database. In questo esempio, l'espressione condizionale creata con il metodo helper dopo la riga di commento 2 specifica che l'elemento deve essere eliminato dal database se l'anno di recitazione del film non è uguale al 2013.

[Le espressioni](ddb-en-client-expressions.md) vengono discusse più avanti in questa guida.

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

I seguenti metodi di supporto vengono utilizzati nell'esempio di codice precedente.

#### Metodi di supporto
<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;
    }
```

Le tabelle DynamoDB contengono i seguenti elementi prima dell'esecuzione dell'esempio di codice.

```
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'}
```

I seguenti elementi sono presenti nelle tabelle al termine dell'esecuzione del codice.

```
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'}
```

Gli elementi rimangono invariati nelle tabelle perché la transazione non è riuscita. Il `actingYear` valore del filmato `Blue Jasmine` è quello `2013` mostrato alla riga 2 dell'elenco degli elementi prima dell'esecuzione dell'esempio di codice.

Le seguenti righe vengono registrate nella console.

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

# Usa indici secondari
<a name="ddb-en-client-use-secindex"></a>

Gli indici secondari migliorano l'accesso ai dati definendo chiavi alternative da utilizzare nelle operazioni di interrogazione e scansione. Gli indici secondari globali (GSI) hanno una chiave di partizione e una chiave di ordinamento che possono essere diverse da quelle della tabella di base. Al contrario, gli indici secondari locali (LSI) utilizzano la chiave di partizione dell'indice primario.

## Annota la classe di dati con annotazioni dell'indice secondario
<a name="ddb-en-client-use-secindex-annomodel"></a>

Gli attributi che partecipano agli indici secondari richiedono l'annotazione `@DynamoDbSecondaryPartitionKey` o`@DynamoDbSecondarySortKey`.

La classe seguente mostra le annotazioni per due indici. Il nome GSI *SubjectLastPostedDateIndex*utilizza l'`Subject`attributo per la chiave di partizione e il `LastPostedDateTime` per la chiave di ordinamento. L'LSI denominato *ForumLastPostedDateIndex*utilizza la `ForumName` come chiave di partizione e `LastPostedDateTime` come chiave di ordinamento.

Si noti che l'`Subject`attributo ha un duplice ruolo. È la chiave di ordinamento della chiave primaria e la chiave di partizione del GSI denominato. *SubjectLastPostedDateIndex*

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

La `MessageThread` classe è adatta all'uso come classe di dati per la [tabella Thread di esempio](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AppendixSampleTables.html) nella *Amazon DynamoDB Developer Guide*.

#### Importazioni
<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 +
                '}';
    }
}
```

## Crea l'indice
<a name="ddb-en-client-use-secindex-confindex"></a>

A partire dalla versione 2.20.86 dell'SDK for Java, il `createTable()` metodo genera automaticamente indici secondari dalle annotazioni delle classi di dati. Per impostazione predefinita, tutti gli attributi della tabella di base vengono copiati in un indice e i valori di throughput assegnati sono 20 unità di capacità di lettura e 20 unità di capacità di scrittura.

Tuttavia, se si utilizza una versione SDK precedente alla 2.20.86, è necessario creare l'indice insieme alla tabella, come mostrato nell'esempio seguente. Questo esempio crea i due indici per la tabella. `Thread` Il parametro [builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/CreateTableEnhancedRequest.Builder.html) dispone di metodi per configurare entrambi i tipi di indici, come illustrato dopo le righe di commento 1 e 2. Utilizzate il `indexName()` metodo del generatore di indici per associare i nomi degli indici specificati nelle annotazioni delle classi di dati al tipo di indice desiderato.

Questo codice configura tutti gli attributi della tabella in modo che finiscano in entrambi gli indici dopo le righe di commento 3 e 4. Ulteriori informazioni sulle [proiezioni degli attributi](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSI.html#LSI.Projections) sono disponibili nella *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))
                )
        );
```

## Esegui una query utilizzando un indice
<a name="ddb-en-client-use-secindex-query"></a>

L’esempio seguente esegue una query sull’indice globale secondario *ForumLastPostedDateIndex*.

Dopo la riga di commento 2, create un [QueryConditional](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html)oggetto che è necessario quando si chiama il [DynamoDbIndexmetodo.query ()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbIndex.html#query(java.util.function.Consumer)). 

Si ottiene un riferimento all'indice che si desidera interrogare dopo la riga di commento 3 inserendo il nome dell'indice. Dopo la riga di commento 4, si chiama il `query()` metodo sull'indice che passa l'`QueryConditional`oggetto. 

È inoltre possibile configurare la query per restituire tre valori di attributo, come mostrato dopo la riga di commento 5. Se non `attributesToProject()` viene chiamata, la query restituisce tutti i valori degli attributi. Notate che i nomi degli attributi specificati iniziano con lettere minuscole. Questi nomi di attributi corrispondono a quelli utilizzati nella tabella, non necessariamente ai nomi degli attributi della classe di dati.

Dopo la riga di commento 6, scorri i risultati e registra ogni elemento restituito dalla query, inoltre memorizzalo nell'elenco per restituirlo al chiamante.

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

I seguenti elementi sono presenti nel database prima dell'esecuzione della query.

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

Le istruzioni di registrazione alle righe 1 e 6 generano il seguente output della console.

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

*La query ha restituito elementi con un `forumName` valore di *Forum02* e un `lastPostedDateTime` valore maggiore o uguale a 2023.03.31.* I risultati mostrano `message` valori con una stringa vuota sebbene `message` gli attributi abbiano valori nell'indice. Questo perché l'attributo message non è stato proiettato dal codice dopo la riga di commento 5. 