

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

# Verwenden Sie erweiterte Mapping-Funktionen
<a name="ddb-en-client-adv-features"></a>

Erfahren Sie mehr über erweiterte Tabellenschemafunktionen in der DynamoDB Enhanced Client API.

## Machen Sie sich mit Tabellenschematypen vertraut
<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)`ist die Schnittstelle zur Mapping-Funktionalität der DynamoDB Enhanced Client API. Sie kann ein Datenobjekt einer Map von und zu einer Map von zuordnen. [AttributeValues](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/AttributeValue.html) Ein `TableSchema` Objekt muss die Struktur der Tabelle kennen, die es abbildet. Diese Strukturinformationen werden in einem [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)Objekt gespeichert.

Die erweiterte Client-API hat mehrere Implementierungen von`TableSchema`, die im Folgenden aufgeführt sind. 

### Aus kommentierten Klassen generiertes Tabellenschema
<a name="ddb-en-client-adv-features-schema-mapped"></a>

Das Erstellen eines `TableSchema` aus annotierten Klassen ist ein relativ teurer Vorgang. Wir empfehlen daher, dies einmal beim Start der Anwendung zu tun.

 [ BeanTableSchema ](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/BeanTableSchema.html)   
Diese Implementierung basiert auf Attributen und Anmerkungen einer Bean-Klasse. Ein Beispiel für diesen Ansatz wird im [Abschnitt Erste Schritte](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean) demonstriert.  
Wenn `BeanTableSchema` sich a nicht wie erwartet verhält, aktivieren Sie die Debug-Protokollierung für. `software.amazon.awssdk.enhanced.dynamodb.beans`

[ImmutableTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/ImmutableTableSchema.html)  
Diese Implementierung basiert auf einer unveränderlichen Datenklasse. Dieser Ansatz wird im [Arbeiten Sie mit unveränderlichen Datenklassen](ddb-en-client-use-immut.md) Abschnitt beschrieben.

### Mit einem Builder generiertes Tabellenschema
<a name="ddb-en-client-adv-features-schema-static"></a>

Die folgenden `TableSchema` s werden mithilfe eines Builders aus Code erstellt. Dieser Ansatz ist kostengünstiger als der Ansatz, bei dem annotierte Datenklassen verwendet werden. Der Builder-Ansatz vermeidet die Verwendung von Anmerkungen und erfordert keine JavaBean Benennungsstandards.

[StaticTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticTableSchema.html)  
Diese Implementierung wurde für veränderbare Datenklassen entwickelt. Im Abschnitt „Erste Schritte“ dieses Handbuchs wurde gezeigt, wie Sie [`StaticTableSchema`mithilfe eines Builders eine generieren](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)  
Ähnlich wie beim Erstellen eines generieren Sie eine Implementierung dieses Typs`StaticTableSchema`, indem Sie einen [Builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticImmutableTableSchema.html) für die `TableSchema` Verwendung mit unveränderlichen Datenklassen verwenden.

### Tabellenschema für Daten ohne festes Schema
<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)  
Im Gegensatz zu anderen Implementierungen von `TableSchema` definieren Sie keine Attribute für eine `DocumentTableSchema` Instanz. Normalerweise geben Sie nur Primärschlüssel und Anbieter von Attributkonvertern an. Eine `EnhancedDocument` Instanz stellt die Attribute bereit, die Sie aus einzelnen Elementen oder aus einer JSON-Zeichenfolge erstellen.

# Fügen Sie Attribute explizit ein oder schließen Sie sie aus
<a name="ddb-en-client-adv-features-inex-attr"></a>

Die DynamoDB Enhanced Client API bietet Anmerkungen, um Datenklassenattribute daran zu hindern, zu Attributen in einer Tabelle zu werden. Mit der API können Sie auch einen Attributnamen verwenden, der sich vom Attributnamen der Datenklasse unterscheidet.

## Attribute ausschließen
<a name="ddb-en-client-adv-features-inex-attr-ex"></a>

Um Attribute zu ignorieren, die keiner DynamoDB-Tabelle zugeordnet werden sollten, markieren Sie das Attribut mit der Anmerkung. `@DynamoDbIgnore`

```
private String internalKey;

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

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

