

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

# Découvrez les principes de base de l'API client améliorée DynamoDB
<a name="ddb-en-client-use"></a>

Cette rubrique décrit les fonctionnalités de base de l'API DynamoDB Enhanced Client et les compare à l'API client [DynamoDB standard](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/package-summary.html).

Si vous découvrez l'API DynamoDB Enhanced Client, nous vous recommandons de suivre [le didacticiel d'introduction pour vous familiariser avec les](ddb-en-client-getting-started.md) classes fondamentales.

## Éléments DynamoDB en Java
<a name="ddb-en-client-use-usecase"></a>

Les tables DynamoDB stockent des éléments. Selon votre cas d'utilisation, les éléments du côté Java peuvent prendre la forme de données structurées statiquement ou de structures créées dynamiquement. 

Si votre cas d'utilisation nécessite des éléments dotés d'un ensemble cohérent d'attributs, utilisez des [classes annotées](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean) ou utilisez un [générateur](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-builder) pour générer le type statique `TableSchema` approprié. 

Sinon, si vous devez stocker des éléments composés de différentes structures, créez un`DocumentTableSchema`. `DocumentTableSchema`fait partie de l'[API Enhanced Document](ddb-en-client-doc-api.md) et ne nécessite qu'une clé primaire de type statique et fonctionne avec des `EnhancedDocument` instances pour contenir les éléments de données. L'API de document améliorée est abordée dans une autre [rubrique.](ddb-en-client-doc-api.md)

## Types d'attributs pour les classes de modèles de données
<a name="ddb-en-client-use-types"></a>

