

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

# Usa funzionalità di mappatura avanzate
<a name="ddb-en-client-adv-features"></a>

Scopri le funzionalità avanzate dello schema delle tabelle nell'API DynamoDB Enhanced Client.

## Comprendi i tipi di schemi delle tabelle
<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)`è l'interfaccia per la funzionalità di mappatura dell'API DynamoDB Enhanced Client. Può mappare un oggetto di dati da e verso una mappa di. [AttributeValues](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/AttributeValue.html) Un `TableSchema` oggetto deve conoscere la struttura della tabella che sta mappando. Queste informazioni sulla struttura vengono memorizzate in 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)oggetto.

L'API client avanzata ha diverse implementazioni di`TableSchema`, che seguono. 

### Schema di tabella generato da classi annotate
<a name="ddb-en-client-adv-features-schema-mapped"></a>

È un'operazione moderatamente costosa creare una a `TableSchema` partire da classi annotate, quindi consigliamo di farlo una sola volta, all'avvio dell'applicazione.

 [ BeanTableSchema ](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/BeanTableSchema.html)   
Questa implementazione è costruita sulla base degli attributi e delle annotazioni di una classe bean. Un esempio di questo approccio è illustrato nella [sezione Guida introduttiva](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean).  
Se a non `BeanTableSchema` si comporta come previsto, abilita la registrazione di debug per. `software.amazon.awssdk.enhanced.dynamodb.beans`

[ImmutableTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/ImmutableTableSchema.html)  
Questa implementazione è costruita da una classe di dati immutabile. Questo approccio è descritto nella sezione. [Lavora con classi di dati immutabili](ddb-en-client-use-immut.md)

### Schema di tabella generato con un builder
<a name="ddb-en-client-adv-features-schema-static"></a>

`TableSchema`I seguenti sono creati a partire dal codice utilizzando un builder. Questo approccio è meno costoso dell'approccio che utilizza classi di dati annotate. L'approccio builder evita l'uso di annotazioni e non richiede standard di denominazione. JavaBean 

[StaticTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticTableSchema.html)  
Questa implementazione è progettata per classi di dati mutabili. La sezione introduttiva di questa guida ha dimostrato come [generare un file `StaticTableSchema` utilizzando un builder](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)  
Analogamente a come si crea un`StaticTableSchema`, si genera un'implementazione di questo tipo `TableSchema` utilizzando un [builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticImmutableTableSchema.html) da utilizzare con classi di dati immutabili.

### Schema di tabella per dati senza schema fisso
<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)  
A differenza di altre implementazioni di`TableSchema`, non si definiscono gli attributi per un'`DocumentTableSchema`istanza. Di solito, si specificano solo chiavi primarie e fornitori di convertitori di attributi. Un'`EnhancedDocument`istanza fornisce gli attributi creati da singoli elementi o da una stringa JSON.

# Includi o escludi gli attributi in modo esplicito
<a name="ddb-en-client-adv-features-inex-attr"></a>

L'API DynamoDB Enhanced Client offre annotazioni per escludere gli attributi delle classi di dati dal diventare attributi su una tabella. Con l'API, puoi anche utilizzare un nome di attributo diverso dal nome dell'attributo della classe di dati.

## Escludi gli attributi
<a name="ddb-en-client-adv-features-inex-attr-ex"></a>

Per ignorare gli attributi che non devono essere mappati su una tabella DynamoDB, contrassegna l'attributo con l'annotazione. `@DynamoDbIgnore`

```
private String internalKey;

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

## Includi attributi
<a name="ddb-en-client-adv-features-inex-attr-in"></a>

Per modificare il nome di un attributo utilizzato nella tabella DynamoDB, contrassegnalo con `@DynamoDbAttribute` l'annotazione e fornisci un nome diverso.