Um den Namen eines in der DynamoDB-Tabelle verwendeten Attributs zu ändern, markieren Sie es mit der `@DynamoDbAttribute` Anmerkung und geben Sie einen anderen Namen ein.

```
private String internalKey;

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

# Steuern Sie die Attributkonvertierung
<a name="ddb-en-client-adv-features-conversion"></a>

Standardmäßig stellt ein Tabellenschema über eine Standardimplementierung der `[AttributeConverterProvider](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverterProvider.html)` Schnittstelle Konverter für viele gängige Java-Typen bereit. Das allgemeine Standardverhalten lässt sich durch eine benutzerdefinierte `AttributeConverterProvider`-Implementierung ändern. Ebenso können Sie den Konverter für ein einzelnes Attribut anpassen.

Eine Liste der verfügbaren Konverter finden Sie im Java-Dokument zur [AttributeConverter](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverter.html)Schnittstelle.

## Stellen Sie Anbieter für benutzerdefinierte Attributkonverter bereit
<a name="ddb-en-client-adv-features-conversion-prov"></a>

Sie können über die `@DynamoDbBean` `(converterProviders = {…})` Anmerkung ein einzelnes `AttributeConverterProvider` oder eine Kette von bestellten `AttributeConverterProvider` S angeben. Jeder benutzerdefinierte `AttributeConverterProvider` Benutzer muss die `AttributeConverterProvider` Schnittstelle erweitern.

Beachten Sie, dass Sie, wenn Sie Ihre eigene Kette von Anbietern für Attributkonverter angeben, den Standardkonverter-Anbieter außer Kraft setzen`DefaultAttributeConverterProvider`. Wenn Sie die Funktionalität von verwenden möchten`DefaultAttributeConverterProvider`, müssen Sie sie in die Kette aufnehmen. 

Es ist auch möglich, die Bean mit einem leeren Array `{}` zu annotieren. Dadurch wird die Verwendung aller Anbieter für Attributkonverter deaktiviert, einschließlich der Standardanbieter. In diesem Fall müssen alle Attribute, die zugeordnet werden sollen, über einen eigenen Attributkonverter verfügen.

Der folgende Ausschnitt zeigt einen einzelnen Konverter-Anbieter.

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

}
```

