

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.

# Utiliser les fonctionnalités de cartographie avancées
<a name="ddb-en-client-adv-features"></a>

Découvrez les fonctionnalités avancées du schéma de table dans l'API DynamoDB Enhanced Client.

## Comprendre les types de schéma de table
<a name="ddb-en-client-adv-features-schm-overview"></a>

`[TableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/TableSchema.html)`est l'interface permettant d'accéder à la fonctionnalité de mappage de l'API DynamoDB Enhanced Client. Il peut mapper un objet de données vers et depuis une carte de [AttributeValues](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/AttributeValue.html). Un `TableSchema` objet doit connaître la structure de la table qu'il mappe. Ces informations de structure sont stockées dans un [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/TableMetadata.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/TableMetadata.html)objet.

L'API client améliorée comporte plusieurs implémentations de`TableSchema`, qui suivent. 

### Schéma de table généré à partir de classes annotées
<a name="ddb-en-client-adv-features-schema-mapped"></a>

La création d'une classe à `TableSchema` partir de classes annotées est une opération modérément coûteuse. Nous vous recommandons donc de ne le faire qu'une seule fois, au démarrage de l'application.

 [ BeanTableSchema ](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/BeanTableSchema.html)   
Cette implémentation est construite sur la base des attributs et des annotations d'une classe de bean. Un exemple de cette approche est présenté dans la [section Commencer](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean).  
Si a ne `BeanTableSchema` se comporte pas comme prévu, activez la journalisation du débogage pour. `software.amazon.awssdk.enhanced.dynamodb.beans`

[ImmutableTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/ImmutableTableSchema.html)  
Cette implémentation est construite à partir d'une classe de données immuable. Cette approche est décrite dans la [Travaillez avec des classes de données immuables](ddb-en-client-use-immut.md) section.

### Schéma de table généré avec un générateur
<a name="ddb-en-client-adv-features-schema-static"></a>

Les éléments suivants `TableSchema` sont créés à partir du code à l'aide d'un générateur. Cette approche est moins coûteuse que celle qui utilise des classes de données annotées. L'approche du générateur évite l'utilisation d'annotations et ne nécessite pas de normes de JavaBean dénomination.

[StaticTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticTableSchema.html)  
Cette implémentation est conçue pour les classes de données mutables. La section de démarrage de ce guide explique comment [générer un à `StaticTableSchema` l'aide d'un générateur](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-builder).

[StaticImmutableTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticImmutableTableSchema.html)  
De la même manière que vous créez un`StaticTableSchema`, vous générez une implémentation de ce type en `TableSchema` utilisant un [générateur](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticImmutableTableSchema.html) à utiliser avec des classes de données immuables.

### Schéma de table pour les données sans schéma fixe
<a name="ddb-en-client-adv-features-schema-document"></a>

[DocumentTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/DocumentTableSchema.html)  
Contrairement aux autres implémentations de`TableSchema`, vous ne définissez pas d'attributs pour une `DocumentTableSchema` instance. En général, vous ne spécifiez que les clés primaires et les fournisseurs de convertisseurs d'attributs. Une `EnhancedDocument` instance fournit les attributs que vous créez à partir d'éléments individuels ou d'une chaîne JSON.

# Inclure ou exclure explicitement des attributs
<a name="ddb-en-client-adv-features-inex-attr"></a>

L'API DynamoDB Enhanced Client propose des annotations pour empêcher les attributs de classe de données de devenir des attributs d'une table. Avec l'API, vous pouvez également utiliser un nom d'attribut différent du nom d'attribut de classe de données.

## Exclure les attributs
<a name="ddb-en-client-adv-features-inex-attr-ex"></a>

Pour ignorer les attributs qui ne doivent pas être mappés à une table DynamoDB, marquez l'attribut avec l'annotation. `@DynamoDbIgnore`

```
private String internalKey;

@DynamoDbIgnore
public String getInternalKey() { return this.internalKey; }
public void setInternalKey(String internalKey) { this.internalKey = internalKey;}
```

## Inclure les attributs
<a name="ddb-en-client-adv-features-inex-attr-in"></a>

Pour modifier le nom d'un attribut utilisé dans la table DynamoDB, marquez-le avec `@DynamoDbAttribute` l'annotation et saisissez un autre nom.

```
private String internalKey;

@DynamoDbAttribute("renamedInternalKey")
public String getInternalKey() { return this.internalKey; }
public void setInternalKey(String internalKey) { this.internalKey = internalKey;}
```

# Conversion des attributs de contrôle
<a name="ddb-en-client-adv-features-conversion"></a>

Par défaut, un schéma de table fournit des convertisseurs pour de nombreux types Java courants via une implémentation par défaut de l'`[AttributeConverterProvider](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverterProvider.html)`interface. Vous pouvez modifier le comportement général par défaut à l’aide d’une implémentation de `AttributeConverterProvider` personnalisée. Vous pouvez également modifier le convertisseur pour un seul attribut.

Pour une liste des convertisseurs disponibles, consultez la documentation Java de [AttributeConverter](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverter.html)l'interface.

## Fournir des fournisseurs de convertisseurs d'attributs personnalisés
<a name="ddb-en-client-adv-features-conversion-prov"></a>

Vous pouvez fournir un seul `AttributeConverterProvider` ou une chaîne de `AttributeConverterProvider` s ordonnés par le biais de l'`@DynamoDbBean``(converterProviders = {…})`annotation. Toute personnalisation `AttributeConverterProvider` doit étendre l'`AttributeConverterProvider`interface.

Notez que si vous fournissez votre propre chaîne de fournisseurs de convertisseurs d'attributs, vous remplacerez le fournisseur de conversion par défaut,`DefaultAttributeConverterProvider`. Si vous souhaitez utiliser les fonctionnalités du`DefaultAttributeConverterProvider`, vous devez l'inclure dans la chaîne. 

Il est également possible d'annoter le bean avec un tableau `{}` vide. Cela désactive l'utilisation de tous les fournisseurs de convertisseurs d'attributs, y compris le fournisseur par défaut. Dans ce cas, tous les attributs à mapper doivent disposer de leur propre convertisseur d'attributs.

L'extrait suivant montre un fournisseur de conversion unique.

```
@DynamoDbBean(converterProviders = ConverterProvider1.class)
public class Customer {

}
```

L'extrait suivant montre l'utilisation d'une chaîne de fournisseurs de convertisseurs. La valeur par défaut du SDK étant fournie en dernier, elle a la priorité la plus basse.

```
@DynamoDbBean(converterProviders = {
   ConverterProvider1.class, 
   ConverterProvider2.class,
   DefaultAttributeConverterProvider.class})
public class Customer {

}
```

Les constructeurs de schémas de tables statiques ont une `attributeConverterProviders()` méthode qui fonctionne de la même manière. Cela est illustré dans l'extrait suivant.

```
private static final StaticTableSchema<Customer> CUSTOMER_TABLE_SCHEMA =
  StaticTableSchema.builder(Customer.class)
    .newItemSupplier(Customer::new)
    .addAttribute(String.class, a -> a.name("name")
                                     a.getter(Customer::getName)
                                     a.setter(Customer::setName))
    .attributeConverterProviders(converterProvider1, converterProvider2)
    .build();
```

## Remplacer le mappage d'un seul attribut
<a name="ddb-en-client-adv-features-conversion-single"></a>

Pour modifier la façon dont un seul attribut est mappé, fournissez un `AttributeConverter` pour l'attribut. Cet ajout remplace tous les convertisseurs fournis `AttributeConverterProviders` dans le schéma de table. Cela ajoute un convertisseur personnalisé pour cet attribut uniquement. Les autres attributs, même ceux du même type, n'utiliseront pas ce convertisseur à moins qu'il ne soit explicitement spécifié pour ces autres attributs.

L'`@DynamoDbConvertedBy`annotation est utilisée pour spécifier la `AttributeConverter` classe personnalisée, comme indiqué dans l'extrait de code suivant.

```
@DynamoDbBean
public class Customer {
    private String name;

    @DynamoDbConvertedBy(CustomAttributeConverter.class)
    public String getName() { return this.name; }
    public void setName(String name) { this.name = name;}
}
```

Les générateurs de schémas statiques utilisent une `attributeConverter()` méthode de génération d'attributs équivalente. Cette méthode prend une instance d'un `AttributeConverter` comme le montre ce qui suit.

```
private static final StaticTableSchema<Customer> CUSTOMER_TABLE_SCHEMA =
  StaticTableSchema.builder(Customer.class)
    .newItemSupplier(Customer::new)
    .addAttribute(String.class, a -> a.name("name")
                                     a.getter(Customer::getName)
                                     a.setter(Customer::setName)
                                     a.attributeConverter(customAttributeConverter))
    .build();
```

## Exemple
<a name="ddb-en-client-adv-features-conversion-example"></a>

Cet exemple montre une `AttributeConverterProvider` implémentation qui fournit un convertisseur d'attributs pour les [https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/HttpCookie.html](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/HttpCookie.html)objets. 

La `SimpleUser` classe suivante contient un attribut nommé `lastUsedCookie` qui est une instance de`HttpCookie`.

Le paramètre des `@DynamoDbBean` annotations répertorie les deux `AttributeConverterProvider` classes qui fournissent des convertisseurs.

------
#### [ Class with annotations ]

```
    @DynamoDbBean(converterProviders = {CookieConverterProvider.class, DefaultAttributeConverterProvider.class})
    public static final class SimpleUser {
        private String name;
        private HttpCookie lastUsedCookie;

        @DynamoDbPartitionKey
        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public HttpCookie getLastUsedCookie() {
            return lastUsedCookie;
        }

        public void setLastUsedCookie(HttpCookie lastUsedCookie) {
            this.lastUsedCookie = lastUsedCookie;
        }
```