```
private String internalKey;

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

# Controlla la conversione degli attributi
<a name="ddb-en-client-adv-features-conversion"></a>

Per impostazione predefinita, uno schema tabellare fornisce convertitori per molti tipi Java comuni tramite un'implementazione predefinita dell'`[AttributeConverterProvider](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverterProvider.html)`interfaccia. È possibile modificare il comportamento predefinito generale con un’implementazione `AttributeConverterProvider` personalizzata. Inoltre, è possibile modificare il convertitore per un singolo attributo.

Per un elenco dei convertitori disponibili, consultate l'[AttributeConverter](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverter.html)interfaccia Java doc.

## Fornisci fornitori di convertitori di attributi personalizzati
<a name="ddb-en-client-adv-features-conversion-prov"></a>

È possibile fornire una singola `AttributeConverterProvider` o una catena di messaggi ordinati `AttributeConverterProvider` tramite l'`@DynamoDbBean``(converterProviders = {…})`annotazione. Qualsiasi personalizzazione `AttributeConverterProvider` deve estendere l'`AttributeConverterProvider`interfaccia.

Tieni presente che se fornisci la tua catena di fornitori di convertitori di attributi, sostituirai il provider di convertitori predefinito,`DefaultAttributeConverterProvider`. Se si desidera utilizzare la funzionalità di`DefaultAttributeConverterProvider`, è necessario includerla nella catena. 

È anche possibile annotare il bean con un array `{}` vuoto. Ciò disabilita l'uso di qualsiasi fornitore di convertitori di attributi, incluso quello predefinito. In questo caso tutti gli attributi che devono essere mappati devono avere un proprio convertitore di attributi.

Il frammento seguente mostra un singolo fornitore di convertitori.

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

}
```

Il seguente frammento mostra l'uso di una catena di fornitori di convertitori. Poiché l'SDK predefinito viene fornito per ultimo, ha la priorità più bassa.

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

}
```

I generatori di schemi di tabelle statiche hanno un `attributeConverterProviders()` metodo che funziona allo stesso modo. Questo è mostrato nel frammento seguente.

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

## Sovrascrivi la mappatura di un singolo attributo
<a name="ddb-en-client-adv-features-conversion-single"></a>

Per sovrascrivere il modo in cui viene mappato un singolo attributo, fornisci un `AttributeConverter` per l'attributo. Questa aggiunta sostituisce tutti i convertitori forniti `AttributeConverterProviders` nello schema della tabella. Questo aggiunge un convertitore personalizzato solo per quell'attributo. Altri attributi, anche quelli dello stesso tipo, non utilizzeranno quel convertitore a meno che non sia specificato esplicitamente per quegli altri attributi.

L'`@DynamoDbConvertedBy`annotazione viene utilizzata per specificare la `AttributeConverter` classe personalizzata, come mostrato nel frammento seguente.

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

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

I costruttori di schemi statici hanno un metodo di creazione di attributi equivalente. `attributeConverter()` Questo metodo accetta un'istanza di an `AttributeConverter` come illustrato di seguito.

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

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

Questo esempio mostra un'`AttributeConverterProvider`implementazione che fornisce un convertitore di attributi per [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)gli oggetti. 

La `SimpleUser` classe seguente contiene un attributo denominato `lastUsedCookie` che è un'istanza di`HttpCookie`.

Il parametro delle `@DynamoDbBean` annotazioni elenca le due `AttributeConverterProvider` classi che forniscono convertitori.

------
#### [ 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`Nell'esempio seguente viene fornita un'istanza di un. `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);
        }
    }
```

### Codice di conversione
<a name="ddb-en-client-adv-features-conversion-example-code"></a>

Nel `transformFrom()` metodo della `HttpCookieConverter` classe seguente, il codice riceve un'`HttpCookie`istanza e la trasforma in una mappa DynamoDB memorizzata come attributo.

Il `transformTo()` metodo riceve un parametro di mappa DynamoDB, quindi richiama `HttpCookie` il costruttore che richiede un nome e un valore.

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

# Modifica il comportamento di aggiornamento degli attributi
<a name="ddb-en-client-adv-features-upd-behavior"></a>