Der folgende Ausschnitt zeigt die Verwendung einer Kette von Konverteranbietern. Da der SDK-Standard zuletzt bereitgestellt wird, hat er die niedrigste Priorität.

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

}
```

Die Schema-Builder für statische Tabellen verfügen über eine `attributeConverterProviders()` Methode, die auf die gleiche Weise funktioniert. Dies wird im folgenden Ausschnitt gezeigt.

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

## Überschreiben Sie die Zuordnung eines einzelnen Attributs
<a name="ddb-en-client-adv-features-conversion-single"></a>

Um die Art und Weise, wie ein einzelnes Attribut zugeordnet wird, zu überschreiben, geben Sie an `AttributeConverter` für das Attribut ein. Dieser Zusatz hat Vorrang vor allen Konvertern, die von `AttributeConverterProviders` im Tabellenschema bereitgestellt werden. Dadurch wird ein benutzerdefinierter Konverter nur für dieses Attribut hinzugefügt. Andere Attribute, auch solche desselben Typs, verwenden diesen Konverter nur, wenn er explizit für diese anderen Attribute angegeben ist.

Die `@DynamoDbConvertedBy` Anmerkung wird verwendet, um die benutzerdefinierte `AttributeConverter` Klasse anzugeben, wie im folgenden Codeausschnitt gezeigt.

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

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

Die Builder für statische Schemas verfügen über eine entsprechende Methode zur Erstellung von Attributen. `attributeConverter()` Diese Methode benötigt eine Instanz von a, `AttributeConverter` wie im Folgenden gezeigt.

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

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

Dieses Beispiel zeigt eine `AttributeConverterProvider` Implementierung, die einen Attributkonverter für [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)Objekte bereitstellt. 

Die folgende `SimpleUser` Klasse enthält ein Attribut mit dem Namen`lastUsedCookie`, das eine Instanz von ist`HttpCookie`.

Der Parameter für die `@DynamoDbBean` Anmerkungen listet die beiden `AttributeConverterProvider` Klassen auf, die Konverter bereitstellen.

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

------

Das `CookieConverterProvider` folgende Beispiel bietet eine Instanz `HttpCookeConverter` von.

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

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

In der `transformFrom()` Methode der folgenden `HttpCookieConverter` Klasse empfängt der Code eine `HttpCookie` Instanz und wandelt sie in eine DynamoDB-Map um, die als Attribut gespeichert wird.

Die `transformTo()` Methode empfängt einen DynamoDB-Zuordnungsparameter und ruft dann den `HttpCookie` Konstruktor auf, der einen Namen und einen Wert benötigt.

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

# Ändern Sie das Aktualisierungsverhalten von Attributen
<a name="ddb-en-client-adv-features-upd-behavior"></a>

Sie können das Aktualisierungsverhalten einzelner Attribute anpassen, wenn Sie einen *Aktualisierungsvorgang* durchführen. [Einige Beispiele für Aktualisierungsvorgänge in der DynamoDB Enhanced Client API sind [updateItem () und ()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html#updateItem(T)). transactWriteItems](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#transactWriteItems(java.util.function.Consumer))

Stellen Sie sich zum Beispiel vor, Sie möchten einen *am erstellten* Zeitstempel in Ihrem Datensatz speichern. Sie möchten jedoch, dass sein Wert nur geschrieben wird, wenn für das Attribut noch kein Wert in der Datenbank vorhanden ist. In diesem Fall verwenden Sie das `[WRITE\$1IF\$1NOT\$1EXISTS](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/UpdateBehavior.html#WRITE_IF_NOT_EXISTS)` Aktualisierungsverhalten.

Das folgende Beispiel zeigt die Anmerkung, die dem `createdOn` Attribut das Verhalten hinzufügt.

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

Sie können dasselbe Aktualisierungsverhalten deklarieren, wenn Sie ein statisches Tabellenschema erstellen, wie im folgenden Beispiel nach Kommentarzeile 1 gezeigt.

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

# Reduzieren Sie Attribute aus anderen Klassen
<a name="ddb-en-client-adv-features-flatmap"></a>

Wenn die Attribute für Ihre Tabelle entweder durch Vererbung oder Zusammensetzung auf mehrere verschiedene Java-Klassen verteilt sind, bietet die DynamoDB Enhanced Client-API Unterstützung, um die Attribute zu einer Klasse zusammenzufassen.

## Verwenden Sie Vererbung
<a name="ddb-en-client-adv-features-flatmap-inheritance"></a>

Wenn Ihre Klassen Vererbung verwenden, verwenden Sie die folgenden Methoden, um die Hierarchie zu vereinfachen.

### Verwenden Sie Beans mit Anmerkungen
<a name="ddb-en-client-adv-features-flatmap-inheritance-anno"></a>

Für den Annotationsansatz müssen beide Klassen die `@DynamoDbBean` Annotation enthalten und eine Klasse muss eine oder mehrere Primärschlüssel-Annotationen enthalten.

Im Folgenden werden Beispiele für Datenklassen gezeigt, die eine Vererbungsbeziehung haben.

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

Die [`onMethod`Option](https://projectlombok.org/features/experimental/onX) von Lombok kopiert attributbasierte DynamoDB-Anmerkungen, wie z. B., in den generierten Code. `@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;
}
```

------

### Verwenden Sie statische Schemas
<a name="ddb-en-client-adv-features-flatmap-inheritance-static"></a>

Verwenden Sie für den statischen Schemaansatz die `extend()` Methode des Builders, um die Attribute der übergeordneten Klasse auf die untergeordnete Klasse zu reduzieren. Dies wird im folgenden Beispiel nach Kommentarzeile 1 gezeigt.

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

Das vorherige Beispiel für ein statisches Schema verwendet die folgenden Datenklassen. Da die Zuordnung beim Erstellen des statischen Tabellenschemas definiert wird, benötigen die Datenklassen keine Anmerkungen.

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

------

## Verwenden Sie Zusammensetzung
<a name="ddb-en-client-adv-features-flatmap-comp"></a>

Wenn in Ihren Klassen Komposition verwendet wird, verwenden Sie die folgenden Methoden, um die Hierarchie zu vereinfachen.

### Verwenden Sie Beans mit Anmerkungen
<a name="ddb-en-client-adv-features-flatmap-comp-anno"></a>

Die `@DynamoDbFlatten` Anmerkung reduziert die enthaltene Klasse.

In den folgenden Beispielen für Datenklassen wird die `@DynamoDbFlatten` Anmerkung verwendet, um der Klasse effektiv alle Attribute der enthaltenen `GenericRecord` Klasse hinzuzufügen. `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;
}
```

------

Sie können die Annotation Flatten verwenden, um so viele verschiedene geeignete Klassen wie nötig zu reduzieren. Die folgenden Einschränkungen gelten:
+ Alle Attributnamen müssen eindeutig sein, nachdem sie reduziert wurden.
+ Es darf nie mehr als einen Partitionsschlüssel, Sortierschlüssel oder Tabellennamen geben.

### Verwenden Sie statische Schemas
<a name="ddb-en-client-adv-features-flatmap-comp-static"></a>

Wenn Sie ein statisches Tabellenschema erstellen, verwenden Sie die `flatten()` Methode des Builders. Sie stellen auch die Getter- und Setter-Methoden bereit, die die enthaltene Klasse identifizieren.

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

Das vorherige Beispiel für ein statisches Schema verwendet die folgenden Datenklassen.

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

------

Sie können das Builder-Muster verwenden, um so viele verschiedene in Frage kommende Klassen zu reduzieren, wie Sie möchten.

## Implikationen für anderen Code
<a name="ddb-en-client-adv-features-flatmap-compare"></a>

Wenn Sie das `@DynamoDbFlatten` Attribut (oder die `flatten()` Builder-Methode) verwenden, enthält das Element in DynamoDB ein Attribut für jedes Attribut des erstellten Objekts. Es enthält auch die Attribute des zusammengesetzten Objekts. 

Wenn Sie dagegen eine Datenklasse mit einer zusammengesetzten Klasse annotieren und diese nicht verwenden`@DynamoDbFlatten`, wird das Element zusammen mit dem zusammengesetzten Objekt als einzelnes Attribut gespeichert.

Vergleichen Sie beispielsweise die im [Beispiel „Reduzieren mit Komposition“ gezeigte `Customer` Klasse mit](#ddb-en-client-adv-features-flatmap-comp-anno) und ohne Reduzierung des Attributs. `record` Sie können den Unterschied mit JSON visualisieren, wie in der folgenden Tabelle dargestellt.


****  

| Mit Abflachen | Ohne Abflachen | 
| --- | --- | 
| 3 Attribute | 2 Attribute | 
|  <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>  | 

Der Unterschied wird wichtig, wenn Sie anderen Code haben, der auf die DynamoDB-Tabelle zugreift und erwartet, bestimmte Attribute zu finden.

# Arbeiten Sie mit Attributen wie Beans, Maps, Listen und Sets
<a name="ddb-en-client-adv-features-nested"></a>

Eine Bean-Definition, wie die unten gezeigte `Person` Klasse, könnte Eigenschaften (oder Attribute) definieren, die sich auf Typen mit zusätzlichen Attributen beziehen. In der `Person` Klasse `mainAddress` handelt es sich beispielsweise um eine Eigenschaft, die sich auf ein `Address` Bean bezieht, das zusätzliche Wertattribute definiert. `addresses`bezieht sich auf eine Java-Map, deren Elemente sich auf `Address` Beans beziehen. Diese komplexen Typen können als Container mit einfachen Attributen betrachtet werden, die Sie für ihren Datenwert im Kontext von DynamoDB verwenden. 

*DynamoDB bezeichnet die Werteigenschaften verschachtelter Elemente wie Maps, Listen oder Beans als verschachtelte Attribute.* Im [Amazon DynamoDB Developer Guide](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes) wird die gespeicherte Form einer Java-Map, -Liste oder -Bean als *Dokumenttyp* bezeichnet. Einfache Attribute, die Sie in Java für ihren Datenwert verwenden, werden in DynamoDB als *skalare Typen* bezeichnet. *Mengen, die mehrere skalare Elemente desselben Typs enthalten und als Mengentypen bezeichnet werden.* 

Es ist wichtig zu wissen, dass die DynamoDB Enhanced Client-API eine Eigenschaft, die Bean ist, beim Speichern in einen DynamoDB-Kartendokumenttyp konvertiert.

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

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

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

## Speichern Sie komplexe Typen
<a name="ddb-en-client-adv-features-nested-mapping"></a>

### Verwenden Sie annotierte Datenklassen
<a name="ddb-en-client-adv-features-nested-map-anno"></a>

Sie speichern verschachtelte Attribute für benutzerdefinierte Klassen, indem Sie sie einfach mit Anmerkungen versehen. Die `Address` Klasse und die `PhoneNumber` Klasse, die zuvor gezeigt wurden, sind nur mit der Anmerkung annotiert. `@DynamoDbBean` Wenn die DynamoDB Enhanced Client-API das Tabellenschema für die `Person` Klasse mit dem folgenden Codeausschnitt erstellt, erkennt die API die Verwendung der `PhoneNumber` Klassen `Address` und und erstellt die entsprechenden Mappings für die Arbeit mit DynamoDB.

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

### Verwenden Sie abstrakte Schemas mit Buildern
<a name="ddb-en-client-adv-features-nested-map-builder"></a>

Der alternative Ansatz besteht darin, Schema-Builder für statische Tabellen für jede verschachtelte Bean-Klasse zu verwenden, wie im folgenden Code gezeigt.

Die Tabellenschemas für die `PhoneNumber` Klassen `Address` und sind abstrakt in dem Sinne, dass sie nicht mit einer DynamoDB-Tabelle verwendet werden können. Das liegt daran, dass ihnen Definitionen für den Primärschlüssel fehlen. Sie werden jedoch als verschachtelte Schemas im Tabellenschema für die `Person` Klasse verwendet.

Nach den Kommentarzeilen 1 und 2 in der Definition von sehen Sie den Code`PERSON_TABLE_SCHEMA`, der die abstrakten Tabellenschemas verwendet. Die Verwendung von `documentOf` in der `EnhanceType.documentOf(...)` Methode bedeutet nicht, dass die Methode einen `EnhancedDocument` Typ der Enhanced Document API zurückgibt. Die `documentOf(...)` Methode gibt in diesem Kontext ein Objekt zurück, das weiß, wie es sein Klassenargument mithilfe des Tabellenschema-Arguments DynamoDB-Tabellenattributen zuordnen kann.

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

## Projektattribute komplexer Typen
<a name="ddb-en-client-adv-features-nested-projection"></a>

Für `query()` und `scan()` Methoden können Sie mithilfe von Methodenaufrufen wie `addNestedAttributeToProject()` und angeben, welche Attribute in den Ergebnissen zurückgegeben werden sollen`attributesToProject()`. Die DynamoDB Enhanced Client API konvertiert die Parameter des Java-Methodenaufrufs in [Projektionsausdrücke](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ProjectionExpressions.html), bevor die Anforderung gesendet wird.

Das folgende Beispiel füllt die `Person` Tabelle mit zwei Elementen und führt dann drei Scanvorgänge durch. 

Beim ersten Scan werden alle Elemente in der Tabelle abgerufen, um die Ergebnisse mit den anderen Scanvorgängen zu vergleichen. 

Der zweite Scan verwendet die [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-Methode, um nur den `street` Attributwert zurückzugeben.

Der dritte Scanvorgang verwendet die [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-Methode, um die Daten für das Attribut der ersten Ebene zurückzugeben,`hobbies`. Der Attributtyp von `hobbies` ist eine Liste. Um auf einzelne Listenelemente zuzugreifen, führen Sie einen `get()` Vorgang in der Liste aus.

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

**Anmerkung**  
Wenn die `attributesToProject()` Methode einer anderen Builder-Methode folgt, die Attribute hinzufügt, die Sie projizieren möchten, `attributesToProject()` ersetzt die mitgelieferte Liste der Attributnamen alle anderen Attributnamen.  
Ein Scan, der mit der `ScanEnhancedRequest` Instanz im folgenden Codeausschnitt durchgeführt wird, gibt nur Hobbydaten zurück.  

```
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]}
```
Der folgende Codeausschnitt verwendet die Methode zuerst. `attributesToProject()` Bei dieser Reihenfolge werden alle anderen angeforderten Attribute beibehalten.  

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

## Verwenden Sie komplexe Typen in Ausdrücken
<a name="ddb-en-client-adv-features-nested-expressions"></a>

Sie können komplexe Typen in Ausdrücken wie Filterausdrücken und Bedingungsausdrücken verwenden, indem Sie Dereferenzierungsoperatoren verwenden, um in der Struktur des komplexen Typs zu navigieren. Verwenden Sie für Objekte und Maps das `. (dot)` und für Listenelemente use `[n]` (eckige Klammern um die Sequenznummer des Elements). Sie können nicht auf einzelne Elemente einer Menge verweisen, aber Sie können die [`contains`Funktion](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions) verwenden.

Das folgende Beispiel zeigt zwei Filterausdrücke, die bei Scanvorgängen verwendet werden. Die Filterausdrücke geben die Übereinstimmungsbedingungen für Elemente an, die Sie in den Ergebnissen haben möchten. In dem Beispiel werden `Person` die zuvor gezeigten `PhoneNumber` Klassen`Address`, und verwendet.

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

### Hilfsmethode, die die Tabelle auffüllt
<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);
    }
```