Bien que DynamoDB prenne [en charge un petit nombre de types d'attributs](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes) par rapport au système de types riches de Java, l'API DynamoDB Enhanced Client fournit des mécanismes permettant de convertir les membres d'une classe Java vers et depuis les types d'attributs DynamoDB.

Les types d'attributs (propriétés) de vos classes de données Java doivent être des types d'objets et non des primitives. Par exemple, utilisez toujours des types de données `Long` et des `Integer` objets, `long` et non des `int` primitives.

[Par défaut, l'API DynamoDB Enhanced Client prend en charge les convertisseurs d'attributs pour un grand nombre de types, [tels que](https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html) Integer [[BigDecimal](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/BigDecimalAttributeConverter.html), String et](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html) Instant.](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/InstantAsStringAttributeConverter.html) La liste apparaît dans les [classes d'implémentation connues de l' AttributeConverter interface](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverter.html). La liste comprend de nombreux types et collections tels que des cartes, des listes et des ensembles.

Pour stocker les données d'un type d'attribut qui n'est pas pris en charge par défaut ou qui n'est pas conforme à la JavaBean convention, vous pouvez écrire une `AttributeConverter` implémentation personnalisée pour effectuer la conversion. Consultez la section sur la conversion d'attributs pour un [exemple](ddb-en-client-adv-features-conversion.md#ddb-en-client-adv-features-conversion-example).

Pour stocker les données d'un type d'attribut dont la classe est conforme à la spécification Java Beans (ou d'une [classe de données immuable](ddb-en-client-use-immut.md)), vous pouvez adopter deux approches. 
+ Si vous avez accès au fichier source, vous pouvez annoter la classe avec `@DynamoDbBean` (ou`@DynamoDbImmutable`). La section qui traite des attributs imbriqués présente des [exemples](ddb-en-client-adv-features-nested.md#ddb-en-client-adv-features-nested-map-anno) d'utilisation de classes annotées.
+ Si vous n'avez pas accès au fichier source de la classe de JavaBean données pour l'attribut (ou si vous ne souhaitez pas annoter le fichier source d'une classe à laquelle vous avez accès), vous pouvez utiliser l'approche du générateur. Cela crée un schéma de table sans définir les clés. Vous pouvez ensuite imbriquer ce schéma de table dans un autre schéma de table pour effectuer le mappage. La section des attributs imbriqués contient un [exemple](ddb-en-client-adv-features-nested.md#ddb-en-client-adv-features-nested-map-builder) illustrant l'utilisation de schémas imbriqués.

### Valeurs nulles
<a name="ddb-en-client-use-types-nulls"></a>

Lorsque vous utilisez `putItem` cette méthode, le client amélioré n'inclut pas les attributs à valeur nulle d'un objet de données mappé dans la demande adressée à DynamoDB.

Le comportement par défaut du SDK pour les `updateItem` demandes supprime les attributs de l'élément de DynamoDB qui sont définis sur null dans l'objet que vous soumettez dans la méthode. `updateItem` Si vous avez l'intention de mettre à jour certaines valeurs d'attributs et de conserver les autres inchangées, deux options s'offrent à vous.
+ Récupérez l'élément (en utilisant`getItem`) avant de modifier les valeurs. En utilisant cette approche, le SDK soumet toutes les valeurs anciennes et mises à jour à DynamoDB.
+ Utilisez le `[IgnoreNullsMode](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/IgnoreNullsMode.html).SCALAR_ONLY` ou `IgnoreNullsMode.MAPS_ONLY` lorsque vous créez la demande pour mettre à jour l'élément. Les deux modes ignorent les propriétés à valeur nulle de l'objet qui représentent des attributs scalaires dans DynamoDB. La [Mettre à jour les éléments contenant des types complexes](ddb-en-client-adv-features-nested.md#ddb-en-client-adv-features-nested-updates) rubrique de ce guide contient plus d'informations sur les `IgnoreNullsMode` valeurs et sur la manière de travailler avec des types complexes.

L'exemple suivant illustre `ignoreNullsMode()` la `updateItem()` méthode.

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

## Méthodes de base du client DynamoDB Enhanced
<a name="ddb-en-client-use-basic-ops"></a>

Les méthodes de base du client amélioré correspondent aux opérations de service DynamoDB dont elles portent le nom. Les exemples suivants montrent la variante la plus simple de chaque méthode. Vous pouvez personnaliser chaque méthode en transmettant un objet de demande amélioré. Les objets de requête améliorés offrent la plupart des fonctionnalités disponibles dans le client DynamoDB standard. Ils sont entièrement documentés dans le Guide de référence des AWS SDK for Java 2.x API.

L'exemple utilise ce qui [classe `Customer`](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust) est indiqué précédemment.

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

## Comparez le client DynamoDB amélioré au client DynamoDB standard
<a name="ddb-en-client-use-compare"></a>

Les APIs clients DynamoDB (standard [et](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/package-summary.html) amélioré) vous [permettent](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/package-summary.html) de travailler avec des tables DynamoDB pour effectuer des opérations CRUD (création, lecture, mise à jour et suppression) au niveau des données. La différence entre le client APIs réside dans la manière dont cela est accompli. Avec le client standard, vous travaillez directement avec des attributs de données de bas niveau. L'API client améliorée utilise des classes Java familières et correspond à l'API de bas niveau utilisée en arrière-plan.

Bien que les deux clients APIs prennent en charge les opérations au niveau des données, le client DynamoDB standard prend également en charge les opérations au niveau des ressources. Les opérations au niveau des ressources gèrent la base de données, telles que la création de sauvegardes, la liste des tables et la mise à jour des tables. L'API client améliorée prend en charge un certain nombre d'opérations au niveau des ressources, telles que la création, la description et la suppression de tables.

Pour illustrer les différentes approches utilisées par les deux clients APIs, les exemples de code suivants montrent la création de la même `ProductCatalog` table à l'aide du client standard et du client amélioré.

### Comparaison : création d'une table à l'aide du 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))
);
```

### Comparaison : création d'une table à l'aide du client DynamoDB Enhanced
<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))
        )
);
```

Le client amélioré utilise la classe de données annotée suivante. Le client DynamoDB Enhanced fait correspondre les types de données Java aux types de données DynamoDB pour un code moins détaillé et plus facile à suivre. `ProductCatalog`est un exemple d'utilisation d'une classe immuable avec le client DynamoDB Enhanced. L'utilisation de classes immuables pour les classes de données mappées est [abordée plus loin](ddb-en-client-use-immut.md) dans cette rubrique.

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

Les deux exemples de code suivants illustrant une écriture par lots illustrent le caractère verbeux et le manque de sécurité de type liés à l'utilisation du client standard par opposition au client amélioré.

### Comparaison : écriture par lots à l'aide du 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());
    }
```

### Comparaison : écriture par lots à l'aide du client DynamoDB Enhanced
<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());
    }
```

# Travaillez avec des classes de données immuables
<a name="ddb-en-client-use-immut"></a>

La fonctionnalité de mappage de l'API DynamoDB Enhanced Client fonctionne avec des classes de données immuables. Une classe immuable ne possède que des méthodes getter et nécessite une classe de générateur que le kit SDK utilise pour créer des instances de la classe. Au lieu d'utiliser l'`@DynamoDbBean`annotation comme indiqué dans la [classe Customer](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust), les classes immuables utilisent l'`@DynamoDbImmutable`annotation, qui prend un paramètre indiquant la classe de générateur à utiliser.

La classe suivante est une version immuable de. `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); }
    }
}
```

Vous devez satisfaire aux exigences suivantes lorsque vous annotez une classe de données avec`@DynamoDbImmutable`.

1. Chaque méthode qui n'est pas une substitution `Object.class` et qui n'a pas été annotée `@DynamoDbIgnore` doit être un getter pour un attribut de la table DynamoDB.

1. Chaque getter doit avoir un setter correspondant distinguant majuscules et minuscules dans la classe de générateur.

1. Seule l'une des conditions de construction suivantes doit être respectée.
   + La classe de générateur doit avoir un constructeur public par défaut.
   + La classe de données doit avoir une méthode statique publique nommée `builder()` qui ne prend aucun paramètre et renvoie une instance de la classe de générateur. Cette option est affichée dans la `Customer` classe immuable.

1.  La classe builder doit avoir une méthode publique nommée `build()` qui ne prend aucun paramètre et renvoie une instance de la classe immuable. 

Pour créer un `TableSchema` pour votre classe immuable, utilisez la `fromImmutableClass()` méthode on `TableSchema` comme indiqué dans l'extrait suivant.

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

Tout comme vous pouvez créer une table DynamoDB à partir d'une classe mutable, vous pouvez en créer une à partir d'une classe immuable avec *un* appel unique à of, comme indiqué dans l'exemple `DynamoDbTable` d'`createTable()`extrait de code suivant.

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

## Utilisez des bibliothèques tierces, telles que Lombok
<a name="ddb-en-client-use-immut-lombok"></a>

Les bibliothèques tierces, telles que [Project Lombok](https://projectlombok.org/), aident à générer du code standard associé à des objets immuables. L'API DynamoDB Enhanced Client fonctionne avec ces bibliothèques tant que les classes de données respectent les conventions détaillées dans cette section. 

L'exemple suivant montre la `CustomerImmutable` classe immuable avec les annotations Lombok. Notez comment la `onMethod` fonctionnalité de Lombok copie les annotations DynamoDB basées sur les attributs, telles que, sur le code généré. `@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;
}
```

# Expressions et conditions d'utilisation
<a name="ddb-en-client-expressions"></a>

[Les expressions de l'API DynamoDB Enhanced Client sont des représentations Java des expressions DynamoDB.](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.html)

L'API client améliorée DynamoDB utilise trois types d'expressions :

[Expression](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Expression.html)  
La `Expression` classe est utilisée lorsque vous définissez des conditions et des filtres.

[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)  
Ce type d'expression représente [les conditions clés](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.KeyConditionExpressions) pour les opérations de requête.

[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)  
Cette classe vous aide à écrire des expressions de [mise à jour DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html) et est actuellement utilisée dans le framework d'extension lorsque vous mettez à jour un élément.

## Anatomie de l'expression
<a name="ddb-en-client-expressions-compoonents"></a>

Une expression est composée des éléments suivants :
+ Une expression sous forme de chaîne (obligatoire). La chaîne contient une expression logique DynamoDB avec des noms d'espaces réservés pour les noms d'attributs et les valeurs d'attribut.
+ Une carte des valeurs d'expression (généralement obligatoire).
+ Carte des noms d'expressions (facultatif).

Utilisez un générateur pour générer un `Expression` objet qui prend la forme générale suivante.

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

`Expression`s nécessitent généralement une carte des valeurs d'expression. La carte fournit les valeurs des espaces réservés dans l'expression sous forme de chaîne. La clé de carte se compose du nom de l'espace réservé précédé de deux points (`:`) et la valeur de la carte est une instance de [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 dispose de méthodes pratiques pour générer une `AttributeValue` instance à partir d'un littéral. Vous pouvez également utiliser le `AttributeValue.Builder` pour générer une `AttributeValue` instance.

L'extrait suivant montre une carte avec deux entrées après la ligne de commentaire 2. La chaîne transmise à la `expression()` méthode, affichée après la ligne de commentaire 1, contient les espaces réservés que DynamoDB résout avant d'effectuer l'opération. Cet extrait ne contient pas de carte des noms d'expressions, car le *prix* est un nom d'attribut autorisé.

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

Si un nom d'attribut dans la table DynamoDB est un mot réservé, commence par un chiffre ou contient un espace, une carte des noms d'expressions est requise pour le. `Expression`

Par exemple, si le nom de l'attribut ne figurait `1price` pas `price` dans l'exemple de code précédent, celui-ci devra être modifié comme indiqué dans l'exemple suivant.

```
        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 espace réservé pour le nom d'une expression commence par le signe dièse (`#`). Une entrée pour la carte des noms d'expressions utilise l'espace réservé comme clé et le nom de l'attribut comme valeur. La carte est ajoutée au générateur d'expressions avec la `expressionNames()` méthode. DynamoDB résout le nom de l'attribut avant d'effectuer l'opération.

Les valeurs d'expression ne sont pas obligatoires si une fonction est utilisée dans l'expression sous forme de chaîne. Voici un exemple de fonction d'expression`attribute_exists(<attribute_name>)`.

L'exemple suivant crée un fichier `Expression` qui utilise une fonction [DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions). Dans cet exemple, la chaîne d'expression n'utilise aucun espace réservé. Cette expression peut être utilisée lors d'une `putItem` opération visant à vérifier si un élément existe déjà dans la base de données avec une valeur d'`movie`attribut égale à celle de l'`movie`attribut de l'objet de données.

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

Le guide du développeur DynamoDB contient des informations complètes sur les expressions de [bas niveau utilisées](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.html) avec DynamoDB.

## Expressions de conditions et conditions
<a name="ddb-en-client-expressions-cond"></a>

Lorsque vous utilisez les `deleteItem()` méthodes`putItem()`, et`updateItem()`, ainsi que lorsque vous utilisez des opérations de transaction et de traitement par lots, vous utilisez `[Expression](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Expression.html)` des objets pour spécifier les conditions que DynamoDB doit respecter pour poursuivre l'opération. Ces expressions sont appelées expressions de condition. Pour un exemple, consultez l'expression de condition utilisée dans la `addDeleteItem()` méthode (après la ligne de commentaire 1) de l'[exemple de transaction](ddb-en-client-use-multiop-trans.md#ddb-en-client-use-multiop-trans-writeitems-opcondition) présenté dans ce guide.

Lorsque vous utilisez les `query()` méthodes, une condition est exprimée sous la forme d'un [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 dispose de plusieurs méthodes pratiques statiques qui vous aident à écrire les critères qui déterminent les éléments à lire dans DynamoDB.

Pour des exemples`QueryConditionals`, consultez le premier exemple de code de la [`Query`exemples de méthodes](ddb-en-client-use-multirecord.md#ddb-en-client-use-multirecord-query-example) section de ce guide.

## Expressions de filtrage
<a name="ddb-en-client-expressions-filter"></a>

Les expressions de filtre sont utilisées dans les opérations de numérisation et de requête pour filtrer les éléments renvoyés. 

Une expression de filtre est appliquée une fois que toutes les données ont été lues dans la base de données. Le coût de lecture est donc le même que s'il n'y avait pas de filtre. [Le guide du *développeur Amazon DynamoDB* contient plus d'informations sur l'utilisation d'expressions de filtre pour [les opérations de requête et](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.FilterExpression) de numérisation.](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.FilterExpression)

L'exemple suivant montre une expression de filtre ajoutée à une demande d'analyse. Les critères limitent les articles retournés aux articles dont le prix se situe entre 8,00 et 80,00€ inclus.

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

## Expressions de mise à jour
<a name="ddb-en-client-expressions-update"></a>

La méthode du client `updateItem()` DynamoDB Enhanced fournit une méthode standard pour mettre à jour des éléments dans DynamoDB. Toutefois, lorsque vous avez besoin de fonctionnalités supplémentaires, [UpdateExpressions](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/update/UpdateExpression.html)fournissez une représentation sécurisée de la syntaxe de l'expression de mise à jour [DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html). Par exemple, vous pouvez l'utiliser `UpdateExpressions` pour augmenter les valeurs sans lire au préalable les éléments de DynamoDB, ou pour ajouter des membres individuels à une liste. Les expressions de mise à jour sont actuellement disponibles dans les extensions personnalisées de la `updateItem()` méthode.

Pour un exemple utilisant des expressions de mise à jour, consultez l'[exemple d'extension personnalisée](ddb-en-client-extensions-custom.md) présenté dans ce guide.

De plus amples informations sur les expressions de mise à jour sont disponibles dans le [manuel du développeur Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html).

# Travailler avec des résultats paginés : scans et requêtes
<a name="ddb-en-client-use-multirecord"></a>

*Les `scan` `batch` méthodes `query` et de l'API DynamoDB Enhanced Client renvoient des réponses contenant une ou plusieurs pages.* Une page contient un ou plusieurs éléments. Votre code peut traiter la réponse page par page ou traiter des éléments individuels.

Une réponse paginée renvoyée par le `DynamoDbEnhancedClient` client synchrone renvoie un [PageIterable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/PageIterable.html)objet, tandis qu'une réponse renvoyée par le client asynchrone `DynamoDbEnhancedAsyncClient` renvoie un objet. [PagePublisher](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/PagePublisher.html)

Cette section traite du traitement des résultats paginés et fournit des exemples d'utilisation du scan et de la requête APIs.

## Analyser une table
<a name="ddb-en-client-use-multirecord-scan"></a>

La [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))méthode du SDK correspond à l'opération [DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html) du même nom. L'API DynamoDB Enhanced Client propose les mêmes options, mais elle utilise un modèle d'objet familier et gère la pagination à votre place.

Tout d'abord, nous explorons l'`PageIterable`interface en examinant la `scan` méthode de la classe de mappage synchrone, [DynamoDbTable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html).

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

L'exemple suivant montre la `scan` méthode qui utilise une [expression](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Expression.html) pour filtrer les éléments renvoyés. [ProductCatalog](ddb-en-client-use.md#ddb-en-client-use-compare-cs3)Il s'agit de l'objet modèle présenté précédemment.

L'expression de filtrage affichée après la ligne de commentaire 2 limite les `ProductCatalog` articles renvoyés à ceux dont le prix est compris entre 8,00 et 80,00€ inclusivement.

Cet exemple exclut également les `isbn` valeurs en utilisant la `attributesToProject` méthode indiquée après la ligne de commentaire 1.

Après la ligne de commentaire 3`pagedResults`, l'`PageIterable`objet est renvoyé par la `scan` méthode. La `stream` méthode de `PageIterable` renvoi d'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)objet, que vous pouvez utiliser pour traiter les pages. Dans cet exemple, le nombre de pages est compté et enregistré.

À partir de la ligne de commentaire 4, l'exemple montre deux variantes d'accès aux `ProductCatalog` éléments. La version située après la ligne de commentaire 4a parcourt chaque page et trie et enregistre les éléments de chaque page. La version située après la ligne de commentaire 4b ignore l'itération de la page et accède directement aux éléments.

L'`PageIterable`interface offre plusieurs manières de traiter les résultats grâce à ses deux interfaces parentes : [https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html](https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html)et [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/pagination/sync/SdkIterable.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/pagination/sync/SdkIterable.html). `Iterable`apporte les `forEach` `spliterator` méthodes, et `SdkIterable` apporte la `stream` méthode. `iterator`

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

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

La `scan` méthode asynchrone renvoie les résultats sous forme d'`PagePublisher`objet. L'`PagePublisher`interface comporte deux `subscribe` méthodes que vous pouvez utiliser pour traiter les pages de réponse. L'une des `subscribe` méthodes provient de l'interface `org.reactivestreams.Publisher` parent. Pour traiter les pages à l'aide de cette première option, transmettez une `[Subscriber](https://www.reactive-streams.org/reactive-streams-1.0.0-javadoc/org/reactivestreams/Subscriber.html)` instance à la `subscribe` méthode. Le premier exemple qui suit montre l'utilisation de la `subscribe` méthode.

La deuxième `subscribe` méthode provient de l'[SdkPublisher](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/SdkPublisher.html)interface. Cette version de `subscribe` accepte un [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)plutôt qu'un`Subscriber`. Cette variante de `subscribe` méthode est illustrée dans le deuxième exemple qui suit.

L'exemple suivant montre la version asynchrone de la `scan` méthode qui utilise la même expression de filtre que dans l'exemple précédent. 

Après la ligne de commentaire 3, `DynamoDbAsyncTable.scan` renvoie un `PagePublisher` objet. Sur la ligne suivante, le code crée une instance de l'`org.reactivestreams.Subscriber`interface`ProductCatalogSubscriber`, qui s'abonne à la ligne de commentaire 4 `PagePublisher` après.

L'`Subscriber`objet collecte les `ProductCatalog` éléments de chaque page de la `onNext` méthode après la ligne de commentaire 8 dans l'exemple `ProductCatalogSubscriber` de classe. Les éléments sont stockés dans la `List` variable privée et sont accessibles dans le code d'appel avec la `ProductCatalogSubscriber.getSubscribedItems()` méthode. Ceci est appelé après la ligne de commentaire 5.

Une fois la liste récupérée, le code trie tous les `ProductCatalog` articles par prix et enregistre chaque article.

La `ProductCatalogSubscriber` classe [CountDownLatch](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html)in bloque le fil d'appel jusqu'à ce que tous les éléments aient été ajoutés à la liste avant de continuer après la ligne de commentaire 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;
        }
    }
```

L'exemple d'extrait de code suivant utilise la version de la `PagePublisher.subscribe` méthode qui accepte une ligne de commentaire `Consumer` 6 après. Le paramètre Java lambda consomme des pages, qui traitent ensuite chaque élément. Dans cet exemple, chaque page est traitée et les éléments de chaque page sont triés puis enregistrés.

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

La `items` méthode qui consiste à `PagePublisher` déballer les instances du modèle afin que votre code puisse traiter les éléments directement. Cette approche est illustrée dans l'extrait suivant.

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

## Interroger une table
<a name="ddb-en-client-use-multirecord-query"></a>

Vous pouvez utiliser le client DynamoDB amélioré pour interroger votre table et récupérer plusieurs éléments répondant à des critères spécifiques. La [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))méthode recherche des éléments en fonction des valeurs de clé primaire à l'`@DynamoDbPartitionKey`aide `@DynamoDbSortKey` des annotations facultatives définies dans votre classe de données.

La `query()` méthode nécessite une valeur de clé de partition et accepte éventuellement les conditions de clé de tri pour affiner davantage les résultats. Comme l'`scan`API, les requêtes renvoient a `PageIterable` pour les appels synchrones et a `PagePublisher` pour les appels asynchrones.

### `Query`exemples de méthodes
<a name="ddb-en-client-use-multirecord-query-example"></a>

L'exemple de code de `query()` méthode qui suit utilise la `MovieActor` classe. La classe de données définit une clé primaire composite composée de l'**`movie`**attribut en tant que clé de partition et de l'**`actor`**attribut en tant que clé de tri. 

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

Les exemples de code qui suivent portent sur les éléments suivants.

#### Éléments du `MovieActor` tableau
<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'}
```

Le code suivant définit deux `QueryConditional` instances : `keyEqual` (après la ligne de commentaire 1) et `sortGreaterThanOrEqualTo` (après la ligne de commentaire 1a).

#### Éléments de requête par clé de partition
<a name="keyEqual-query-conditional-example"></a>

L'`keyEqual`instance fait correspondre les éléments dont la valeur de clé de partition est de **`movie01`**. 

Cet exemple définit également une expression de filtre après la ligne de commentaire 2 qui filtre tout élément dépourvu de **`actingschoolname`**valeur.

`QueryEnhancedRequest`Combine la condition clé et l'expression du filtre pour la requête.

```
    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 — Sortie utilisant la `keyEqual` requête conditionnelle**  
Voici le résultat de l'exécution de la méthode. La sortie affiche les éléments dont `movieName` la valeur est **movie01** et n'affiche aucun élément dont la valeur est `actingSchoolName` égale à. **`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'}
```

#### Rechercher des éléments par clé de partition et clé de tri
<a name="sort-type-query-conditional-example"></a>

`sortGreaterThanOrEqualTo``QueryConditional`**Affine une correspondance de clé de partition (**movie01**) en ajoutant une condition de clé de tri pour les valeurs supérieures ou égales à actor2.**

[`QueryConditional`les méthodes](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html) qui commencent par `sort` nécessitent qu'une valeur de clé de partition corresponde et affinent davantage la requête par une comparaison basée sur la valeur de la clé de tri. `Sort`dans le nom de la méthode ne signifie pas que les résultats sont triés, mais qu'une valeur de clé de tri sera utilisée pour la comparaison.

Dans l'extrait suivant, nous modifions la demande de requête affichée précédemment après la ligne de commentaire 3. Cet extrait remplace la requête conditionnelle « KeyEqual » par la requête conditionnelle « sortGreaterThan OrEqualTo » définie après la ligne de commentaire 1a. Le code suivant supprime également l'expression du filtre.

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

**Example — Sortie utilisant la `sortGreaterThanOrEqualTo` requête conditionnelle**  
La sortie suivante affiche les résultats de la requête. **La requête renvoie les éléments dont `movieName` la valeur est égale à **movie01** et uniquement les éléments dont `actorName` la valeur est supérieure ou égale à actor2.** Comme nous supprimons le filtre, la requête renvoie des éléments qui n'ont aucune valeur pour l'`actingSchoolName`attribut.  

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

# Effectuer des opérations par lots
<a name="ddb-en-client-use-multiop-batch"></a>

[L'API DynamoDB Enhanced Client propose deux méthodes par lots [`batchGetItem`, () et (](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))

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

Avec [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))cette méthode, vous pouvez récupérer jusqu'à 100 éléments individuels dans plusieurs tables dans une seule demande globale. L'exemple suivant utilise les classes de [`MovieActor`](ddb-en-client-use-multirecord.md#ddb-en-client-use-movieactor-class)données [`Customer`](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust)et présentées précédemment.

Dans l'exemple qui suit les lignes 1 et 2, vous créez `[ReadBatch](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/ReadBatch.html)` des objets que vous ajoutez ultérieurement en tant que paramètres à la `batchGetItem()` méthode après la ligne de commentaire 3. 

Le code situé après la ligne de commentaire 1 crée le lot à lire dans le `Customer` tableau. Le code situé après la ligne de commentaire 1a montre l'utilisation d'un `[GetItemEnhancedRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/GetItemEnhancedRequest.Builder.html)` générateur qui prend une valeur de clé primaire et une valeur de clé de tri pour spécifier l'élément à lire. Si la classe de données possède une clé composite, vous devez fournir à la fois la valeur de la clé de partition et la valeur de la clé de tri. 

Contrairement à la spécification de valeurs clés pour demander un élément, vous pouvez utiliser une classe de données pour demander un élément, comme indiqué après la ligne de commentaire 1b. Le SDK extrait les valeurs clés en arrière-plan avant de soumettre la demande.

[Lorsque vous spécifiez l'élément à l'aide de l'approche basée sur les clés, comme indiqué dans les deux instructions situées après 2a, vous pouvez également spécifier que DynamoDB doit effectuer une lecture très cohérente.](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html) Lorsque la `consistentRead()` méthode est utilisée, elle doit être utilisée sur tous les éléments demandés pour la même table.

Pour récupérer les éléments trouvés par DynamoDB, utilisez `[resultsForTable() ](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchGetResultPage.html#resultsForTable(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource))` la méthode indiquée après la ligne de commentaire 4. Appelez la méthode pour chaque table lue dans la demande. `resultsForTable()`renvoie une liste des éléments trouvés que vous pouvez traiter à l'aide de n'importe quelle `java.util.List` méthode. Cet exemple enregistre chaque élément.

Pour découvrir les éléments que DynamoDB n'a pas traités, utilisez l'approche indiquée après la ligne de commentaire 5. La `BatchGetResultPage` classe possède la `[unprocessedKeysForTable()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchGetResultPage.html#unprocessedKeysForTable(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource))` méthode qui vous donne accès à chaque clé non traitée. La [référence de l'BatchGetItem API](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchGetItem.html) contient plus d'informations sur les situations qui se traduisent par des éléments non traités.

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

Supposons que les éléments suivants se trouvent dans les deux tables avant d'exécuter l'exemple de code.

### Éléments figurant dans les tableaux
<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'}
```

La sortie suivante montre les éléments renvoyés et enregistrés après la ligne de commentaire 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'}
```

## Exemple de `batchWriteItem()`
<a name="ddb-en-client-use-multiop-batch-write"></a>

Le `batchWriteItem()` procédé place ou supprime plusieurs éléments dans une ou plusieurs tables. Vous pouvez spécifier jusqu'à 25 opérations de mise ou de suppression individuelles dans la demande. L'exemple suivant utilise les classes de [`MovieActor`](ddb-en-client-use-multirecord.md#ddb-en-client-use-movieactor-class)modèles [`ProductCatalog`](ddb-en-client-use.md#ddb-en-client-use-compare-cs3)et présentées précédemment.

`WriteBatch`les objets sont créés après les lignes de commentaires 1 et 2. Pour le `ProductCatalog` tableau, le code place un élément et en supprime un. Pour le `MovieActor` tableau situé après la ligne de commentaire 2, le code place deux éléments et en supprime un.

La `batchWriteItem` méthode est appelée après la ligne de commentaire 3. Le `[builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchWriteItemEnhancedRequest.Builder.html)` paramètre fournit les demandes par lots pour chaque table.

L'`[BatchWriteResult](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchWriteResult.html)`objet renvoyé fournit des méthodes distinctes pour chaque opération afin de visualiser les demandes non traitées. Le code situé après la ligne de commentaire 4a fournit les clés pour les demandes de suppression non traitées et le code situé après la ligne de commentaire 4b fournit les éléments de saisie non traités.

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

Les méthodes d'assistance suivantes fournissent les objets modèles pour les opérations de saisie et de suppression.

### Méthodes auxiliaires
<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.     }
```

Supposons que les tables contiennent les éléments suivants avant d'exécuter l'exemple de code.

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

Une fois l'exemple de code terminé, les tableaux contiennent les éléments suivants.

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

Notez dans le `MovieActor` tableau que l'élément du `Blue Jasmine` film a été remplacé par l'élément utilisé dans la demande de vente acquise via la méthode d'`getMovieActorBlanchettPartial()`assistance. Si aucune valeur d'attribut Data Bean n'a été fournie, la valeur de la base de données est supprimée. C'est pourquoi le résultat `actingSchoolName` est nul pour l'élément `Blue Jasmine` vidéo.

**Note**  
[Bien que la documentation de l'API suggère que des expressions de condition peuvent être utilisées et que les mesures de capacité consommée et de collecte peuvent être renvoyées avec des demandes d'envoi et de [suppression](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/DeleteItemEnhancedRequest.html) individuelles, ce n'est pas le cas dans un scénario d'écriture par lots.](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/PutItemEnhancedRequest.html) Pour améliorer les performances des opérations par lots, ces options individuelles sont ignorées.

# Effectuer des opérations de transaction
<a name="ddb-en-client-use-multiop-trans"></a>

L'API DynamoDB Enhanced Client fournit `transactGetItems()` les méthodes et. `transactWriteItems()` Les méthodes de transaction du SDK for Java assurent l'atomicité, la cohérence, l'isolation et la durabilité (ACID) des tables DynamoDB, vous aidant ainsi à maintenir l'exactitude des données dans vos applications.

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

La `[transactGetItems()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#transactGetItems(java.util.function.Consumer))` méthode accepte jusqu'à 100 demandes individuelles d'articles. Tous les éléments sont lus en une seule transaction atomique. Le guide du *développeur Amazon DynamoDB* contient des informations sur les [conditions à l'origine de l'échec `transactGetItems()` d'une méthode, ainsi que sur le niveau d'isolation utilisé lorsque vous](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-txgetitems) appelez. `[transactGetItem()](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-isolation)`

Après la ligne de commentaire 1 dans l'exemple suivant, le code appelle la `transactGetItems()` méthode avec un `[builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactGetItemsEnhancedRequest.Builder.html)` paramètre. Le générateur `[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))` est invoqué trois fois avec un objet de données contenant les valeurs clés que le SDK utilisera pour générer la demande finale.

La demande renvoie une liste d'`[Document](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Document.html)`objets après la ligne de commentaire 2. La liste des documents renvoyée contient des instances de [document](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Document.html) non nulles contenant des données d'article dans le même ordre que celui demandé. La `[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))` méthode convertit un objet non typé en `Document` objet Java typé si des données d'élément ont été renvoyées, sinon la méthode renvoie 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());
        }
    }
```

Les tables DynamoDB contiennent les éléments suivants avant l'exécution de l'exemple de code.

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

La sortie suivante est enregistrée. Si un article est demandé mais introuvable, il n'est pas retourné comme c'est le cas pour la demande pour le film nommé`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'}
```

## Exemples pour l’`transactWriteItems()`
<a name="ddb-en-client-use-multiop-trans-writeitems"></a>

Il `[transactWriteItems()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#transactWriteItems(java.util.function.Consumer))` accepte jusqu'à 100 actions de mise, de mise à jour ou de suppression dans une seule transaction atomique sur plusieurs tables. Le guide du *développeur Amazon DynamoDB* contient des informations détaillées sur les restrictions et les conditions de défaillance du fonctionnement du service [DynamoDB sous-jacent](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-txwriteitems).

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

Dans l'exemple suivant, quatre opérations sont demandées pour deux tables. Les classes [`ProductCatalog`](ddb-en-client-use.md#ddb-en-client-use-compare-cs3)de modèles correspondantes [`MovieActor`](ddb-en-client-use-multirecord.md#ddb-en-client-use-movieactor-class)ont été présentées précédemment.

Chacune des trois opérations possibles (put, update et delete) utilise un paramètre de demande dédié pour spécifier les détails. 

Le code après la ligne de commentaire 1 montre la variante simple de la `addPutItem()` méthode. La méthode accepte un `[MappedTableResource](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/MappedTableResource.html)` objet et l'instance d'objet de données à placer. L'instruction située après la ligne de commentaire 2 indique la variante qui accepte une `[TransactPutItemEnhancedRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactPutItemEnhancedRequest.html)` instance. Cette variante vous permet d'ajouter d'autres options dans la demande, comme une expression conditionnelle. L'[exemple](#ddb-en-client-use-multiop-trans-writeitems-opcondition) suivant montre une expression de condition pour une opération individuelle.

Une opération de mise à jour est demandée après la ligne de commentaire 3. `[TransactUpdateItemEnhancedRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactUpdateItemEnhancedRequest.Builder.html)`possède une `ignoreNulls()` méthode qui vous permet de configurer ce que le SDK fait avec `null` les valeurs de l'objet modèle. Si la `ignoreNulls()` méthode renvoie true, le SDK ne supprime pas les valeurs d'attribut de la table pour les attributs d'objets de données qui le sont`null`. Si la `ignoreNulls()` méthode renvoie false, le SDK demande au service DynamoDB de supprimer les attributs de l'élément de la table. La valeur par défaut pour `ignoreNulls` est false.

L'instruction située après la ligne de commentaire 4 montre la variante d'une demande de suppression qui prend un objet de données. Le client amélioré extrait les valeurs clés avant d'envoyer la demande 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())
        );
    }
```

Les méthodes d'assistance suivantes fournissent les objets de données pour les `add*Item` paramètres.

#### Méthodes auxiliaires
<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;
    }
```

Les tables DynamoDB contiennent les éléments suivants avant l'exécution de l'exemple de code.

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

Les éléments suivants figurent dans les tableaux une fois l'exécution du code terminée.

```
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'élément de la ligne 2 a été supprimé et les lignes 3 et 5 indiquent les articles qui ont été placés. La ligne 4 montre la mise à jour de la ligne 1. La `price` valeur est la seule valeur qui a changé sur l'élément. Si la valeur renvoyée `ignoreNulls()` était fausse, la ligne 4 ressemblerait à la ligne suivante.

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

### Exemple de vérification de l'état
<a name="ddb-en-client-use-multiop-trans-writeitems-checkcond"></a>

L'exemple suivant montre l'utilisation d'un contrôle de condition. Un contrôle de condition est utilisé pour vérifier l'existence d'un article ou pour vérifier l'état d'attributs spécifiques d'un article dans la base de données. L'article enregistré lors du contrôle de condition ne peut pas être utilisé lors d'une autre opération de la transaction.

**Note**  
Vous ne pouvez pas cibler le même élément avec plusieurs opérations contenues dans la même transaction. Par exemple, vous ne pouvez pas effectuer de vérification de condition et essayer de mettre à jour le même article dans le cadre de la même transaction.

L'exemple montre un type d'opération de chaque type dans une demande d'écriture d'éléments transactionnelle. Après la ligne de commentaire 2, la `addConditionCheck()` méthode fournit la condition qui fait échouer la transaction si le `conditionExpression` paramètre est évalué à`false`. L'expression de condition renvoyée par la méthode indiquée dans le bloc des méthodes d'assistance vérifie si l'année de récompense du film n'`Sophie's Choice`est pas égale à`1982`. Si c'est le cas, l'expression est évaluée à `false` et la transaction échoue.

Ce guide aborde les [expressions](ddb-en-client-expressions.md) de manière approfondie dans une autre rubrique.

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

Les méthodes d'assistance suivantes sont utilisées dans l'exemple de code précédent.

#### Méthodes auxiliaires
<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;
    }
```

Les tables DynamoDB contiennent les éléments suivants avant l'exécution de l'exemple de code.

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

Les éléments suivants figurent dans les tableaux une fois l'exécution du code terminée.

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

Les éléments restent inchangés dans les tables car la transaction a échoué. La `actingYear` valeur du film `Sophie's Choice` est`1982`, comme indiqué à la ligne 2 des éléments du tableau avant l'appel de la `transactWriteItem()` méthode.

Pour saisir les informations d'annulation de la transaction, placez l'appel de `transactWriteItems()` méthode dans un `try` bloc et `catch` le [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). Après la ligne de commentaire 4 de l'exemple, le code enregistre chaque `[CancellationReason](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/CancellationReason.html)` objet. Étant donné que le code suivant la ligne de commentaire 3 de l'exemple indique que des valeurs doivent être renvoyées pour l'élément à l'origine de l'échec de la transaction, le journal affiche les valeurs de base de données brutes pour l'élément `Sophie's Choice` vidéo.

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

### Exemple de condition de fonctionnement unique
<a name="ddb-en-client-use-multiop-trans-writeitems-opcondition"></a>

L'exemple suivant montre l'utilisation d'une condition pour une seule opération dans une demande de transaction. L'opération de suppression après la ligne de commentaire 1 contient une condition qui vérifie la valeur de l'élément cible de l'opération par rapport à la base de données. Dans cet exemple, l'expression de condition créée avec la méthode d'assistance après la ligne de commentaire 2 indique que l'élément doit être supprimé de la base de données si l'année active du film n'est pas égale à 2013.

Les [expressions](ddb-en-client-expressions.md) sont abordées plus loin dans ce guide.

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

Les méthodes d'assistance suivantes sont utilisées dans l'exemple de code précédent.

#### Méthodes auxiliaires
<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;
    }