*È possibile personalizzare il comportamento di aggiornamento dei singoli attributi quando si esegue un'operazione di aggiornamento.* [Alcuni esempi di operazioni di aggiornamento nell'API DynamoDB Enhanced Client [sono updateItem](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html#updateItem(T)) () e (). transactWriteItems](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#transactWriteItems(java.util.function.Consumer))

Ad esempio, immaginate di voler memorizzare un timestamp *creato su* un vostro record. Tuttavia, desideri che il suo valore venga scritto solo se non esiste alcun valore esistente per l'attributo già nel database. In questo caso, si utilizza il comportamento di `[WRITE\$1IF\$1NOT\$1EXISTS](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/UpdateBehavior.html#WRITE_IF_NOT_EXISTS)` aggiornamento.

L'esempio seguente mostra l'annotazione che aggiunge il comportamento all'`createdOn`attributo.

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

È possibile dichiarare lo stesso comportamento di aggiornamento quando si crea uno schema di tabella statico, come mostrato nell'esempio seguente dopo la riga di commento 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();
```

# Appiattisci gli attributi di altre classi
<a name="ddb-en-client-adv-features-flatmap"></a>

Se gli attributi della tabella sono distribuiti tra diverse classi Java, tramite ereditarietà o composizione, l'API Enhanced Client di DynamoDB fornisce supporto per riunire gli attributi in un'unica classe.

## Usa l'ereditarietà
<a name="ddb-en-client-adv-features-flatmap-inheritance"></a>

Se le tue classi utilizzano l'ereditarietà, usa i seguenti approcci per appiattire la gerarchia.

### Usa fagioli annotati
<a name="ddb-en-client-adv-features-flatmap-inheritance-anno"></a>

Per l'approccio all'annotazione, entrambe le classi devono contenere l'`@DynamoDbBean`annotazione e una classe deve contenere una o più annotazioni chiave primarie.

Di seguito vengono illustrati esempi di classi di dati che hanno una relazione di ereditarietà.

------
#### [ 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 ]

[`onMethod`L'opzione](https://projectlombok.org/features/experimental/onX) di Lombok copia le annotazioni DynamoDB basate sugli attributi, ad esempio, sul codice generato. `@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;
}
```

------

### Usa schemi statici
<a name="ddb-en-client-adv-features-flatmap-inheritance-static"></a>

Per l'approccio allo schema statico, utilizzate il `extend()` metodo del generatore per comprimere gli attributi della classe principale nella classe figlia. Questo è mostrato dopo la riga di commento 1 nell'esempio seguente.

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

Il precedente esempio di schema statico utilizza le seguenti classi di dati. Poiché la mappatura viene definita quando si crea lo schema statico della tabella, le classi di dati non richiedono annotazioni.

#### Classi di dati
<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;
}
```

------

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

Se le tue classi usano la composizione, usa i seguenti approcci per appiattire la gerarchia.

### Usa fagioli annotati
<a name="ddb-en-client-adv-features-flatmap-comp-anno"></a>

L'`@DynamoDbFlatten`annotazione appiattisce la classe contenuta.

I seguenti esempi di classi di dati utilizzano l'`@DynamoDbFlatten`annotazione per aggiungere efficacemente tutti gli attributi della `GenericRecord` classe contenuta alla classe. `Customer`

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

------

Puoi usare l'annotazione flatten per appiattire tutte le diverse classi idonee di cui hai bisogno. Vengono applicati i vincoli seguenti:
+ Tutti i nomi degli attributi devono essere unici dopo essere stati appiattiti.
+ Non deve mai esserci più di una chiave di partizione, chiave di ordinamento o nome di tabella.

### Usa schemi statici
<a name="ddb-en-client-adv-features-flatmap-comp-static"></a>

Quando crei uno schema di tabella statico, usa il `flatten()` metodo del generatore. Fornite anche i metodi getter e setter che identificano la classe contenuta.

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

Il precedente esempio di schema statico utilizza le seguenti classi di dati.