### JSON-Darstellung von Elementen in der Datenbank
<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"
  }
 ]
}
```

## Aktualisieren Sie Elemente, die komplexe Typen enthalten
<a name="ddb-en-client-adv-features-nested-updates"></a>

Um ein Element zu aktualisieren, das komplexe Typen enthält, haben Sie zwei grundlegende Methoden:
+ Ansatz 1: Rufen Sie zuerst das Element ab (mithilfe von`getItem`), aktualisieren Sie das Objekt und rufen Sie es dann auf`DynamoDbTable#updateItem`.
+ Ansatz 2: Rufen Sie das Element nicht ab, sondern erstellen Sie eine neue Instanz, legen Sie die Eigenschaften fest, die Sie aktualisieren möchten, und leiten Sie die Instanz weiter, `DynamoDbTable#updateItem` indem Sie den entsprechenden Wert von festlegen [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). Bei diesem Ansatz müssen Sie das Element nicht abrufen, bevor Sie es aktualisieren.

Die in diesem Abschnitt gezeigten Beispiele verwenden die zuvor aufgeführten `PhoneNumber` Klassen `Person``Address`, und.

### Aktualisierungsansatz 1: Abrufen, dann aktualisieren
<a name="ddb-en-client-adv-features-nested-updates-retreive"></a>