------
#### [ Static table schema ]

```
    private static final TableSchema<SimpleUser> SIMPLE_USER_TABLE_SCHEMA =
            TableSchema.builder(SimpleUser.class)
                    .newItemSupplier(SimpleUser::new)
                    .attributeConverterProviders(CookieConverterProvider.create(), AttributeConverterProvider.defaultProvider())
                    .addAttribute(String.class, a -> a.name("name")
                            .setter(SimpleUser::setName)
                            .getter(SimpleUser::getName)
                            .tags(StaticAttributeTags.primaryPartitionKey()))
                    .addAttribute(HttpCookie.class, a -> a.name("lastUsedCookie")
                            .setter(SimpleUser::setLastUsedCookie)
                            .getter(SimpleUser::getLastUsedCookie))
                    .build();
```

------

`CookieConverterProvider`L'exemple suivant fournit une instance de`HttpCookeConverter`.

```
    public static final class CookieConverterProvider implements AttributeConverterProvider {
        private final Map<EnhancedType<?>, AttributeConverter<?>> converterCache = ImmutableMap.of(
                // 1. Add HttpCookieConverter to the internal cache.
                EnhancedType.of(HttpCookie.class), new HttpCookieConverter());

        public static CookieConverterProvider create() {
            return new CookieConverterProvider();
        }

        // The SDK calls this method to find out if the provider contains a AttributeConverter instance
        // for the EnhancedType<T> argument.
        @SuppressWarnings("unchecked")
        @Override
        public <T> AttributeConverter<T> converterFor(EnhancedType<T> enhancedType) {
            return (AttributeConverter<T>) converterCache.get(enhancedType);
        }
    }
```

### Code de conversion
<a name="ddb-en-client-adv-features-conversion-example-code"></a>

Dans la `transformFrom()` méthode de la `HttpCookieConverter` classe suivante, le code reçoit une `HttpCookie` instance et la transforme en une carte DynamoDB stockée sous forme d'attribut.

La `transformTo()` méthode reçoit un paramètre de carte DynamoDB, puis appelle le constructeur qui a besoin d'un `HttpCookie` nom et d'une valeur.

```
    public static final class HttpCookieConverter implements AttributeConverter<HttpCookie> {

        @Override
        public AttributeValue transformFrom(HttpCookie httpCookie) {

            return AttributeValue.fromM(
            Map.of ("cookieName", AttributeValue.fromS(httpCookie.getName()),
                    "cookieValue", AttributeValue.fromS(httpCookie.getValue()))
            );
        }

        @Override
        public HttpCookie transformTo(AttributeValue attributeValue) {
            Map<String, AttributeValue> map = attributeValue.m();
            return new HttpCookie(
                    map.get("cookieName").s(),
                    map.get("cookieValue").s());
        }

        @Override
        public EnhancedType<HttpCookie> type() {
            return EnhancedType.of(HttpCookie.class);
        }

        @Override
        public AttributeValueType attributeValueType() {
            return AttributeValueType.M;
        }
    }
```

# Modifier le comportement de mise à jour des attributs
<a name="ddb-en-client-adv-features-upd-behavior"></a>