```

Les tables DynamoDB contiennent les éléments suivants avant l'exécution de l'exemple de code.

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

Les éléments suivants figurent dans les tableaux une fois l'exécution du code terminée.

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

Les éléments restent inchangés dans les tables car la transaction a échoué. La `actingYear` valeur du film `Blue Jasmine` est `2013` celle indiquée à la ligne 2 dans la liste des éléments avant l'exécution de l'exemple de code.

Les lignes suivantes sont enregistrées dans la 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)
```

# Utiliser des indices secondaires
<a name="ddb-en-client-use-secindex"></a>

Les index secondaires améliorent l'accès aux données en définissant des clés alternatives que vous pouvez utiliser dans les opérations de requête et de numérisation. Les indices secondaires globaux (GSI) possèdent une clé de partition et une clé de tri qui peuvent être différentes de celles de la table de base. En revanche, les indices secondaires locaux (LSI) utilisent la clé de partition de l'index principal.

## Annoter une classe de données avec des annotations d'index secondaires
<a name="ddb-en-client-use-secindex-annomodel"></a>

Les attributs qui participent aux index secondaires nécessitent l'`@DynamoDbSecondarySortKey`annotation `@DynamoDbSecondaryPartitionKey` ou.

La classe suivante montre les annotations pour deux indices. Le GSI nommé *SubjectLastPostedDateIndex*utilise l'`Subject`attribut pour la clé de partition et `LastPostedDateTime` pour la clé de tri. Le LSI nommé *ForumLastPostedDateIndex*utilise le `ForumName` comme clé de partition et `LastPostedDateTime` comme clé de tri.