Mit diesem Ansatz stellen Sie sicher, dass beim Update keine Daten verloren gehen. Die DynamoDB Enhanced Client API erstellt die Bean mit den Attributen des in DynamoDB gespeicherten Elements neu, einschließlich Werten komplexer Typen. Anschließend müssen Sie die Getter und Setter verwenden, um die Bean zu aktualisieren. Der Nachteil dieses Ansatzes sind die Kosten, die Ihnen entstehen, wenn Sie den Artikel zuerst abrufen.

Das folgende Beispiel zeigt, dass keine Daten verloren gehen, wenn Sie das Element zuerst abrufen, bevor Sie es aktualisieren.

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

### Aktualisierungsansatz 2: Verwenden Sie eine `IgnoreNullsMode` Aufzählung, ohne das Element zuerst abzurufen
<a name="ddb-en-client-adv-features-nested-updates-nullmode"></a>

Um ein Element in DynamoDB zu aktualisieren, können Sie ein neues Objekt angeben, das nur die Eigenschaften hat, die Sie aktualisieren möchten, und die anderen Werte auf Null belassen. Bei diesem Ansatz müssen Sie wissen, wie Nullwerte im Objekt vom SDK behandelt werden und wie Sie das Verhalten steuern können.

Um anzugeben, welche Eigenschaften mit Nullwerten das SDK ignorieren soll, geben Sie beim Erstellen des eine `IgnoreNullsMode` Aufzählung an. [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) Als Beispiel für die Verwendung eines der Aufzählungswerte verwendet der folgende Codeausschnitt den Modus. `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"
    }
  }
*/
```