#### Classi di dati
<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;
}
```

------

Puoi utilizzare il modello builder per appiattire tutte le diverse classi idonee di cui hai bisogno.

## Implicazioni per altro codice
<a name="ddb-en-client-adv-features-flatmap-compare"></a>

Quando si utilizza l'`@DynamoDbFlatten`attributo (o il metodo `flatten()` builder), l'elemento in DynamoDB contiene un attributo per ogni attributo dell'oggetto composto. Include anche gli attributi dell'oggetto che lo compone. 

Al contrario, se annotate una classe di dati con una classe composta e non la utilizzate`@DynamoDbFlatten`, l'elemento viene salvato con l'oggetto composto come attributo singolo.

Ad esempio, confrontate la `Customer` classe mostrata nell'esempio di [appiattimento con l'esempio di composizione con](#ddb-en-client-adv-features-flatmap-comp-anno) e senza appiattimento dell'attributo. `record` È possibile visualizzare la differenza con JSON come illustrato nella tabella seguente.


****  

| Con appiattimento | Senza appiattimento | 
| --- | --- | 
| 3 attributi | 2 attributi | 
|  <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 differenza diventa importante se si dispone di altro codice che accede alla tabella DynamoDB che prevede di trovare determinati attributi.

# Lavora con attributi che sono bean, mappe, elenchi e set
<a name="ddb-en-client-adv-features-nested"></a>

Una definizione di bean, come la `Person` classe mostrata di seguito, potrebbe definire proprietà (o attributi) che si riferiscono a tipi con attributi aggiuntivi. Ad esempio, nella `Person` classe, `mainAddress` c'è una proprietà che si riferisce a un `Address` bean che definisce attributi di valore aggiuntivi. `addresses`si riferisce a una mappa Java, i cui elementi si riferiscono ai `Address` bean. Questi tipi complessi possono essere considerati contenitori di attributi semplici da utilizzare per il valore dei dati nel contesto di DynamoDB. 

*DynamoDB si riferisce alle proprietà di valore degli elementi nidificati, come mappe, elenchi o bean, come attributi nidificati.* *La [Amazon DynamoDB Developer](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes) Guide si riferisce al formato salvato di una mappa, un elenco o un bean Java come tipo di documento.* Gli attributi semplici utilizzati per il loro valore di dati in Java sono denominati *tipi scalari* in DynamoDB. *I set, che contengono più elementi scalari dello stesso tipo e sono denominati tipi di set.* 

È importante sapere che l'API DynamoDB Enhanced Client converte una proprietà che è bean in un tipo di documento di mappa DynamoDB quando viene salvata.

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

## Salva tipi complessi
<a name="ddb-en-client-adv-features-nested-mapping"></a>

### Usa classi di dati annotate
<a name="ddb-en-client-adv-features-nested-map-anno"></a>

Puoi salvare gli attributi annidati per le classi personalizzate semplicemente annotandoli. La `Address` classe e la `PhoneNumber` classe mostrate in precedenza vengono annotate solo con l'annotazione. `@DynamoDbBean` Quando l'API DynamoDB Enhanced Client crea lo schema della tabella per la classe con `Person` il seguente frammento, l'API rileva l'uso delle classi `PhoneNumber` and e crea le mappature corrispondenti per `Address` funzionare con DynamoDB.

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

### Usa schemi astratti con i builder
<a name="ddb-en-client-adv-features-nested-map-builder"></a>

L'approccio alternativo consiste nell'utilizzare generatori di schemi di tabelle statiche per ogni classe bean annidata, come mostrato nel codice seguente.

Gli schemi di tabella per le `PhoneNumber` classi `Address` and sono astratti, nel senso che non possono essere utilizzati con una tabella DynamoDB. Questo perché mancano di definizioni per la chiave primaria. Vengono tuttavia utilizzati come schemi annidati nello schema tabellare della classe. `Person`

Dopo le righe di commento 1 e 2 nella definizione di`PERSON_TABLE_SCHEMA`, viene visualizzato il codice che utilizza gli schemi di tabella astratti. L'uso di `documentOf` nel `EnhanceType.documentOf(...)` metodo non indica che il metodo restituisca un `EnhancedDocument` tipo di Enhanced Document API. Il `documentOf(...)` metodo in questo contesto restituisce un oggetto che sa come mappare l'argomento della classe da e verso gli attributi della tabella DynamoDB utilizzando l'argomento dello schema della tabella.

#### Codice dello schema statico
<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();
```

## Attributi di progetto di tipi complessi
<a name="ddb-en-client-adv-features-nested-projection"></a>

Per `query()` i `scan()` metodi, è possibile specificare quali attributi si desidera vengano restituiti nei risultati utilizzando chiamate di metodo come `addNestedAttributeToProject()` e`attributesToProject()`. L'API DynamoDB Enhanced Client converte i parametri di chiamata al metodo Java [in espressioni di proiezione](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ProjectionExpressions.html) prima dell'invio della richiesta.

L'esempio seguente popola la `Person` tabella con due elementi, quindi esegue tre operazioni di scansione. 