Notez que l'`Subject`attribut joue un double rôle. Il s'agit de la clé de tri de la clé primaire et de la clé de partition du GSI nommé *SubjectLastPostedDateIndex*.

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

La `MessageThread` classe peut être utilisée comme classe de données pour l'[exemple de table de threads](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AppendixSampleTables.html) du manuel *Amazon DynamoDB Developer* Guide.

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

## Création de l'index
<a name="ddb-en-client-use-secindex-confindex"></a>

À partir de la version 2.20.86 du SDK pour Java, la `createTable()` méthode génère automatiquement des index secondaires à partir des annotations de classes de données. Par défaut, tous les attributs de la table de base sont copiés dans un index et les valeurs de débit allouées sont de 20 unités de capacité de lecture et 20 unités de capacité d'écriture.

Toutefois, si vous utilisez une version du SDK antérieure à la version 2.20.86, vous devez créer l'index avec le tableau, comme indiqué dans l'exemple suivant. Cet exemple crée les deux index de la `Thread` table. Le paramètre [builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/CreateTableEnhancedRequest.Builder.html) possède des méthodes pour configurer les deux types d'index, comme indiqué après les lignes de commentaire 1 et 2. Vous utilisez la `indexName()` méthode du générateur d'index pour associer les noms d'index spécifiés dans les annotations de classe de données au type d'index souhaité.