Das Amazon DynamoDB DynamoDB-Entwicklerhandbuch enthält weitere Informationen zu [Aktualisierungsausdrücken](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html).

#### Beschreibungen der Optionen `IgnoreNullsMode`
<a name="ignore-nulls-mode-descriptions"></a>
+ `IgnoreNullsMode.SCALAR_ONLY`- Verwenden Sie diese Einstellung, um skalare Attribute auf jeder Ebene zu aktualisieren. Das SDK erstellt eine Aktualisierungsanweisung, die nur skalare Attribute ungleich Null an DynamoDB sendet. Das SDK ignoriert nullwertige, skalare Attribute einer Bean oder Map und behält den gespeicherten Wert in DynamoDB bei.

  Wenn Sie ein skalares Attribut von map oder bean aktualisieren, muss die Map bereits in DynamoDB vorhanden sein. Wenn Sie dem Objekt eine Map oder eine Bean hinzufügen, die noch nicht für das Objekt in DynamoDB vorhanden ist, erhalten Sie eine `DynamoDbException` mit der Meldung *Der im Aktualisierungsausdruck angegebene Dokumentpfad ist für die Aktualisierung ungültig*. Sie müssen `MAPS_ONLY` den Modus verwenden, um DynamoDB ein Bean oder eine Map hinzuzufügen, bevor Sie eines ihrer Attribute aktualisieren.