Vous pouvez personnaliser le comportement de mise à jour des attributs individuels lorsque vous effectuez une opération de *mise à jour*. [UpdateItem () et () sont des exemples d'opérations de mise à jour](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html#updateItem(T)) [dans l'API DynamoDB Enhanced Client. transactWriteItems](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#transactWriteItems(java.util.function.Consumer))

Imaginons, par exemple, que vous souhaitiez enregistrer un fichier *créé sur* horodatage dans votre dossier. Toutefois, vous souhaitez que sa valeur ne soit écrite que s'il n'existe aucune valeur existante pour l'attribut dans la base de données. Dans ce cas, vous utilisez le comportement de `[WRITE\$1IF\$1NOT\$1EXISTS](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/UpdateBehavior.html#WRITE_IF_NOT_EXISTS)` mise à jour.

L'exemple suivant montre l'annotation qui ajoute le comportement à l'`createdOn`attribut.

```
@DynamoDbBean
public class Customer extends GenericRecord {
    private String id;
    private Instant createdOn;

    @DynamoDbPartitionKey
    public String getId() { return this.id; }
    public void setId(String id) { this.name = id; }

    @DynamoDbUpdateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS)
    public Instant getCreatedOn() { return this.createdOn; }    
    public void setCreatedOn(Instant createdOn) { this.createdOn = createdOn; }
}
```

Vous pouvez déclarer le même comportement de mise à jour lorsque vous créez un schéma de table statique, comme indiqué dans l'exemple suivant après la ligne de commentaire 1.

```
static final TableSchema<Customer> CUSTOMER_TABLE_SCHEMA =
     TableSchema.builder(Customer.class)
       .newItemSupplier(Customer::new)
       .addAttribute(String.class, a -> a.name("id")
                                         .getter(Customer::getId)
                                         .setter(Customer::setId)
                                         .tags(StaticAttributeTags.primaryPartitionKey()))
       .addAttribute(Instant.class, a -> a.name("createdOn")
                                          .getter(Customer::getCreatedOn)
                                          .setter(Customer::setCreatedOn)
                                          // 1. Add an UpdateBehavior.
                                          .tags(StaticAttributeTags.updateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS)))
       .build();
```

# Aplatir les attributs des autres classes
<a name="ddb-en-client-adv-features-flatmap"></a>

Si les attributs de votre table sont répartis dans plusieurs classes Java différentes, que ce soit par héritage ou par composition, l'API DynamoDB Enhanced Client permet de regrouper les attributs en une seule classe.

## Utiliser l'héritage
<a name="ddb-en-client-adv-features-flatmap-inheritance"></a>

Si vos classes utilisent l'héritage, appliquez les approches suivantes pour aplatir la hiérarchie.

### Utiliser des haricots annotés
<a name="ddb-en-client-adv-features-flatmap-inheritance-anno"></a>

Pour l'approche d'annotation, les deux classes doivent porter l'`@DynamoDbBean`annotation et une classe doit porter une ou plusieurs annotations de clé primaire.

Vous trouverez ci-dessous des exemples de classes de données dotées d'une relation d'héritage.

------
#### [ Standard data class ]

```
@DynamoDbBean
public class Customer extends GenericRecord {
    private String name;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}

@DynamoDbBean
public abstract class GenericRecord {
    private String id;
    private String createdDate;

    @DynamoDbPartitionKey
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }

    public String getCreatedDate() { return createdDate; }
    public void setCreatedDate(String createdDate) { this.createdDate = createdDate; }
}
```

------
#### [ Lombok ]

L'[`onMethod`option](https://projectlombok.org/features/experimental/onX) de Lombok copie les annotations DynamoDB basées sur les attributs, telles que, sur le code généré. `@DynamoDbPartitionKey`

```
@DynamoDbBean
@Data
@ToString(callSuper = true)
public class Customer extends GenericRecord {
    private String name;
}

@Data
@DynamoDbBean
public abstract class GenericRecord {
    @Getter(onMethod_=@DynamoDbPartitionKey)
    private String id;
    private String createdDate;
}
```

------

### Utiliser des schémas statiques
<a name="ddb-en-client-adv-features-flatmap-inheritance-static"></a>

Pour l'approche du schéma statique, utilisez la `extend()` méthode du générateur pour réduire les attributs de la classe parent sur ceux de la classe enfant. Ceci est affiché après la ligne de commentaire 1 dans l'exemple suivant.

```
        StaticTableSchema<org.example.tests.model.inheritance.stat.GenericRecord> GENERIC_RECORD_SCHEMA =
                StaticTableSchema.builder(org.example.tests.model.inheritance.stat.GenericRecord.class)
                        // The partition key will be inherited by the top level mapper.
                        .addAttribute(String.class, a -> a.name("id")
                                .getter(org.example.tests.model.inheritance.stat.GenericRecord::getId)
                                .setter(org.example.tests.model.inheritance.stat.GenericRecord::setId)
                                .tags(primaryPartitionKey()))
                        .addAttribute(String.class, a -> a.name("created_date")
                                .getter(org.example.tests.model.inheritance.stat.GenericRecord::getCreatedDate)
                                .setter(org.example.tests.model.inheritance.stat.GenericRecord::setCreatedDate))
                        .build();

        StaticTableSchema<org.example.tests.model.inheritance.stat.Customer> CUSTOMER_SCHEMA =
                StaticTableSchema.builder(org.example.tests.model.inheritance.stat.Customer.class)
                        .newItemSupplier(org.example.tests.model.inheritance.stat.Customer::new)
                        .addAttribute(String.class, a -> a.name("name")
                                .getter(org.example.tests.model.inheritance.stat.Customer::getName)
                                .setter(org.example.tests.model.inheritance.stat.Customer::setName))
                        // 1. Use the extend() method to collapse the parent attributes onto the child class.
                        .extend(GENERIC_RECORD_SCHEMA)     // All the attributes of the GenericRecord schema are added to Customer.
                        .build();
```

L'exemple de schéma statique précédent utilise les classes de données suivantes. Comme le mappage est défini lorsque vous créez le schéma de table statique, les classes de données ne nécessitent pas d'annotations.

#### Classes de données
<a name="gunk"></a>

------
#### [ Standard data class ]

```
public class Customer extends GenericRecord {
    private String name;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
}


public abstract class GenericRecord {
    private String id;
    private String createdDate;

    public String getId() { return id; }
    public void setId(String id) { this.id = id; }

    public String getCreatedDate() { return createdDate; }
    public void setCreatedDate(String createdDate) { this.createdDate = createdDate; }
```

------
#### [ Lombok ]

```
@Data
@ToString(callSuper = true)
public class Customer extends GenericRecord{
    private String name;
}

@Data
public abstract class GenericRecord {
    private String id;
    private String createdDate;
}
```

------

## Utiliser la composition
<a name="ddb-en-client-adv-features-flatmap-comp"></a>

Si vos classes utilisent la composition, appliquez les approches suivantes pour aplatir la hiérarchie.

### Utiliser des haricots annotés
<a name="ddb-en-client-adv-features-flatmap-comp-anno"></a>

L'`@DynamoDbFlatten`annotation aplatit la classe contenue.

Les exemples de classes de données suivants utilisent l'`@DynamoDbFlatten`annotation pour ajouter efficacement tous les attributs de la `GenericRecord` classe contenue à la `Customer` classe.

------
#### [ Standard data class ]

```
@DynamoDbBean
public class Customer {
    private String name;
    private GenericRecord record;

    public String getName() { return this.name; }
    public void setName(String name) { this.name = name; }

    @DynamoDbFlatten
    public GenericRecord getRecord() { return this.record; }
    public void setRecord(GenericRecord record) { this.record = record; }

@DynamoDbBean
public class GenericRecord {
    private String id;
    private String createdDate;

    @DynamoDbPartitionKey
    public String getId() { return this.id; }
    public void setId(String id) { this.id = id; }

    public String getCreatedDate() { return this.createdDate; }
    public void setCreatedDate(String createdDate) { this.createdDate = createdDate; }
}
```

------
#### [ Lombok ]

```
@Data
@DynamoDbBean
public class Customer {
    private String name;
    @Getter(onMethod_=@DynamoDbFlatten)
    private GenericRecord record;
}

@Data
@DynamoDbBean
public class GenericRecord {
    @Getter(onMethod_=@DynamoDbPartitionKey)
    private String id;
    private String createdDate;
}
```

------

Vous pouvez utiliser l'annotation aplatir pour aplatir autant de classes éligibles que nécessaire. Les contraintes suivantes s’appliquent :
+ Tous les noms d'attributs doivent être uniques une fois aplatis.
+ Il ne doit jamais y avoir plus d'une clé de partition, clé de tri ou nom de table.

### Utiliser des schémas statiques
<a name="ddb-en-client-adv-features-flatmap-comp-static"></a>

Lorsque vous créez un schéma de table statique, utilisez la `flatten()` méthode du générateur. Vous fournissez également les méthodes getter et setter qui identifient la classe contenue.

```
        StaticTableSchema<GenericRecord> GENERIC_RECORD_SCHEMA =
                StaticTableSchema.builder(GenericRecord.class)
                        .newItemSupplier(GenericRecord::new)
                        .addAttribute(String.class, a -> a.name("id")
                                .getter(GenericRecord::getId)
                                .setter(GenericRecord::setId)
                                .tags(primaryPartitionKey()))
                        .addAttribute(String.class, a -> a.name("created_date")
                                .getter(GenericRecord::getCreatedDate)
                                .setter(GenericRecord::setCreatedDate))
                        .build();

        StaticTableSchema<Customer> CUSTOMER_SCHEMA =
                StaticTableSchema.builder(Customer.class)
                        .newItemSupplier(Customer::new)
                        .addAttribute(String.class, a -> a.name("name")
                                .getter(Customer::getName)
                                .setter(Customer::setName))
                        // Because we are flattening a component object, we supply a getter and setter so the
                        // mapper knows how to access it.
                        .flatten(GENERIC_RECORD_SCHEMA, Customer::getRecord, Customer::setRecord)
                        .build();
```

L'exemple de schéma statique précédent utilise les classes de données suivantes.

#### Classes de données
<a name="ddb-en-client-adv-features-flatmap-comp-static-supporting"></a>

------
#### [ Standard data class ]

```
public class Customer {
    private String name;
    private GenericRecord record;

    public String getName() { return this.name; }
    public void setName(String name) { this.name = name; }

    public GenericRecord getRecord() { return this.record; }
    public void setRecord(GenericRecord record) { this.record = record; }

public class GenericRecord {
    private String id;
    private String createdDate;

    public String getId() { return this.id; }
    public void setId(String id) { this.id = id; }

    public String getCreatedDate() { return this.createdDate; }
    public void setCreatedDate(String createdDate) { this.createdDate = createdDate; }
}
```

------
#### [ Lombok ]

```
@Data
public class Customer {
    private String name;
    private GenericRecord record;
}

@Data
public class GenericRecord {
    private String id;
    private String createdDate;
}
```

------

Vous pouvez utiliser le modèle Builder pour aplatir autant de classes éligibles que nécessaire.

## Implications pour les autres codes
<a name="ddb-en-client-adv-features-flatmap-compare"></a>

Lorsque vous utilisez l'`@DynamoDbFlatten`attribut (ou la méthode du `flatten()` générateur), l'élément de DynamoDB contient un attribut pour chaque attribut de l'objet composé. Il inclut également les attributs de l'objet composant. 

En revanche, si vous annotez une classe de données avec une classe composée et que vous ne l'utilisez pas`@DynamoDbFlatten`, l'élément est enregistré avec l'objet composé en tant qu'attribut unique.

Par exemple, comparez la `Customer` classe indiquée dans l'[exemple d'aplatissement avec la composition avec](#ddb-en-client-adv-features-flatmap-comp-anno) et sans aplatissement de l'attribut. `record` Vous pouvez visualiser la différence avec JSON, comme indiqué dans le tableau suivant.


****  

| Avec aplatissement | Sans aplatir | 
| --- | --- | 
| 3 attributs | 2 attributs | 
|  <pre>{<br />  "id": "1",<br />  "createdDate": "today",<br />  "name": "my name"<br />}</pre>  |  <pre>{<br />  "id": "1",<br />  "record": {<br />      "createdDate": "today",<br />      "name": "my name"<br />  }<br />}</pre>  | 

La différence devient importante si vous disposez d'un autre code accédant à la table DynamoDB qui devrait trouver certains attributs.

# Travaillez avec des attributs tels que des beans, des cartes, des listes et des ensembles
<a name="ddb-en-client-adv-features-nested"></a>

Une définition de bean, telle que la `Person` classe illustrée ci-dessous, peut définir des propriétés (ou des attributs) qui font référence à des types dotés d'attributs supplémentaires. Par exemple, dans la `Person` classe, se `mainAddress` trouve une propriété qui fait référence à un `Address` bean qui définit des attributs de valeur supplémentaires. `addresses`fait référence à une carte Java, dont les éléments font référence à des `Address` beans. Ces types complexes peuvent être considérés comme des conteneurs d'attributs simples que vous utilisez pour leur valeur de données dans le contexte de DynamoDB. 

*DynamoDB désigne les propriétés de valeur des éléments imbriqués, tels que les cartes, les listes ou les beans, en tant qu'attributs imbriqués.* *Le guide du [développeur Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes) fait référence à la forme enregistrée d'une carte, d'une liste ou d'un bean Java en tant que type de document.* Les attributs simples que vous utilisez pour leur valeur de données en Java sont appelés *types scalaires* dans DynamoDB. Ensembles, qui contiennent plusieurs éléments scalaires du même type, appelés *types d'ensembles*. 

Il est important de savoir que l'API DynamoDB Enhanced Client convertit une propriété qui est un bean en un type de document ArcMap DynamoDB lors de son enregistrement.

## classe `Person`
<a name="ddb-en-client-adv-features-nested-person"></a>

```
@DynamoDbBean
public class Person {
    private Integer id;
    private String firstName;
    private String lastName;
    private Integer age;
    private Address mainAddress;
    private Map<String, Address> addresses;
    private List<PhoneNumber> phoneNumbers;
    private Set<String> hobbies;

    @DynamoDbPartitionKey
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Address getMainAddress() {
        return mainAddress;
    }

    public void setMainAddress(Address mainAddress) {
        this.mainAddress = mainAddress;
    }

    public Map<String, Address> getAddresses() {
        return addresses;
    }

    public void setAddresses(Map<String, Address> addresses) {
        this.addresses = addresses;
    }

    public List<PhoneNumber> getPhoneNumbers() {
        return phoneNumbers;
    }

    public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) {
        this.phoneNumbers = phoneNumbers;
    }

    public Set<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(Set<String> hobbies) {
        this.hobbies = hobbies;
    }

    @Override
    public String toString() {
        return "Person{" +
               "addresses=" + addresses +
               ", id=" + id +
               ", firstName='" + firstName + '\'' +
               ", lastName='" + lastName + '\'' +
               ", age=" + age +
               ", mainAddress=" + mainAddress +
               ", phoneNumbers=" + phoneNumbers +
               ", hobbies=" + hobbies +
               '}';
    }
}
```

## classe `Address`
<a name="ddb-en-client-adv-features-nested-address"></a>

```
@DynamoDbBean
public class Address {
    private String street;
    private String city;
    private String state;
    private String zipCode;

    public Address() {
    }

    public String getStreet() {
        return this.street;
    }

    public String getCity() {
        return this.city;
    }

    public String getState() {
        return this.state;
    }

    public String getZipCode() {
        return this.zipCode;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public void setState(String state) {
        this.state = state;
    }

    public void setZipCode(String zipCode) {
        this.zipCode = zipCode;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Address address = (Address) o;
        return Objects.equals(street, address.street) && Objects.equals(city, address.city) && Objects.equals(state, address.state) && Objects.equals(zipCode, address.zipCode);
    }

    @Override
    public int hashCode() {
        return Objects.hash(street, city, state, zipCode);
    }

    @Override
    public String toString() {
        return "Address{" +
                "street='" + street + '\'' +
                ", city='" + city + '\'' +
                ", state='" + state + '\'' +
                ", zipCode='" + zipCode + '\'' +
                '}';
    }
}
```

## classe `PhoneNumber`
<a name="ddb-en-client-adv-features-nested-phonenumber"></a>

```
@DynamoDbBean
public class PhoneNumber {
    String type;
    String number;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    @Override
    public String toString() {
        return "PhoneNumber{" +
                "type='" + type + '\'' +
                ", number='" + number + '\'' +
                '}';
    }
}
```

## Enregistrer des types complexes
<a name="ddb-en-client-adv-features-nested-mapping"></a>

### Utiliser des classes de données annotées
<a name="ddb-en-client-adv-features-nested-map-anno"></a>

Pour enregistrer les attributs imbriqués des classes personnalisées, il suffit de les annoter. La `Address` classe et la `PhoneNumber` classe affichées précédemment sont annotées uniquement avec l'`@DynamoDbBean`annotation. Lorsque l'API DynamoDB Enhanced Client crée le schéma de table pour la classe à `Person` l'aide de l'extrait suivant, l'API découvre l'utilisation des classes et `PhoneNumber` et crée les mappages `Address` correspondants pour fonctionner avec DynamoDB.

```
TableSchema<Person> personTableSchema = TableSchema.fromBean(Person.class);
```

### Utiliser des schémas abstraits avec des constructeurs
<a name="ddb-en-client-adv-features-nested-map-builder"></a>

L'approche alternative consiste à utiliser des générateurs de schémas de tables statiques pour chaque classe de bean imbriquée, comme indiqué dans le code suivant.

Les schémas de table des `PhoneNumber` classes `Address` et sont abstraits dans le sens où ils ne peuvent pas être utilisés avec une table DynamoDB. Cela est dû au fait qu'ils ne disposent pas de définitions pour la clé primaire. Ils sont toutefois utilisés comme schémas imbriqués dans le schéma de table de la `Person` classe.

Après les lignes de commentaire 1 et 2 de la définition de`PERSON_TABLE_SCHEMA`, vous pouvez voir le code qui utilise les schémas de table abstraits. L'utilisation de `documentOf` dans la `EnhanceType.documentOf(...)` méthode n'indique pas que la méthode renvoie un `EnhancedDocument` type d'API de document améliorée. Dans ce contexte, la `documentOf(...)` méthode renvoie un objet qui sait comment mapper son argument de classe vers et depuis les attributs de table DynamoDB en utilisant l'argument de schéma de table.

#### Code de schéma statique
<a name="ddb-en-client-adv-features-nested-map-builder-code"></a>

```
    // Abstract table schema that cannot be used to work with a DynamoDB table,
    // but can be used as a nested schema.
    public static final TableSchema<Address> TABLE_SCHEMA_ADDRESS = TableSchema.builder(Address.class)
        .newItemSupplier(Address::new)
        .addAttribute(String.class, a -> a.name("street")
            .getter(Address::getStreet)
            .setter(Address::setStreet))
        .addAttribute(String.class, a -> a.name("city")
            .getter(Address::getCity)
            .setter(Address::setCity))
        .addAttribute(String.class, a -> a.name("zipcode")
            .getter(Address::getZipCode)
            .setter(Address::setZipCode))
        .addAttribute(String.class, a -> a.name("state")
            .getter(Address::getState)
            .setter(Address::setState))
        .build();

    // Abstract table schema that cannot be used to work with a DynamoDB table,
    // but can be used as a nested schema.
    public static final TableSchema<PhoneNumber> TABLE_SCHEMA_PHONENUMBER = TableSchema.builder(PhoneNumber.class)
        .newItemSupplier(PhoneNumber::new)
        .addAttribute(String.class, a -> a.name("type")
            .getter(PhoneNumber::getType)
            .setter(PhoneNumber::setType))
        .addAttribute(String.class, a -> a.name("number")
            .getter(PhoneNumber::getNumber)
            .setter(PhoneNumber::setNumber))
        .build();

    // A static table schema that can be used with a DynamoDB table.
    // The table schema contains two nested schemas that are used to perform mapping to/from DynamoDB.
    public static final TableSchema<Person> PERSON_TABLE_SCHEMA =
        TableSchema.builder(Person.class)
            .newItemSupplier(Person::new)
            .addAttribute(Integer.class, a -> a.name("id")
                .getter(Person::getId)
                .setter(Person::setId)
                .addTag(StaticAttributeTags.primaryPartitionKey()))
            .addAttribute(String.class, a -> a.name("firstName")
                .getter(Person::getFirstName)
                .setter(Person::setFirstName))
            .addAttribute(String.class, a -> a.name("lastName")
                .getter(Person::getLastName)
                .setter(Person::setLastName))
            .addAttribute(Integer.class, a -> a.name("age")
                .getter(Person::getAge)
                .setter(Person::setAge))
            .addAttribute(EnhancedType.documentOf(Address.class, TABLE_SCHEMA_ADDRESS), a -> a.name("mainAddress")
                .getter(Person::getMainAddress)
                .setter(Person::setMainAddress))
            .addAttribute(EnhancedType.listOf(String.class), a -> a.name("hobbies")
                .getter(Person::getHobbies)
                .setter(Person::setHobbies))
            .addAttribute(EnhancedType.mapOf(
                EnhancedType.of(String.class),
                // 1. Use mapping functionality of the Address table schema.
                EnhancedType.documentOf(Address.class, TABLE_SCHEMA_ADDRESS)), a -> a.name("addresses")
                .getter(Person::getAddresses)
                .setter(Person::setAddresses))
            .addAttribute(EnhancedType.listOf(
                // 2. Use mapping functionality of the PhoneNumber table schema.
                EnhancedType.documentOf(PhoneNumber.class, TABLE_SCHEMA_PHONENUMBER)), a -> a.name("phoneNumbers")
                .getter(Person::getPhoneNumbers)
                .setter(Person::setPhoneNumbers))
            .build();
```

## Attributs de projet de types complexes
<a name="ddb-en-client-adv-features-nested-projection"></a>

Pour les `scan()` méthodes `query()` et, vous pouvez spécifier les attributs que vous souhaitez voir renvoyés dans les résultats en utilisant des appels de méthode tels que `addNestedAttributeToProject()` et`attributesToProject()`. L'API DynamoDB Enhanced Client convertit les paramètres d'appel de méthode Java [en expressions de projection](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ProjectionExpressions.html) avant l'envoi de la demande.

L'exemple suivant remplit le `Person` tableau avec deux éléments, puis exécute trois opérations de numérisation. 

Le premier scan accède à tous les éléments du tableau afin de comparer les résultats aux autres opérations d'analyse. 

Le second scan utilise la méthode du [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/ScanEnhancedRequest.Builder.html#addNestedAttributeToProject(software.amazon.awssdk.enhanced.dynamodb.NestedAttributeName)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/ScanEnhancedRequest.Builder.html#addNestedAttributeToProject(software.amazon.awssdk.enhanced.dynamodb.NestedAttributeName))générateur pour renvoyer uniquement la valeur de `street` l'attribut.

La troisième opération d'analyse utilise la méthode du [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/ScanEnhancedRequest.Builder.html#attributesToProject(java.lang.String...)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/ScanEnhancedRequest.Builder.html#attributesToProject(java.lang.String...))générateur pour renvoyer les données de l'attribut de premier niveau,`hobbies`. Le type d'attribut de `hobbies` est une liste. Pour accéder à des éléments de liste individuels, effectuez une `get()` opération sur la liste.

```
        personDynamoDbTable = getDynamoDbEnhancedClient().table("Person", PERSON_TABLE_SCHEMA);
        PersonUtils.createPersonTable(personDynamoDbTable, getDynamoDbClient());
        // Use a utility class to add items to the Person table.
        List<Person> personList = PersonUtils.getItemsForCount(2);
        // This utility method performs a put against DynamoDB to save the instances in the list argument.
        PersonUtils.putCollection(getDynamoDbEnhancedClient(), personList, personDynamoDbTable);

        // The first scan logs all items in the table to compare to the results of the subsequent scans.
        final PageIterable<Person> allItems = personDynamoDbTable.scan();
        allItems.items().forEach(p ->
                // 1. Log what is in the table.
                logger.info(p.toString()));

        // Scan for nested attributes.
        PageIterable<Person> streetScanResult = personDynamoDbTable.scan(b -> b
                // Use the 'addNestedAttributeToProject()' or 'addNestedAttributesToProject()' to access data nested in maps in DynamoDB.
                .addNestedAttributeToProject(
                        NestedAttributeName.create("addresses", "work", "street")
                ));

        streetScanResult.items().forEach(p ->
                //2. Log the results of requesting nested attributes.
                logger.info(p.toString()));

        // Scan for a top-level list attribute.
        PageIterable<Person> hobbiesScanResult = personDynamoDbTable.scan(b -> b
                // Use the 'attributesToProject()' method to access first-level attributes.
                .attributesToProject("hobbies"));

        hobbiesScanResult.items().forEach((p) -> {
            // 3. Log the results of the request for the 'hobbies' attribute.
            logger.info(p.toString());
            // To access an item in a list, first get the parent attribute, 'hobbies', then access items in the list.
            String hobby = p.getHobbies().get(1);
            // 4. Log an item in the list.
            logger.info(hobby);
        });
```

```
// Logged results from comment line 1.
Person{id=2, firstName='first name 2', lastName='last name 2', age=11, addresses={work=Address{street='street 21', city='city 21', state='state 21', zipCode='33333'}, home=Address{street='street 2', city='city 2', state='state 2', zipCode='22222'}}, phoneNumbers=[PhoneNumber{type='home', number='222-222-2222'}, PhoneNumber{type='work', number='333-333-3333'}], hobbies=[hobby 2, hobby 21]}
Person{id=1, firstName='first name 1', lastName='last name 1', age=11, addresses={work=Address{street='street 11', city='city 11', state='state 11', zipCode='22222'}, home=Address{street='street 1', city='city 1', state='state 1', zipCode='11111'}}, phoneNumbers=[PhoneNumber{type='home', number='111-111-1111'}, PhoneNumber{type='work', number='222-222-2222'}], hobbies=[hobby 1, hobby 11]}

// Logged results from comment line 2.
Person{id=null, firstName='null', lastName='null', age=null, addresses={work=Address{street='street 21', city='null', state='null', zipCode='null'}}, phoneNumbers=null, hobbies=null}
Person{id=null, firstName='null', lastName='null', age=null, addresses={work=Address{street='street 11', city='null', state='null', zipCode='null'}}, phoneNumbers=null, hobbies=null}

// Logged results from comment lines 3 and 4.
Person{id=null, firstName='null', lastName='null', age=null, addresses=null, phoneNumbers=null, hobbies=[hobby 2, hobby 21]}
hobby 21
Person{id=null, firstName='null', lastName='null', age=null, addresses=null, phoneNumbers=null, hobbies=[hobby 1, hobby 11]}
hobby 11
```

**Note**  
Si la `attributesToProject()` méthode suit une autre méthode de création qui ajoute les attributs que vous souhaitez projeter, la liste des noms d'attributs fournie au `attributesToProject()` remplace tous les autres noms d'attributs.  
Une analyse effectuée avec l'`ScanEnhancedRequest`instance figurant dans l'extrait suivant renvoie uniquement les données relatives aux loisirs.  

```
ScanEnhancedRequest lastOverwrites = ScanEnhancedRequest.builder()
        .addNestedAttributeToProject(
                NestedAttributeName.create("addresses", "work", "street"))
        .addAttributeToProject("firstName")
        // If the 'attributesToProject()' method follows other builder methods that add attributes for projection,
        // its list of attributes replace all previous attributes.
        .attributesToProject("hobbies")
        .build();
PageIterable<Person> hobbiesOnlyResult = personDynamoDbTable.scan(lastOverwrites);
hobbiesOnlyResult.items().forEach(p ->
        logger.info(p.toString()));

// Logged results.
Person{id=null, firstName='null', lastName='null', age=null, addresses=null, phoneNumbers=null, hobbies=[hobby 2, hobby 21]}
Person{id=null, firstName='null', lastName='null', age=null, addresses=null, phoneNumbers=null, hobbies=[hobby 1, hobby 11]}
```
L'extrait de code suivant utilise d'abord la `attributesToProject()` méthode. Cet ordre préserve tous les autres attributs demandés.  

```
ScanEnhancedRequest attributesPreserved = ScanEnhancedRequest.builder()
        // Use 'attributesToProject()' first so that the method call does not replace all other attributes
        // that you want to project.
        .attributesToProject("firstName")
        .addNestedAttributeToProject(
                NestedAttributeName.create("addresses", "work", "street"))
        .addAttributeToProject("hobbies")
        .build();
PageIterable<Person> allAttributesResult = personDynamoDbTable.scan(attributesPreserved);
allAttributesResult.items().forEach(p ->
        logger.info(p.toString()));

// Logged results.
Person{id=null, firstName='first name 2', lastName='null', age=null, addresses={work=Address{street='street 21', city='null', state='null', zipCode='null'}}, phoneNumbers=null, hobbies=[hobby 2, hobby 21]}
Person{id=null, firstName='first name 1', lastName='null', age=null, addresses={work=Address{street='street 11', city='null', state='null', zipCode='null'}}, phoneNumbers=null, hobbies=[hobby 1, hobby 11]}
```

## Utiliser des types complexes dans les expressions
<a name="ddb-en-client-adv-features-nested-expressions"></a>

Vous pouvez utiliser des types complexes dans les expressions, telles que les expressions de filtre et les expressions de condition, en utilisant des opérateurs de déréférencement pour parcourir la structure du type complexe. Pour les objets et les cartes, utilisez le `. (dot)` et pour les éléments de liste `[n]` (crochets autour du numéro de séquence de l'élément). Vous ne pouvez pas faire référence à des éléments individuels d'un ensemble, mais vous pouvez utiliser la [`contains`fonction](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions).

L'exemple suivant montre deux expressions de filtre utilisées dans les opérations de numérisation. Les expressions de filtre spécifient les conditions de correspondance pour les éléments que vous souhaitez voir apparaître dans les résultats. L'exemple utilise`Person`,`Address`, et `PhoneNumber` les classes présentés précédemment.

```
    public void scanUsingFilterOfNestedAttr() {
        // The following is a filter expression for an attribute that is a map of Address objects.
        // By using this filter expression, the SDK returns Person objects that have an address
        // with 'mailing' as a key and 'MS2' for a state value.
        Expression addressFilter = Expression.builder()
                .expression("addresses.#type.#field = :value")
                .putExpressionName("#type", "mailing")
                .putExpressionName("#field", "state")
                .putExpressionValue(":value", AttributeValue.builder().s("MS2").build())
                .build();

        PageIterable<Person> addressFilterResults = personDynamoDbTable.scan(rb -> rb.
                filterExpression(addressFilter));
        addressFilterResults.items().stream().forEach(p -> logger.info("Person: {}", p));

        assert addressFilterResults.items().stream().count() == 1;


        // The following is a filter expression for an attribute that is a list of phone numbers.
        // By using this filter expression, the SDK returns Person objects whose second phone number
        // in the list has a type equal to 'cell'.
        Expression phoneFilter = Expression.builder()
                .expression("phoneNumbers[1].#type = :type")
                .putExpressionName("#type", "type")
                .putExpressionValue(":type", AttributeValue.builder().s("cell").build())
                .build();

        PageIterable<Person> phoneFilterResults = personDynamoDbTable.scan(rb -> rb
                .filterExpression(phoneFilter)
                .attributesToProject("id", "firstName", "lastName", "phoneNumbers")
        );

        phoneFilterResults.items().stream().forEach(p -> logger.info("Person: {}", p));

        assert phoneFilterResults.items().stream().count() == 1;
        assert phoneFilterResults.items().stream().findFirst().get().getPhoneNumbers().get(1).getType().equals("cell");
    }
```

### Méthode d'assistance qui remplit le tableau
<a name="nested-expressions-helper-method"></a>

```
    public static void populateDatabase() {
        Person person1 = new Person();
        person1.setId(1);
        person1.setFirstName("FirstName1");
        person1.setLastName("LastName1");

        Address billingAddr1 = new Address();
        billingAddr1.setState("BS1");
        billingAddr1.setCity("BillingTown1");

        Address mailing1 = new Address();
        mailing1.setState("MS1");
        mailing1.setCity("MailingTown1");

        person1.setAddresses(Map.of("billing", billingAddr1, "mailing", mailing1));

        PhoneNumber pn1_1 = new PhoneNumber();
        pn1_1.setType("work");
        pn1_1.setNumber("111-111-1111");

        PhoneNumber pn1_2 = new PhoneNumber();
        pn1_2.setType("home");
        pn1_2.setNumber("222-222-2222");

        List<PhoneNumber> phoneNumbers1 = List.of(pn1_1, pn1_2);
        person1.setPhoneNumbers(phoneNumbers1);

        personDynamoDbTable.putItem(person1);

        Person person2 = person1;
        person2.setId(2);
        person2.setFirstName("FirstName2");
        person2.setLastName("LastName2");

        Address billingAddress2 = billingAddr1;
        billingAddress2.setCity("BillingTown2");
        billingAddress2.setState("BS2");

        Address mailing2 = mailing1;
        mailing2.setCity("MailingTown2");
        mailing2.setState("MS2");

        person2.setAddresses(Map.of("billing", billingAddress2, "mailing", mailing2));

        PhoneNumber pn2_1 = new PhoneNumber();
        pn2_1.setType("work");
        pn2_1.setNumber("333-333-3333");

        PhoneNumber pn2_2 = new PhoneNumber();
        pn2_2.setType("cell");
        pn2_2.setNumber("444-444-4444");

        List<PhoneNumber> phoneNumbers2 = List.of(pn2_1, pn2_2);
        person2.setPhoneNumbers(phoneNumbers2);

        personDynamoDbTable.putItem(person2);
    }
```

### Représentation JSON des éléments de la base de données
<a name="nested-attributes-expression-json-items"></a>

```
{
 "id": 1,
 "addresses": {
  "billing": {
   "city": "BillingTown1",
   "state": "BS1",
   "street": null,
   "zipCode": null
  },
  "mailing": {
   "city": "MailingTown1",
   "state": "MS1",
   "street": null,
   "zipCode": null
  }
 },
 "firstName": "FirstName1",
 "lastName": "LastName1",
 "phoneNumbers": [
  {
   "number": "111-111-1111",
   "type": "work"
  },
  {
   "number": "222-222-2222",
   "type": "home"
  }
 ]
}

{
 "id": 2,
 "addresses": {
  "billing": {
   "city": "BillingTown2",
   "state": "BS2",
   "street": null,
   "zipCode": null
  },
  "mailing": {
   "city": "MailingTown2",
   "state": "MS2",
   "street": null,
   "zipCode": null
  }
 },
 "firstName": "FirstName2",
 "lastName": "LastName2",
 "phoneNumbers": [
  {
   "number": "333-333-3333",
   "type": "work"
  },
  {
   "number": "444-444-4444",
   "type": "cell"
  }
 ]
}
```

## Mettre à jour les éléments contenant des types complexes
<a name="ddb-en-client-adv-features-nested-updates"></a>

Pour mettre à jour un élément contenant des types complexes, vous avez deux approches de base :
+ Approche 1 : récupérez d'abord l'élément (en utilisant`getItem`), mettez à jour l'objet, puis appelez`DynamoDbTable#updateItem`.
+ Approche 2 : Ne récupérez pas l'élément, mais créez une nouvelle instance, définissez les propriétés que vous souhaitez mettre à jour et soumettez l'instance `DynamoDbTable#updateItem` en définissant la valeur appropriée de [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/IgnoreNullsMode.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/IgnoreNullsMode.html). Cette approche ne nécessite pas que vous récupériez l'élément avant de le mettre à jour.

Les exemples présentés dans cette section utilisent les `PhoneNumber` classes `Person``Address`,, et présentées précédemment.

### Approche de mise à jour 1 : récupération, puis mise à jour
<a name="ddb-en-client-adv-features-nested-updates-retreive"></a>

En utilisant cette approche, vous vous assurez qu'aucune donnée n'est perdue lors de la mise à jour. L'API DynamoDB Enhanced Client recrée le bean avec les attributs de l'élément enregistré dans DynamoDB, y compris les valeurs de types complexes. Vous devez ensuite utiliser les getters et setters pour mettre à jour le bean. L'inconvénient de cette approche est le coût que vous devez engager pour récupérer l'article en premier.

L'exemple suivant montre qu'aucune donnée n'est perdue si vous récupérez l'élément avant de le mettre à jour.

```
    public void retrieveThenUpdateExample()  {
        // Assume that we ran this code yesterday.
        Person person = new Person();
        person.setId(1);
        person.setFirstName("FirstName");
        person.setLastName("LastName");

        Address mainAddress = new Address();
        mainAddress.setStreet("123 MyStreet");
        mainAddress.setCity("MyCity");
        mainAddress.setState("MyState");
        mainAddress.setZipCode("MyZipCode");
        person.setMainAddress(mainAddress);

        PhoneNumber homePhone = new PhoneNumber();
        homePhone.setNumber("1111111");
        homePhone.setType("HOME");
        person.setPhoneNumbers(List.of(homePhone));

        personDynamoDbTable.putItem(person);

        // Assume that we are running this code now.
        // First, retrieve the item
        Person retrievedPerson = personDynamoDbTable.getItem(Key.builder().partitionValue(1).build());

        // Make any updates.
        retrievedPerson.getMainAddress().setCity("YourCity");

        // Save the updated bean. 'updateItem' returns the bean as it appears after the update.
        Person updatedPerson = personDynamoDbTable.updateItem(retrievedPerson);

        // Verify for this example.
        Address updatedMainAddress = updatedPerson.getMainAddress();
        assert updatedMainAddress.getCity().equals("YourCity");
        assert updatedMainAddress.getState().equals("MyState"); // Unchanged.
        // The list of phone numbers remains; it was not set to null;
        assert updatedPerson.getPhoneNumbers().size() == 1;
    }
```

### Approche de mise à jour 2 : utilisez une `IgnoreNullsMode` énumération sans récupérer l'élément au préalable
<a name="ddb-en-client-adv-features-nested-updates-nullmode"></a>

Pour mettre à jour un élément dans DynamoDB, vous pouvez fournir un nouvel objet contenant uniquement les propriétés que vous souhaitez mettre à jour et laisser les autres valeurs nulles. Avec cette approche, vous devez être conscient de la manière dont les valeurs nulles de l'objet sont traitées par le SDK et de la manière dont vous pouvez contrôler le comportement.

Pour spécifier les propriétés à valeur nulle que vous souhaitez que le SDK ignore, fournissez une `IgnoreNullsMode` énumération lorsque vous créez le. [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/UpdateItemEnhancedRequest.Builder.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/UpdateItemEnhancedRequest.Builder.html) À titre d'exemple d'utilisation de l'une des valeurs énumérées, l'extrait de code suivant utilise le mode. `IgnoreNullsMode.SCALAR_ONLY`

```
// Create a new Person object to update the existing item in DynamoDB.
Person personForUpdate = new Person();
personForUpdate.setId(1);
personForUpdate.setFirstName("updatedFirstName");  // 'firstName' is a top scalar property.

Address addressForUpdate = new Address();
addressForUpdate.setCity("updatedCity");
personForUpdate.setMainAddress(addressForUpdate);

personDynamoDbTable.updateItem(r -> r
                .item(personForUpdate)
                .ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY));

/* With IgnoreNullsMode.SCALAR_ONLY provided, The SDK ignores all null properties. The SDK adds or replaces
the 'firstName' property with the provided value, "updatedFirstName". The SDK updates the 'city' value of
'mainAddress', as long as the 'mainAddress' attribute already exists in DynamoDB.

In the background, the SDK generates an update expression that it sends in the request to DynamoDB.
The following JSON object is a simplified version of what it sends. Notice that the SDK includes the paths
to 'mainAddress.city' and 'firstName' in the SET clause of the update expression. No null values in
'personForUpdate' are included.

{
  "TableName": "PersonTable",
  "Key": {
    "id": {
      "N": "1"
    }
  },
  "ReturnValues": "ALL_NEW",
  "UpdateExpression": "SET #mainAddress.#city = :mainAddress_city, #firstName = :firstName",
  "ExpressionAttributeNames": {
    "#city": "city",
    "#firstName": "firstName",
    "#mainAddress": "mainAddress"
  },
  "ExpressionAttributeValues": {
    ":firstName": {
      "S": "updatedFirstName"
    },
    ":mainAddress_city": {
      "S": "updatedCity"
    }
  }
}

Had we chosen 'IgnoreNullsMode.DEFAULT' instead of 'IgnoreNullsMode.SCALAR_ONLY', the SDK would have included
null values in the "ExpressionAttributeValues" section of the request as shown in the following snippet.

  "ExpressionAttributeValues": {
    ":mainAddress": {
      "M": {
        "zipCode": {
          "NULL": true
        },
        "city": {
          "S": "updatedCity"
        },
        "street": {
          "NULL": true
        },
        "state": {
          "NULL": true
        }
      }
    },
    ":firstName": {
      "S": "updatedFirstName"
    }
  }
*/
```

[Le guide du développeur Amazon DynamoDB contient plus d'informations sur les expressions de mise à jour.](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html)

#### Descriptions des `IgnoreNullsMode` options
<a name="ignore-nulls-mode-descriptions"></a>
+ `IgnoreNullsMode.SCALAR_ONLY`- Utilisez ce paramètre pour mettre à jour les attributs scalaires à tous les niveaux. Le SDK crée une instruction de mise à jour qui envoie uniquement des attributs scalaires non nuls à DynamoDB. Le SDK ignore les attributs scalaires à valeur nulle d'un bean ou d'une carte, en conservant la valeur enregistrée dans DynamoDB.

  Lorsque vous mettez à jour un attribut scalaire de map ou bean, la carte doit déjà exister dans DynamoDB. Si vous ajoutez une carte ou un bean à l'objet qui n'existe pas déjà pour cet objet dans DynamoDB, le *message « Le chemin du document indiqué dans l'expression de mise à jour n'est pas `DynamoDbException` valide pour la mise à jour* » s'affiche. Vous devez utiliser `MAPS_ONLY` le mode pour ajouter un bean ou une carte à DynamoDB avant de mettre à jour l'un de ses attributs.
+ `IgnoreNullsMode.MAPS_ONLY`- Utilisez ce paramètre pour ajouter ou remplacer des propriétés qui sont un bean ou une carte. Le SDK remplace ou ajoute toute carte ou bean fourni dans l'objet. Tous les beans ou cartes dont la valeur est nulle dans l'objet sont ignorés, ce qui permet de conserver la carte qui existe dans DynamoDB.
+ `IgnoreNullsMode.DEFAULT`- Avec ce paramètre, le SDK n'ignore jamais les valeurs nulles. Les attributs scalaires de tous les niveaux qui sont nuls sont mis à jour à zéro. Le SDK met à jour toute propriété de type bean, map, list ou set dont la valeur est nulle dans l'objet en lui attribuant la valeur null dans DynamoDB. Lorsque vous utilisez ce mode (ou que vous n'en fournissez aucun puisqu'il s'agit du mode par défaut), vous devez d'abord récupérer l'élément afin que les valeurs fournies dans l'objet pour la mise à jour dans DynamoDB ne soient pas définies sur null, sauf si vous avez l'intention de définir les valeurs sur null.

Dans tous les modes, si vous fournissez un objet `updateItem` dont la liste ou l'ensemble n'est pas nul, la liste ou l'ensemble est enregistré dans DynamoDB. 

#### Pourquoi les modes ?
<a name="ddb-en-client-adv-features-nested-updates-nullmodes-why"></a>

Lorsque vous fournissez à un objet un bean ou un mappage vers la `updateItem` méthode, le SDK ne peut pas dire s'il doit utiliser les valeurs des propriétés du bean (ou les valeurs d'entrée dans la carte) pour mettre à jour l'élément, ou si l'intégralité bean/map doit remplacer ce qui a été enregistré dans DynamoDB.

À partir de notre exemple précédent qui montre d'abord la récupération de l'élément, essayons de mettre à jour l'`city`attribut de `mainAddress` sans la récupération.

```
/* The retrieval example saved the Person object with a 'mainAddress' property whose 'city' property value is "MyCity".
/* Note that we create a new Person with only the necessary information to update the city value
of the mainAddress. */
Person personForUpdate = new Person();
personForUpdate.setId(1);
// The update we want to make changes the city.
Address mainAddressForUpdate = new Address();
mainAddressForUpdate.setCity("YourCity");
personForUpdate.setMainAddress(mainAddressForUpdate);

// Lets' try the following:
Person updatedPerson = personDynamoDbTable.updateItem(personForUpdate);
/*
 Since we haven't retrieved the item, we don't know if the 'mainAddress' property
 already exists, so what update expression should the SDK generate?

A) Should it replace or add the 'mainAddress' with the provided object (setting all attributes to null other than city)
   as shown in the following simplified JSON?

      {
        "TableName": "PersonTable",
        "Key": {
          "id": {
            "N": "1"
          }
        },
        "ReturnValues": "ALL_NEW",
        "UpdateExpression": "SET #mainAddress = :mainAddress",
        "ExpressionAttributeNames": {
          "#mainAddress": "mainAddress"
        },
        "ExpressionAttributeValues": {
          ":mainAddress": {
            "M": {
              "zipCode": {
                "NULL": true
              },
              "city": {
                "S": "YourCity"
              },
              "street": {
                "NULL": true
              },
              "state": {
                "NULL": true
              }
            }
          }
        }
      }
 
B) Or should it update only the 'city' attribute of an existing 'mainAddress' as shown in the following simplified JSON?

      {
        "TableName": "PersonTable",
        "Key": {
          "id": {
            "N": "1"
          }
        },
        "ReturnValues": "ALL_NEW",
        "UpdateExpression": "SET #mainAddress.#city = :mainAddress_city",
        "ExpressionAttributeNames": {
          "#city": "city",
          "#mainAddress": "mainAddress"
        },
        "ExpressionAttributeValues": {
          ":mainAddress_city": {
            "S": "YourCity"
          }
        }
      }

However, assume that we don't know if the 'mainAddress' already exists. If it doesn't exist, the SDK would try to update 
an attribute of a non-existent map, which results in an exception.

In this particular case, we would likely select option B (SCALAR_ONLY) to retain the other values of the 'mainAddress'.
*/
```

Les deux exemples suivants montrent les utilisations des valeurs `SCALAR_ONLY` énumérées `MAPS_ONLY` et. `MAPS_ONLY`ajoute une carte et `SCALAR_ONLY` met à jour une carte.

##### Exemple de `IgnoreNullsMode.MAPS_ONLY`
<a name="scalar-only-example"></a>

```
    public void mapsOnlyModeExample() {
        // Assume that we ran this code yesterday.
        Person person = new Person();
        person.setId(1);
        person.setFirstName("FirstName");

        personDynamoDbTable.putItem(person);

        // Assume that we are running this code now.

        /* Note that we create a new Person with only the necessary information to update the city value
        of the mainAddress. */
        Person personForUpdate = new Person();
        personForUpdate.setId(1);
        // The update we want to make changes the city.
        Address mainAddressForUpdate = new Address();
        mainAddressForUpdate.setCity("YourCity");
        personForUpdate.setMainAddress(mainAddressForUpdate);


        Person updatedPerson = personDynamoDbTable.updateItem(r -> r
                .item(personForUpdate)
                .ignoreNullsMode(IgnoreNullsMode.MAPS_ONLY)); // Since the mainAddress property does not exist, use MAPS_ONLY mode.
        assert updatedPerson.getMainAddress().getCity().equals("YourCity");
        assert updatedPerson.getMainAddress().getState() == null;
    }
```

##### `IgnoreNullsMode.SCALAR_ONLY example`
<a name="maps-only-example"></a>

```
    public void scalarOnlyExample() {
        // Assume that we ran this code yesterday.
        Person person = new Person();
        person.setId(1);
        Address mainAddress = new Address();
        mainAddress.setCity("MyCity");
        mainAddress.setState("MyState");
        person.setMainAddress(mainAddress);

        personDynamoDbTable.putItem(person);

        // Assume that we are running this code now.

        /* Note that we create a new Person with only the necessary information to update the city value
        of the mainAddress. */
        Person personForUpdate = new Person();
        personForUpdate.setId(1);
        // The update we want to make changes the city.
        Address mainAddressForUpdate = new Address();
        mainAddressForUpdate.setCity("YourCity");
        personForUpdate.setMainAddress(mainAddressForUpdate);

        Person updatedPerson = personDynamoDbTable.updateItem(r -> r
                .item(personForUpdate)
                .ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY)); // SCALAR_ONLY mode ignores null properties in the in mainAddress.
        assert updatedPerson.getMainAddress().getCity().equals("YourCity");
        assert updatedPerson.getMainAddress().getState().equals("MyState"); // The state property remains the same.
    }
```

Reportez-vous au tableau suivant pour savoir quelles valeurs nulles sont ignorées pour chaque mode. Vous pouvez souvent travailler avec l'un ou `SCALAR_ONLY` l'autre`MAPS_ONLY`, sauf lorsque vous travaillez avec des beans ou des cartes.


**Quelles propriétés à valeur nulle de l'objet soumis le SDK `updateItem` ignore-t-il pour chaque mode ?**  

| Type de propriété | en mode SCALAR\$1ONLY | en mode MAPS\$1ONLY | en mode DEFAULT | 
| --- | --- | --- | --- | 
| Meilleur scalaire | Oui | Oui | Non | 
| Bean ou carte | Oui | Oui | Non | 
| Valeur scalaire d'un bean ou d'une entrée de carte | Oui1 | No2 | Non | 
| Liste ou ensemble | Oui | Oui | Non | 

1 Cela suppose que la carte existe déjà dans DynamoDB. Toute valeur scalaire (nulle ou non nulle) du bean ou de la carte que vous fournissez dans l'objet à mettre à jour nécessite qu'un chemin d'accès à la valeur existe dans DynamoDB. Le SDK construit un chemin d'accès à l'attribut en utilisant l'opérateur de `. (dot)` déréférencement avant de soumettre la demande.

2 Puisque vous utilisez le `MAPS_ONLY` mode pour remplacer complètement ou ajouter un bean ou une map, toutes les valeurs nulles du bean ou de la map sont conservées dans la map enregistrée dans DynamoDB.

# Préservez les objets vides avec `@DynamoDbPreserveEmptyObject`
<a name="ddb-en-client-adv-features-empty"></a>

Si vous enregistrez un bean dans Amazon DynamoDB avec des objets vides et que vous souhaitez que le SDK recrée les objets vides lors de leur extraction, annotez le getter du bean interne avec. `@DynamoDbPreserveEmptyObject`

Pour illustrer le fonctionnement de l'annotation, l'exemple de code utilise les deux beans suivants.

## Exemple de haricots
<a name="ddb-en-client-adv-features-empty-ex1"></a>

La classe de données suivante contient deux `InnerBean` champs. La méthode getter`getInnerBeanWithoutAnno()`, n'est pas annotée avec. `@DynamoDbPreserveEmptyObject` La `getInnerBeanWithAnno()` méthode est annotée.

```
@DynamoDbBean
public class MyBean {

    private String id;
    private String name;
    private InnerBean innerBeanWithoutAnno;
    private InnerBean innerBeanWithAnno;

    @DynamoDbPartitionKey
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public InnerBean getInnerBeanWithoutAnno() { return innerBeanWithoutAnno; }
    public void setInnerBeanWithoutAnno(InnerBean innerBeanWithoutAnno) { this.innerBeanWithoutAnno = innerBeanWithoutAnno; }

    @DynamoDbPreserveEmptyObject
    public InnerBean getInnerBeanWithAnno() { return innerBeanWithAnno; }
    public void setInnerBeanWithAnno(InnerBean innerBeanWithAnno) { this.innerBeanWithAnno = innerBeanWithAnno; }

    @Override
    public String toString() {
        return new StringJoiner(", ", MyBean.class.getSimpleName() + "[", "]")
                .add("innerBeanWithoutAnno=" + innerBeanWithoutAnno)
                .add("innerBeanWithAnno=" + innerBeanWithAnno)
                .add("id='" + id + "'")
                .add("name='" + name + "'")
                .toString();
    }
}
```

Les instances de la `InnerBean` classe suivante sont des champs de `MyBean` et sont initialisées en tant qu'objets vides dans l'exemple de code.

```
@DynamoDbBean
public class InnerBean {

    private String innerBeanField;

    public String getInnerBeanField() {
        return innerBeanField;
    }

    public void setInnerBeanField(String innerBeanField) {
        this.innerBeanField = innerBeanField;
    }

    @Override
    public String toString() {
        return "InnerBean{" +
                "innerBeanField='" + innerBeanField + '\'' +
                '}';
    }
}
```

L'exemple de code suivant enregistre un `MyBean` objet avec des beans internes initialisés dans DynamoDB, puis récupère l'élément. La sortie enregistrée indique que le n'`innerBeanWithoutAnno`est pas initialisé, mais qu'il `innerBeanWithAnno` a été créé.

```
    public MyBean preserveEmptyObjectAnnoUsingGetItemExample(DynamoDbTable<MyBean> myBeanTable) {
        // Save an item to DynamoDB.
        MyBean bean = new MyBean();
        bean.setId("1");
        bean.setInnerBeanWithoutAnno(new InnerBean());   // Instantiate the inner bean.
        bean.setInnerBeanWithAnno(new InnerBean());      // Instantiate the inner bean.
        myBeanTable.putItem(bean);

        GetItemEnhancedRequest request = GetItemEnhancedRequest.builder()
                .key(Key.builder().partitionValue("1").build())
                .build();
        MyBean myBean = myBeanTable.getItem(request);

        logger.info(myBean.toString());
        // Output 'MyBean[innerBeanWithoutAnno=null, innerBeanWithAnno=InnerBean{innerBeanField='null'}, id='1', name='null']'.

        return myBean;
    }
```

## Schéma statique alternatif
<a name="ddb-en-client-adv-features-empty-ex1-static"></a>

Vous pouvez utiliser la `StaticTableSchema` version suivante des schémas de table à la place des annotations sur les beans.

```
    public static TableSchema<MyBean> buildStaticSchemas() {

        StaticTableSchema<InnerBean> innerBeanStaticTableSchema =
                StaticTableSchema.builder(InnerBean.class)
                        .newItemSupplier(InnerBean::new)
                        .addAttribute(String.class, a -> a.name("innerBeanField")
                                .getter(InnerBean::getInnerBeanField)
                                .setter(InnerBean::setInnerBeanField))
                        .build();

        return StaticTableSchema.builder(MyBean.class)
                .newItemSupplier(MyBean::new)
                .addAttribute(String.class, a -> a.name("id")
                        .getter(MyBean::getId)
                        .setter(MyBean::setId)
                        .addTag(primaryPartitionKey()))
                .addAttribute(String.class, a -> a.name("name")
                        .getter(MyBean::getName)
                        .setter(MyBean::setName))
                .addAttribute(EnhancedType.documentOf(InnerBean.class,
                                innerBeanStaticTableSchema),
                        a -> a.name("innerBean1")
                                .getter(MyBean::getInnerBeanWithoutAnno)
                                .setter(MyBean::setInnerBeanWithoutAnno))
                .addAttribute(EnhancedType.documentOf(InnerBean.class,
                                innerBeanStaticTableSchema,
                                b -> b.preserveEmptyObject(true)),
                        a -> a.name("innerBean2")
                                .getter(MyBean::getInnerBeanWithAnno)
                                .setter(MyBean::setInnerBeanWithAnno))
                .build();
    }
```

# Évitez de sauvegarder les attributs nuls des objets imbriqués
<a name="ddb-en-client-adv-features-ignore-null"></a>

Vous pouvez ignorer les attributs nuls des objets imbriqués lorsque vous enregistrez un objet de classe de données dans DynamoDB en appliquant l'annotation. `@DynamoDbIgnoreNulls` En revanche, les attributs de niveau supérieur contenant des valeurs nulles ne sont jamais enregistrés dans la base de données.

Pour illustrer le fonctionnement de l'annotation, l'exemple de code utilise les deux beans suivants.

## Exemple de haricots
<a name="ddb-en-client-adv-features-ignore-null-ex1"></a>

La classe de données suivante contient deux `InnerBean` champs. La méthode getter`getInnerBeanWithoutAnno()`, n'est pas annotée. La `getInnerBeanWithIgnoreNullsAnno()` méthode est annotée avec`@DynamoDbIgnoreNulls`.

```
@DynamoDbBean
public class MyBean {

    private String id;
    private String name;
    private InnerBean innerBeanWithoutAnno;
    private InnerBean innerBeanWithIgnoreNullsAnno;

    @DynamoDbPartitionKey
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public InnerBean getInnerBeanWithoutAnno() { return innerBeanWithoutAnno; }
    public void setInnerBeanWithoutAnno(InnerBean innerBeanWithoutAnno) { this.innerBeanWithoutAnno = innerBeanWithoutAnno; }

    @DynamoDbIgnoreNulls
    public InnerBean getInnerBeanWithIgnoreNullsAnno() { return innerBeanWithIgnoreNullsAnno; }
    public void setInnerBeanWithIgnoreNullsAnno(InnerBean innerBeanWithAnno) { this.innerBeanWithIgnoreNullsAnno = innerBeanWithAnno; }

    @Override
    public String toString() {
        return new StringJoiner(", ", MyBean.class.getSimpleName() + "[", "]")
                .add("innerBeanWithoutAnno=" + innerBeanWithoutAnno)
                .add("innerBeanWithIgnoreNullsAnno=" + innerBeanWithIgnoreNullsAnno)
                .add("id='" + id + "'")
                .add("name='" + name + "'")
                .toString();
    }
}
```

Les instances de la `InnerBean` classe suivante sont des champs de `MyBean` et sont utilisées dans l'exemple de code suivant.

```
@DynamoDbBean
public class InnerBean {

    private String innerBeanFieldString;
    private Integer innerBeanFieldInteger;

    public String getInnerBeanFieldString() { return innerBeanFieldString; }
    public void setInnerBeanFieldString(String innerBeanFieldString) { this.innerBeanFieldString = innerBeanFieldString; }

    public Integer getInnerBeanFieldInteger() { return innerBeanFieldInteger; }
    public void setInnerBeanFieldInteger(Integer innerBeanFieldInteger) { this.innerBeanFieldInteger = innerBeanFieldInteger; }

    @Override
    public String toString() {
        return new StringJoiner(", ", InnerBean.class.getSimpleName() + "[", "]")
                .add("innerBeanFieldString='" + innerBeanFieldString + "'")
                .add("innerBeanFieldInteger=" + innerBeanFieldInteger)
                .toString();
    }
}
```

L'exemple de code suivant crée un `InnerBean` objet et attribue une valeur à un seul de ses deux attributs. 

```
    public void ignoreNullsAnnoUsingPutItemExample(DynamoDbTable<MyBean> myBeanTable) {
        // Create an InnerBean object and give only one attribute a value.
        InnerBean innerBeanOneAttributeSet = new InnerBean();
        innerBeanOneAttributeSet.setInnerBeanFieldInteger(200);

        // Create a MyBean instance and use the same InnerBean instance both for attributes.
        MyBean bean = new MyBean();
        bean.setId("1");
        bean.setInnerBeanWithoutAnno(innerBeanOneAttributeSet);
        bean.setInnerBeanWithIgnoreNullsAnno(innerBeanOneAttributeSet);

        Map<String, AttributeValue> itemMap = myBeanTable.tableSchema().itemToMap(bean, true);
        logger.info(itemMap.toString());
        // Log the map that is sent to the database.
        // {innerBeanWithIgnoreNullsAnno=AttributeValue(M={innerBeanFieldInteger=AttributeValue(N=200)}), id=AttributeValue(S=1), innerBeanWithoutAnno=AttributeValue(M={innerBeanFieldInteger=AttributeValue(N=200), innerBeanFieldString=AttributeValue(NUL=true)})}
        
        // Save the MyBean object to the table.
        myBeanTable.putItem(bean);
    }
```

Pour visualiser les données de bas niveau envoyées à DynamoDB, le code enregistre la carte attributaire avant d'enregistrer l'objet. `MyBean`

La sortie enregistrée montre qu'elle `innerBeanWithIgnoreNullsAnno` produit un attribut,

```
innerBeanWithIgnoreNullsAnno=AttributeValue(M={innerBeanFieldInteger=AttributeValue(N=200)})
```

L'`innerBeanWithoutAnno`instance génère deux attributs. L'un des attributs a une valeur de 200 et l'autre est un attribut de valeur nulle.

```
innerBeanWithoutAnno=AttributeValue(M={innerBeanFieldInteger=AttributeValue(N=200), innerBeanFieldString=AttributeValue(NUL=true)})
```

## Représentation JSON de la carte attributaire
<a name="ddb-en-client-adv-features-ignore-null-ex2"></a>

La représentation JSON suivante permet de visualiser plus facilement les données enregistrées dans DynamoDB.

```
{
  "id": {
    "S": "1"
  },
  "innerBeanWithIgnoreNullsAnno": {
    "M": {
      "innerBeanFieldInteger": {
        "N": "200"
      }
    }
  },
  "innerBeanWithoutAnno": {
    "M": {
      "innerBeanFieldInteger": {
        "N": "200"
      },
      "innerBeanFieldString": {
        "NULL": true
      }
    }
  }
}
```

## Schéma statique alternatif
<a name="ddb-en-client-adv-features-empty-ex1-static"></a>

Vous pouvez utiliser la `StaticTableSchema` version suivante des schémas de table pour mettre en place des annotations de classe de données.

```
public static TableSchema<MyBean> buildStaticSchemas() {

    StaticTableSchema<InnerBean> innerBeanStaticTableSchema =
        StaticTableSchema.builder(InnerBean.class)
            .newItemSupplier(InnerBean::new)
            .addAttribute(String.class, a -> a.name("innerBeanFieldString")
                .getter(InnerBean::getInnerBeanFieldString)
                .setter(InnerBean::setInnerBeanFieldString))
            .addAttribute(Integer.class, a -> a.name("innerBeanFieldInteger")
                .getter(InnerBean::getInnerBeanFieldInteger)
                .setter(InnerBean::setInnerBeanFieldInteger))
            .build();

    return StaticTableSchema.builder(MyBean.class)
        .newItemSupplier(MyBean::new)
        .addAttribute(String.class, a -> a.name("id")
            .getter(MyBean::getId)
            .setter(MyBean::setId)
            .addTag(primaryPartitionKey()))
        .addAttribute(String.class, a -> a.name("name")
            .getter(MyBean::getName)
            .setter(MyBean::setName))
        .addAttribute(EnhancedType.documentOf(InnerBean.class,
                innerBeanStaticTableSchema),
            a -> a.name("innerBeanWithoutAnno")
                .getter(MyBean::getInnerBeanWithoutAnno)
                .setter(MyBean::setInnerBeanWithoutAnno))
        .addAttribute(EnhancedType.documentOf(InnerBean.class,
                innerBeanStaticTableSchema,
                b -> b.ignoreNulls(true)),
            a -> a.name("innerBeanWithIgnoreNullsAnno")
                .getter(MyBean::getInnerBeanWithIgnoreNullsAnno)
                .setter(MyBean::setInnerBeanWithIgnoreNullsAnno))
        .build();
}
```