La prima scansione accede a tutti gli elementi della tabella per confrontare i risultati con le altre operazioni di scansione. 

La seconda scansione utilizza il metodo [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))builder per restituire solo il `street` valore dell'attributo.

La terza operazione di scansione utilizza il metodo [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...))builder per restituire i dati per l'attributo di primo livello,. `hobbies` Il tipo di attributo di `hobbies` è un elenco. Per accedere a singoli elementi dell'elenco, eseguire un'`get()`operazione sull'elenco.

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

**Nota**  
Se il `attributesToProject()` metodo segue qualsiasi altro metodo di creazione che aggiunge gli attributi che desiderate proiettare, l'elenco dei nomi degli attributi fornito `attributesToProject()` sostituisce tutti gli altri nomi di attributo.  
Una scansione eseguita con l'`ScanEnhancedRequest`istanza nel frammento seguente restituisce solo dati relativi agli hobby.  

```
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]}
```
Il seguente frammento di codice utilizza innanzitutto il metodo. `attributesToProject()` Questo ordinamento conserva tutti gli altri attributi richiesti.  

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

## Usa tipi complessi nelle espressioni
<a name="ddb-en-client-adv-features-nested-expressions"></a>

È possibile utilizzare tipi complessi nelle espressioni, ad esempio espressioni di filtro ed espressioni di condizione, utilizzando gli operatori di dereferenziamento per navigare nella struttura del tipo complesso. Per oggetti e mappe, usa `. (dot)` e per gli elementi dell'elenco `[n]` (parentesi quadre attorno al numero di sequenza dell'elemento). Non puoi fare riferimento ai singoli elementi di un set, ma puoi usare la [`contains`funzione](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions).

L'esempio seguente mostra due espressioni di filtro utilizzate nelle operazioni di scansione. Le espressioni di filtro specificano le condizioni di corrispondenza per gli elementi da inserire nei risultati. L'esempio utilizza e `Person` `Address` le `PhoneNumber` classi mostrate in precedenza.

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

### Metodo di supporto che popola la tabella
<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);
    }
```

### Rappresentazione JSON degli elementi del database
<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"
  }
 ]
}
```

## Aggiorna gli elementi che contengono tipi complessi
<a name="ddb-en-client-adv-features-nested-updates"></a>

Per aggiornare un elemento che contiene tipi complessi, sono disponibili due approcci di base:
+ Approccio 1: recupera prima l'elemento (utilizzando`getItem`), aggiorna l'oggetto, quindi chiama`DynamoDbTable#updateItem`.
+ Approccio 2: non recuperate l'elemento, ma create una nuova istanza, impostate le proprietà che desiderate aggiornare e inviate l'istanza `DynamoDbTable#updateItem` impostando il valore appropriato di. [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) Questo approccio non richiede il recupero dell'elemento prima di aggiornarlo.

Gli esempi mostrati in questa sezione utilizzano le `PhoneNumber` classi `Person``Address`, e mostrate in precedenza.

### Approccio di aggiornamento 1: recupera, quindi aggiorna
<a name="ddb-en-client-adv-features-nested-updates-retreive"></a>

Utilizzando questo approccio, vi assicurate che nessun dato vada perso durante l'aggiornamento. L'API DynamoDB Enhanced Client ricrea il bean con gli attributi dell'elemento salvato in DynamoDB, inclusi i valori di tipi complessi. È quindi necessario utilizzare i getter e i setter per aggiornare il bean. Lo svantaggio di questo approccio è il costo che si deve sostenere per recuperare prima l'articolo.

L'esempio seguente dimostra che non si perde alcun dato se si recupera l'elemento prima di aggiornarlo.

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

### Approccio di aggiornamento 2: utilizzare un `IgnoreNullsMode` enum senza prima recuperare l'elemento
<a name="ddb-en-client-adv-features-nested-updates-nullmode"></a>

Per aggiornare un elemento in DynamoDB, puoi fornire un nuovo oggetto con solo le proprietà che desideri aggiornare e lasciare gli altri valori come nulli. Con questo approccio, è necessario essere consapevoli di come i valori nulli nell'oggetto vengono trattati dall'SDK e di come è possibile controllarne il comportamento.