+ `IgnoreNullsMode.MAPS_ONLY`- Verwenden Sie diese Einstellung, um Eigenschaften hinzuzufügen oder zu ersetzen, bei denen es sich um eine Bean oder Map handelt. Das SDK ersetzt oder fügt alle im Objekt bereitgestellten Maps oder Beans hinzu. Alle Beans oder Maps, die im Objekt Null sind, werden ignoriert, wobei die in DynamoDB vorhandene Map beibehalten wird.
+ `IgnoreNullsMode.DEFAULT`— Mit dieser Einstellung ignoriert das SDK niemals Nullwerte. Skalare Attribute auf jeder Ebene, die Null sind, werden auf Null aktualisiert. Das SDK aktualisiert alle Bean-, Map-, List- oder Set-Eigenschaften mit Nullwert im Objekt in DynamoDB auf Null. Wenn Sie diesen Modus verwenden — oder keinen Modus angeben, da es der Standardmodus ist — sollten Sie das Element zuerst abrufen, damit Werte in DynamoDB nicht auf Null gesetzt werden, die im Objekt zur Aktualisierung bereitgestellt werden, es sei denn, Sie beabsichtigen, die Werte auf Null zu setzen.

Wenn Sie in allen Modi ein Objekt mit einer Liste oder einem Satz angeben`updateItem`, der nicht Null ist, wird die Liste oder der Satz in DynamoDB gespeichert. 

#### Warum die Modi?
<a name="ddb-en-client-adv-features-nested-updates-nullmodes-why"></a>

Wenn Sie ein Objekt mit einem Bean oder einer Map für die `updateItem` Methode bereitstellen, kann das SDK nicht sagen, ob es die Eigenschaftswerte in der Bean (oder die Eintragswerte in der Map) verwenden soll, um das Element zu aktualisieren, oder ob das Ganze das ersetzen bean/map soll, was in DynamoDB gespeichert wurde.

Ausgehend von unserem vorherigen Beispiel, das zuerst den Abruf des Elements zeigt, versuchen wir, das `city` Attribut von `mainAddress` ohne den Abruf zu aktualisieren.

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

Die folgenden beiden Beispiele zeigen die Verwendung der Werte `MAPS_ONLY` und der `SCALAR_ONLY` Aufzählungswerte. `MAPS_ONLY`fügt eine Map hinzu und `SCALAR_ONLY` aktualisiert eine Map.

##### `IgnoreNullsMode.MAPS_ONLY`Beispiel für
<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.
    }