Ce code configure tous les attributs de table pour qu'ils apparaissent dans les deux index après les lignes de commentaires 3 et 4. De plus amples informations sur [les projections d'attributs](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSI.html#LSI.Projections) sont disponibles dans le manuel du *développeur Amazon DynamoDB*.

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

## Requête à l'aide d'un index
<a name="ddb-en-client-use-secindex-query"></a>

L’exemple suivant interroge l’index secondaire global *ForumLastPostedDateIndex*.

Après la ligne de commentaire 2, vous créez un [QueryConditional](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html)objet obligatoire lors de l'appel de la méthode [DynamoDbIndex.query ()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbIndex.html#query(java.util.function.Consumer)). 

Vous obtenez une référence à l'index que vous souhaitez interroger après la ligne de commentaire 3 en transmettant le nom de l'index. Après la ligne de commentaire 4, vous appelez la `query()` méthode sur l'index qui transmet l'`QueryConditional`objet. 

Vous configurez également la requête pour renvoyer trois valeurs d'attribut, comme indiqué après la ligne de commentaire 5. Si elle n'`attributesToProject()`est pas appelée, la requête renvoie toutes les valeurs d'attribut. Notez que les noms d'attributs spécifiés commencent par des lettres minuscules. Ces noms d'attributs correspondent à ceux utilisés dans la table, pas nécessairement aux noms d'attributs de la classe de données.

Après la ligne de commentaire 6, parcourez les résultats, enregistrez chaque élément renvoyé par la requête et stockez-le également dans la liste pour le renvoyer à l'appelant.

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

Les éléments suivants existent dans la base de données avant l'exécution de la requête.

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

Les instructions de journalisation des lignes 1 et 6 génèrent la sortie de console suivante.

```
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 requête a renvoyé des éléments avec une `forumName` valeur de *Forum02* et une `lastPostedDateTime` valeur supérieure ou égale à *2023.03.31*. Les résultats affichent `message` des valeurs avec une chaîne vide, bien que les `message` attributs contiennent des valeurs dans l'index. Cela est dû au fait que l'attribut message n'a pas été projeté par le code après la ligne de commentaire 5. 