Per specificare quali proprietà con valori nulli vuoi che l'SDK ignori, fornisci un enum quando crei il. `IgnoreNullsMode` [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) Come esempio di utilizzo di uno dei valori enumerati, il frammento seguente utilizza la modalità. `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"
    }
  }
*/
```

[L'Amazon DynamoDB Developer Guide contiene ulteriori informazioni sulle espressioni di aggiornamento.](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html)

#### Descrizioni delle opzioni `IgnoreNullsMode`
<a name="ignore-nulls-mode-descriptions"></a>
+ `IgnoreNullsMode.SCALAR_ONLY`- Utilizzate questa impostazione per aggiornare gli attributi scalari a qualsiasi livello. L'SDK crea un'istruzione di aggiornamento che invia solo attributi scalari non nulli a DynamoDB. L'SDK ignora gli attributi scalari con valori nulli di un bean o di una mappa, mantenendo il valore salvato in DynamoDB.

  Quando si aggiorna un attributo scalare di map o bean, la mappa deve già esistere in DynamoDB. Se aggiungi una mappa o un bean all'oggetto che non esiste già per l'oggetto in DynamoDB, ricevi `DynamoDbException` un messaggio con il *messaggio Il percorso del documento fornito nell'espressione di aggiornamento non è valido per l'aggiornamento*. È necessario utilizzare `MAPS_ONLY` la modalità per aggiungere un bean o una map a DynamoDB prima di aggiornare uno qualsiasi dei suoi attributi.
+ `IgnoreNullsMode.MAPS_ONLY`- Utilizzate questa impostazione per aggiungere o sostituire proprietà che sono un bean o una mappa. L'SDK sostituisce o aggiunge qualsiasi mappa o bean fornito nell'oggetto. Tutti i bean o le mappe che sono nulli nell'oggetto vengono ignorati, mantenendo la mappa esistente in DynamoDB.
+ `IgnoreNullsMode.DEFAULT`- Con questa impostazione, l'SDK non ignora mai i valori nulli. Gli attributi scalari a qualsiasi livello che sono nulli vengono aggiornati a null. L'SDK aggiorna qualsiasi proprietà bean, map, list o set con valori nulli nell'oggetto su null in DynamoDB. Quando si utilizza questa modalità, o non si fornisce una modalità poiché è la modalità predefinita, è necessario recuperare prima l'elemento in modo che i valori in DynamoDB non siano impostati su null forniti nell'oggetto per l'aggiornamento, a meno che non si intenda impostare i valori su null.

In tutte le modalità, se si fornisce un oggetto con un elenco o un set non nullo, l'elenco o il set viene salvato in DynamoDB. `updateItem` 

#### Perché le modalità?
<a name="ddb-en-client-adv-features-nested-updates-nullmodes-why"></a>

Quando fornisci un oggetto con un bean o una map al `updateItem` metodo, l'SDK non è in grado di stabilire se utilizzare i valori delle proprietà nel bean (o i valori di immissione nella mappa) per aggiornare l'elemento o se l'intero bean/map deve sostituire ciò che è stato salvato in DynamoDB.

Partendo dal nostro esempio precedente che mostra innanzitutto il recupero dell'elemento, proviamo ad aggiornare l'`city`attributo di without the retrieval. `mainAddress`

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

I due esempi seguenti mostrano gli usi di e dei valori enumerati`MAPS_ONLY`. `SCALAR_ONLY` `MAPS_ONLY`aggiunge una mappa e `SCALAR_ONLY` aggiorna una mappa.

##### `IgnoreNullsMode.MAPS_ONLY` Esempio
<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.
    }
```

Fate riferimento alla tabella seguente per vedere quali valori nulli vengono ignorati per ciascuna modalità. Spesso è possibile lavorare con entrambi`SCALAR_ONLY`, `MAPS_ONLY` tranne quando si lavora con bean o mappe.


**Quali proprietà con valori nulli nell'oggetto inviato vengono ignorate dall'SDK per `updateItem` ciascuna modalità?**  

| Tipo di proprietà | in modalità SCALAR\$1ONLY | in modalità MAPS\$1ONLY | in modalità DEFAULT | 
| --- | --- | --- | --- | 
| Scalare superiore | Sì  | Sì | No | 
| Bean o mappa | Sì  | Sì | No | 
| Valore scalare di un bean o di una voce di mappa | Sì1 | No2 | No | 
| Elenco o set | Sì  | Sì | No | 