```

In der folgenden Tabelle können Sie nachlesen, welche Nullwerte für jeden Modus ignoriert werden. Sie können oft mit beiden `SCALAR_ONLY` arbeiten, `MAPS_ONLY` außer wenn Sie mit Beans oder Maps arbeiten.


**Welche Eigenschaften mit Nullwert in dem Objekt, an das gesendet wurde, ignoriert `updateItem` das SDK für jeden Modus?**  

| Art der Eigenschaft | im Modus SCALAR\$1ONLY | im MAPS\$1ONLY-Modus | im DEFAULT-Modus | 
| --- | --- | --- | --- | 
| Oberster Skalar | Ja | Ja | Nein | 
| Bean oder Karte | Ja | Ja | Nein | 
| Skalarwert eines Bean- oder Map-Eintrags | Ja1 | Nr.2 | Nein | 
| Liste oder Satz | Ja | Ja | Nein | 

1 Dies setzt voraus, dass die Map bereits in DynamoDB vorhanden ist. Jeder Skalarwert — Null oder nicht Null — der Bean oder Map, die Sie im Objekt für die Aktualisierung angeben, erfordert, dass ein Pfad zu dem Wert in DynamoDB existiert. Das SDK erstellt mithilfe des Dereferenzierungsoperators einen Pfad zu dem Attribut, bevor es die Anforderung sendet. `. (dot)`

2 Da Sie den `MAPS_ONLY` Modus verwenden, um eine Bean oder Map vollständig zu ersetzen oder hinzuzufügen, werden alle Nullwerte in der Bean oder Map in der in DynamoDB gespeicherten Map beibehalten.

# Leere Objekte konservieren mit `@DynamoDbPreserveEmptyObject`
<a name="ddb-en-client-adv-features-empty"></a>

Wenn Sie eine Bean mit leeren Objekten in Amazon DynamoDB speichern und möchten, dass das SDK die leeren Objekte beim Abrufen neu erstellt, kommentieren Sie den Getter der inneren Bean mit. `@DynamoDbPreserveEmptyObject`

Um zu veranschaulichen, wie die Anmerkung funktioniert, verwendet das Codebeispiel die folgenden beiden Beans.

## Beispiel Bohnen
<a name="ddb-en-client-adv-features-empty-ex1"></a>

Die folgende Datenklasse enthält zwei `InnerBean` Felder. Die Getter-Methode,`getInnerBeanWithoutAnno()`, ist nicht mit annotiert. `@DynamoDbPreserveEmptyObject` Die `getInnerBeanWithAnno()` Methode ist mit Anmerkungen versehen.

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

Instanzen der folgenden `InnerBean` Klasse sind Felder von `MyBean` und werden im Beispielcode als leere Objekte initialisiert.

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

Das folgende Codebeispiel speichert ein `MyBean` Objekt mit initialisierten inneren Beans in DynamoDB und ruft dann das Element ab. Die protokollierte Ausgabe zeigt, dass das `innerBeanWithoutAnno` nicht initialisiert wurde, sondern erstellt wurde. `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;
    }
```

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

Sie können die folgende `StaticTableSchema` Version der Tabellenschemas anstelle der Anmerkungen auf den Beans verwenden.

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

# Vermeiden Sie das Speichern von Null-Attributen verschachtelter Objekte
<a name="ddb-en-client-adv-features-ignore-null"></a>

Sie können Null-Attribute verschachtelter Objekte überspringen, wenn Sie ein Datenklassenobjekt in DynamoDB speichern, indem Sie die Anmerkung anwenden. `@DynamoDbIgnoreNulls` Im Gegensatz dazu werden Attribute der obersten Ebene mit Nullwerten niemals in der Datenbank gespeichert.

Um zu veranschaulichen, wie die Anmerkung funktioniert, verwendet das Codebeispiel die folgenden beiden Beans.

## Beispiel Bohnen
<a name="ddb-en-client-adv-features-ignore-null-ex1"></a>

Die folgende Datenklasse enthält zwei `InnerBean` Felder. Die Getter-Methode,`getInnerBeanWithoutAnno()`, ist nicht annotiert. Die `getInnerBeanWithIgnoreNullsAnno()` Methode ist mit annotiert. `@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();
    }
}
```

Instanzen der folgenden `InnerBean` Klasse sind Felder von `MyBean` und werden im folgenden Beispielcode verwendet.

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

Im folgenden Codebeispiel wird ein `InnerBean` Objekt erstellt und nur einem seiner beiden Attribute ein Wert zugewiesen. 

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

Um die Low-Level-Daten zu visualisieren, die an DynamoDB gesendet werden, protokolliert der Code die Attributzuordnung, bevor das Objekt gespeichert wird. `MyBean`

Die protokollierte Ausgabe zeigt, dass ein Attribut `innerBeanWithIgnoreNullsAnno` ausgegeben wird,

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

Die `innerBeanWithoutAnno` Instanz gibt zwei Attribute aus. Ein Attribut hat einen Wert von 200 und das andere ist ein Nullwertattribut.

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

## JSON-Darstellung der Attributzuordnung
<a name="ddb-en-client-adv-features-ignore-null-ex2"></a>

Die folgende JSON-Darstellung macht es einfacher, die in DynamoDB gespeicherten Daten zu sehen.

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

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

Sie können die folgende `StaticTableSchema` Version der Tabellenschemas anstelle von Datenklassenanmerkungen verwenden.

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