1 Ciò presuppone che la mappa esista già in DynamoDB. Qualsiasi valore scalare, nullo o non nullo, del bean o della mappa fornito nell'oggetto per l'aggiornamento richiede che esista un percorso al valore in DynamoDB. L'SDK costruisce un percorso verso l'attributo utilizzando l'operatore di dereferenza prima di inviare la richiesta. `. (dot)`

2 Poiché si utilizza la `MAPS_ONLY` modalità per sostituire completamente o aggiungere un bean o una map, tutti i valori null nel bean o nella mappa vengono mantenuti nella mappa salvata in DynamoDB.

# Conserva gli oggetti vuoti con `@DynamoDbPreserveEmptyObject`
<a name="ddb-en-client-adv-features-empty"></a>

Se salvi un bean in Amazon DynamoDB con oggetti vuoti e desideri che l'SDK ricrei gli oggetti vuoti al momento del recupero, annota il getter del bean interno con. `@DynamoDbPreserveEmptyObject`

Per illustrare come funziona l'annotazione, l'esempio di codice utilizza i due bean seguenti.

## Fagioli di esempio
<a name="ddb-en-client-adv-features-empty-ex1"></a>

La seguente classe di dati contiene due `InnerBean` campi. Il metodo getter`getInnerBeanWithoutAnno()`, non è annotato con. `@DynamoDbPreserveEmptyObject` Il `getInnerBeanWithAnno()` metodo è annotato.

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

Le istanze della `InnerBean` classe seguente sono campi di `MyBean` e vengono inizializzate come oggetti vuoti nel codice di esempio.

```
@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 + '\'' +
                '}';
    }
}
```

Il seguente esempio di codice salva un `MyBean` oggetto con bean interni inizializzati in DynamoDB e quindi recupera l'elemento. L'output registrato mostra che non `innerBeanWithoutAnno` è stato inizializzato, ma è stato creato. `innerBeanWithAnno`

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

## Schema statico alternativo
<a name="ddb-en-client-adv-features-empty-ex1-static"></a>

È possibile utilizzare la seguente `StaticTableSchema` versione degli schemi delle tabelle al posto delle annotazioni sui bean.

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

# Evita di salvare gli attributi nulli degli oggetti annidati
<a name="ddb-en-client-adv-features-ignore-null"></a>

È possibile ignorare gli attributi nulli degli oggetti nidificati quando si salva un oggetto di classe di dati in DynamoDB applicando l'annotazione. `@DynamoDbIgnoreNulls` Al contrario, gli attributi di primo livello con valori nulli non vengono mai salvati nel database.

Per illustrare come funziona l'annotazione, l'esempio di codice utilizza i due bean seguenti.

## Fagioli di esempio
<a name="ddb-en-client-adv-features-ignore-null-ex1"></a>

La seguente classe di dati contiene due `InnerBean` campi. Il metodo getter`getInnerBeanWithoutAnno()`, non è annotato. Il `getInnerBeanWithIgnoreNullsAnno()` metodo è annotato con. `@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();
    }
}
```

Le istanze della seguente `InnerBean` classe sono campi di `MyBean` e vengono utilizzate nel seguente codice di esempio.

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

Il seguente esempio di codice crea un `InnerBean` oggetto e imposta solo uno dei suoi due attributi con un valore. 

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

Per visualizzare i dati di basso livello inviati a DynamoDB, il codice registra la mappa degli attributi prima di salvare l'oggetto. `MyBean`

L'output registrato mostra che restituisce un attributo, `innerBeanWithIgnoreNullsAnno`

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

L'`innerBeanWithoutAnno`istanza restituisce due attributi. Un attributo ha un valore di 200 e l'altro è un attributo con valore nullo.

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

## Rappresentazione JSON della mappa degli attributi
<a name="ddb-en-client-adv-features-ignore-null-ex2"></a>

La seguente rappresentazione JSON semplifica la visualizzazione dei dati salvati in DynamoDB.

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

## Schema statico alternativo
<a name="ddb-en-client-adv-features-empty-ex1-static"></a>

È possibile utilizzare la seguente `StaticTableSchema` versione degli schemi delle tabelle al posto delle annotazioni delle classi di dati.

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