

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

# Trabalhe com DynamoDB
<a name="examples-dynamodb"></a>

O [DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html) é um serviço de banco de dados NoSQL totalmente gerenciado que proporciona um desempenho rápido e previsível com escalabilidade contínua. Esta seção mostra como trabalhar com o DynamoDB usando o AWS SDK para Java 2.x.

## Escolher o cliente do DynamoDB
<a name="ddb-clients-overview"></a>

O SDK fornece duas abordagens principais para trabalhar com o DynamoDB:

Cliente de nível inferior (`DynamoDbClient`)  
Fornece acesso direto às operações do DynamoDB com controle total sobre solicitações e respostas. Use esse cliente quando precisar de um controle refinado ou trabalhar com esquemas dinâmicos.

Cliente aprimorado (`DynamoDbEnhancedClient`)  
Oferece programação orientada a objetos com mapeamento automático entre objetos do Java e itens do DynamoDB. Também fornece recursos orientados a documentos para trabalhar com dados semelhantes a JSON que não seguem um esquema fixo. Use esse cliente ao trabalhar com modelos de dados bem definidos ou dados do tipo de documento.

## Configurar clientes do DynamoDB
<a name="ddb-configuration-setup"></a>

Antes de trabalhar com o DynamoDB, configure o cliente para obter desempenho e confiabilidade ideais.

### Noções básicas do comportamento de nova tentativa do DynamoDB
<a name="ddb-retry-behavior"></a>

Os clientes do DynamoDB usam uma contagem máxima de novas tentativas padrão de 8, que é maior que a de outros clientes de AWS service (Serviço da AWS) . Essa contagem mais alta de tentativas ajuda a lidar com a natureza distribuída e as limitações temporárias de capacidade do DynamoDB. Consulte mais informações sobre estratégias de novas tentativas em [Configure o comportamento de repetição no AWS SDK for Java 2.x](retry-strategy.md).

### Otimize o desempenho com endpoints baseados em contas
<a name="ddb-account-based-endpoints-v2"></a>

O DynamoDB [AWS oferece endpoints baseados em contas](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Programming.SDKOverview.html#Programming.SDKs.endpoints) que melhoram o desempenho usando AWS seu ID de conta para simplificar o roteamento de solicitações. 

Para usar esse recurso, você precisa da versão 2.28.4 ou posterior do AWS SDK for Java 2.x. É possível encontrar a versão mais recente no [repositório central do Maven](https://central.sonatype.com/artifact/software.amazon.awssdk/bom/versions). As versões compatíveis do SDK usam os novos endpoints automaticamente.

Para desativar o roteamento baseado em conta, escolha uma destas opções:
+ Configurar um cliente de serviço do DynamoDB com `AccountIdEndpointMode` definido como `DISABLED`.
+ Definir uma variável de ambiente.
+ Definir uma propriedade do sistema da JVM.
+ Atualize a AWS configuração do arquivo de configuração compartilhado.

O seguinte exemplo mostra como desabilitar o roteamento baseado em contas configurando um cliente de serviço do DynamoDB:

```
DynamoDbClient.builder()
                 .accountIdEndpointMode(AccountIdEndpointMode.DISABLED)
                 .build();
```

Para obter mais informações sobre as outras opções de configuração, consulte [Endpoints baseados em conta](https://docs.aws.amazon.com/sdkref/latest/guide/feature-account-endpoints.html) no Guia de referência de ferramentas AWS SDKs e ferramentas.

## O que é abordado neste tópico
<a name="ddb-topics-overview"></a>

As seguintes seções mostram como trabalhar com o DynamoDB:
+ [Trabalhar com tabelas no DynamoDB](examples-dynamodb-tables.md): criar, descrever, atualizar e excluir tabelas
+ [Trabalhe com itens em DynamoDB](examples-dynamodb-items.md): adicionar, recuperar e atualizar itens individuais
+ [Mapeie objetos Java para itens do DynamoDB com o AWS SDK for Java 2.x](dynamodb-enhanced-client.md): usar mapeamento de objetos e dados orientados a documentos com o Cliente Aprimorado

Para ver exemplos adicionais de código do DynamoDB, consulte exemplos de código do [DynamoDB na Biblioteca de exemplos de códigos.](java_dynamodb_code_examples.md) AWS 

# Trabalhar com tabelas no DynamoDB
<a name="examples-dynamodb-tables"></a>

Tabelas são os contêineres de todos os itens em um banco de dados do DynamoDB. Para adicionar ou remover dados do DynamoDB, você deve criar uma tabela.

Para cada tabela, você deve definir:
+ Um *nome* de tabela é exclusivo para a conta e a região.
+ Uma *chave primária* para a qual cada valor deve ser único; dois itens na tabela não podem ter o mesmo valor de chave primária.

  Uma chave primária pode ser *simples*, consistindo em uma única chave de partição (HASH) ou *composta*, que consiste em uma partição e uma chave de classificação (RANGE).

  Cada valor de chave tem um *tipo de dados* associado, enumerado pela classe [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ScalarAttributeType.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ScalarAttributeType.html). O valor da chave pode ser binário (B), numérico (N) ou uma string (S). Para obter mais informações, consulte [Regras de nomenclatura e tipos de dados](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html) no Guia do desenvolvedor do Amazon DynamoDB.
+  *Throughput provisionado* são valores que definem o número de unidades de capacidade de leitura/gravação reservado para a tabela.
**nota**  
 A [definição de preço do Amazon DynamoDB](https://aws.amazon.com/dynamodb/pricing/) se baseia nos valores de throughput provisionados definidos por você nas tabelas. Dessa forma, reserve somente a capacidade máxima de que você imagina precisar para a tabela.

  O throughput provisionado para uma tabela pode ser modificado a qualquer momento. Dessa forma, você poderá ajustar a capacidade conforme suas necessidades mudam.

## Criar uma tabela
<a name="dynamodb-create-table"></a>

Use o método `createTable` do `DynamoDbClient’s` para criar uma tabela do DynamoDB. Você precisa construir atributos de tabela e um esquema de tabela, ambos usados para identificar a chave primária da tabela. Você também deve fornecer valores de throughput provisionado iniciais e um nome de tabela.

**nota**  
Se uma tabela com o nome escolhido por você já existir, uma `[DynamoDbException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/DynamoDbException.html)` será lançada.

### Criar uma tabela com uma chave primária simples
<a name="dynamodb-create-table-simple"></a>

Esse código cria uma tabela com um atributo, que é a chave primária simples da tabela. O exemplo usa os objetos `[AttributeDefinition](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/AttributeDefinition.html)` e `[KeySchemaElement](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/KeySchemaElement.html)` para [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/CreateTableRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/CreateTableRequest.html).

 **Importações** 

```
import software.amazon.awssdk.core.waiters.WaiterResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
import software.amazon.awssdk.services.dynamodb.model.KeyType;
import software.amazon.awssdk.services.dynamodb.model.CreateTableResponse;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.waiters.DynamoDbWaiter;
```

 **Código da** 

```
    public static String createTable(DynamoDbClient ddb, String tableName, String key) {

        DynamoDbWaiter dbWaiter = ddb.waiter();
        CreateTableRequest request = CreateTableRequest.builder()
                .attributeDefinitions(AttributeDefinition.builder()
                        .attributeName(key)
                        .attributeType(ScalarAttributeType.S)
                        .build())
                .keySchema(KeySchemaElement.builder()
                        .attributeName(key)
                        .keyType(KeyType.HASH)
                        .build())
                .provisionedThroughput(ProvisionedThroughput.builder()
                        .readCapacityUnits(new Long(10))
                        .writeCapacityUnits(new Long(10))
                        .build())
                .tableName(tableName)
                .build();

        String newTable ="";
        try {
            CreateTableResponse response = ddb.createTable(request);
            DescribeTableRequest tableRequest = DescribeTableRequest.builder()
                    .tableName(tableName)
                    .build();

            // Wait until the Amazon DynamoDB table is created
            WaiterResponse<DescribeTableResponse> waiterResponse =  dbWaiter.waitUntilTableExists(tableRequest);
            waiterResponse.matched().response().ifPresent(System.out::println);

            newTable = response.tableDescription().tableName();
            return newTable;

        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
       return "";
    }
```

Veja o [exemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/CreateTable.java) no GitHub.

### Criar uma tabela com uma chave primária composta
<a name="dynamodb-create-table-composite"></a>

O exemplo a seguir cria uma tabela com dois atributos. Os dois atributos são usados para a chave primária composta.

 **Importações** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest;
import software.amazon.awssdk.services.dynamodb.model.CreateTableResponse;
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
import software.amazon.awssdk.services.dynamodb.model.KeyType;
import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
import software.amazon.awssdk.services.dynamodb.model.ScalarAttributeType;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
```

 **Código da** 

```
    public static String createTableComKey(DynamoDbClient ddb, String tableName) {
        CreateTableRequest request = CreateTableRequest.builder()
                .attributeDefinitions(
                        AttributeDefinition.builder()
                                .attributeName("Language")
                                .attributeType(ScalarAttributeType.S)
                                .build(),
                        AttributeDefinition.builder()
                                .attributeName("Greeting")
                                .attributeType(ScalarAttributeType.S)
                                .build())
                .keySchema(
                        KeySchemaElement.builder()
                                .attributeName("Language")
                                .keyType(KeyType.HASH)
                                .build(),
                        KeySchemaElement.builder()
                                .attributeName("Greeting")
                                .keyType(KeyType.RANGE)
                                .build())
                .provisionedThroughput(
                        ProvisionedThroughput.builder()
                                .readCapacityUnits(new Long(10))
                                .writeCapacityUnits(new Long(10)).build())
                .tableName(tableName)
                .build();

       String tableId = "";

       try {
            CreateTableResponse result = ddb.createTable(request);
            tableId = result.tableDescription().tableId();
            return tableId;
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
       return "";
    }
```

Veja o [exemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/CreateTableCompositeKey.java) no GitHub.

## Listar tabelas
<a name="dynamodb-list-tables"></a>

É possível listar as tabelas em determinada região chamando o método `listTables` do `DynamoDbClient’s`.

**nota**  
Se a tabela nomeada não existir para a conta e a região, uma [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html) será lançada.

 **Importações** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse;
import software.amazon.awssdk.services.dynamodb.model.ListTablesRequest;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import java.util.List;
```

 **Código da** 

```
    public static void listAllTables(DynamoDbClient ddb){

        boolean moreTables = true;
        String lastName = null;

        while(moreTables) {
            try {
                ListTablesResponse response = null;
                if (lastName == null) {
                    ListTablesRequest request = ListTablesRequest.builder().build();
                    response = ddb.listTables(request);
                } else {
                    ListTablesRequest request = ListTablesRequest.builder()
                            .exclusiveStartTableName(lastName).build();
                    response = ddb.listTables(request);
                }

                List<String> tableNames = response.tableNames();

                if (tableNames.size() > 0) {
                    for (String curName : tableNames) {
                        System.out.format("* %s\n", curName);
                    }
                } else {
                    System.out.println("No tables found!");
                    System.exit(0);
                }

                lastName = response.lastEvaluatedTableName();
                if (lastName == null) {
                    moreTables = false;
                }
            } catch (DynamoDbException e) {
                System.err.println(e.getMessage());
                System.exit(1);
            }
        }
        System.out.println("\nDone!");
    }
```

Por padrão, até 100 tabelas são retornadas por chamada. Use `lastEvaluatedTableName` no objeto [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ListTablesResponse.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ListTablesResponse.html) retornado para obter a tabela mais recentemente avaliada. Você pode usar esse valor para iniciar a listagem depois do último valor retornado da listagem anterior.

Veja o [exemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/ListTables.java) no GitHub.

## Descrever (obter informações sobre) uma tabela
<a name="dynamodb-describe-table"></a>

Use o método `DynamoDbClient’s` `describeTable` para obter informações sobre uma tabela.

**nota**  
Se a tabela nomeada não existir para a conta e a região, uma [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html) será lançada.

 **Importações** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest;
import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughputDescription;
import software.amazon.awssdk.services.dynamodb.model.TableDescription;
import java.util.List;
```

 **Código da** 

```
    public static void describeDymamoDBTable(DynamoDbClient ddb,String tableName ) {

        DescribeTableRequest request = DescribeTableRequest.builder()
                .tableName(tableName)
                .build();

        try {
            TableDescription tableInfo =
                    ddb.describeTable(request).table();

            if (tableInfo != null) {
                System.out.format("Table name  : %s\n",
                        tableInfo.tableName());
                System.out.format("Table ARN   : %s\n",
                        tableInfo.tableArn());
                System.out.format("Status      : %s\n",
                        tableInfo.tableStatus());
                System.out.format("Item count  : %d\n",
                        tableInfo.itemCount().longValue());
                System.out.format("Size (bytes): %d\n",
                        tableInfo.tableSizeBytes().longValue());

                ProvisionedThroughputDescription throughputInfo =
                        tableInfo.provisionedThroughput();
                System.out.println("Throughput");
                System.out.format("  Read Capacity : %d\n",
                        throughputInfo.readCapacityUnits().longValue());
                System.out.format("  Write Capacity: %d\n",
                        throughputInfo.writeCapacityUnits().longValue());

                List<AttributeDefinition> attributes =
                        tableInfo.attributeDefinitions();
                System.out.println("Attributes");

                for (AttributeDefinition a : attributes) {
                    System.out.format("  %s (%s)\n",
                            a.attributeName(), a.attributeType());
                }
            }
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
        System.out.println("\nDone!");
    }
```

Veja o [exemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/DescribeTable.java) no GitHub.

## Modificar (atualizar) uma tabela
<a name="dynamodb-update-table"></a>

Você pode modificar os valores de throughput provisionada da tabela a qualquer momento chamando o método `DynamoDbClient’s` `updateTable`.

**nota**  
Se a tabela nomeada não existir para a conta e a região, uma [ResourceNotFoundException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html) será lançada.

 **Importações** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.ProvisionedThroughput;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.UpdateTableRequest;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
```

 **Código da** 

```
    public static void updateDynamoDBTable(DynamoDbClient ddb,
                                           String tableName,
                                           Long readCapacity,
                                           Long writeCapacity) {

        System.out.format(
                "Updating %s with new provisioned throughput values\n",
                tableName);
        System.out.format("Read capacity : %d\n", readCapacity);
        System.out.format("Write capacity : %d\n", writeCapacity);

        ProvisionedThroughput tableThroughput = ProvisionedThroughput.builder()
                .readCapacityUnits(readCapacity)
                .writeCapacityUnits(writeCapacity)
                .build();

        UpdateTableRequest request = UpdateTableRequest.builder()
                .provisionedThroughput(tableThroughput)
                .tableName(tableName)
                .build();

        try {
            ddb.updateTable(request);
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }

        System.out.println("Done!");
    }
```

Veja o [exemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/UpdateTable.java) no GitHub.

## Excluir uma tabela
<a name="dynamodb-delete-table"></a>

Para excluir uma tabela, chame o método `DynamoDbClient’s` `deleteTable` e forneça o nome da tabela.

**nota**  
Se a tabela nomeada não existir para a conta e a região, uma [ResourceNotFoundException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html) será lançada.

 **Importações** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.DeleteTableRequest;
```

 **Código da** 

```
    public static void deleteDynamoDBTable(DynamoDbClient ddb, String tableName) {

        DeleteTableRequest request = DeleteTableRequest.builder()
                .tableName(tableName)
                .build();

        try {
            ddb.deleteTable(request);

        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
        System.out.println(tableName +" was successfully deleted!");
    }
```

Veja o [exemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/DeleteTable.java) no GitHub.

## Mais informações
<a name="more-information"></a>
+  [Diretrizes para trabalhar com tabelas](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GuidelinesForTables.html) no Guia do desenvolvedor do Amazon DynamoDB
+  [Trabalhar com tabelas no DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithTables.html) no Guia do Desenvolvedor do Amazon DynamoDB

# Trabalhe com itens em DynamoDB
<a name="examples-dynamodb-items"></a>

Em DynamoDB, um item é uma coleção de *atributos*, cada um com um *nome* e um *valor*. Um valor de atributo pode ser uma escalar, um conjunto ou um tipo de documento. Para obter mais informações, consulte [Regras de nomenclatura e tipos de dados](https://docs.aws.amazon.com//amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html) no Guia do desenvolvedor do Amazon DynamoDB .

## Recuperar (obter) um item de uma tabela
<a name="dynamodb-get-item"></a>

Chame o `getItem` método DynamoDbClient's e passe a ele um [GetItemRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/GetItemRequest.html)objeto com o nome da tabela e o valor da chave primária do item que você deseja. Ele retorna um [GetItemResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/GetItemResponse.html)objeto com todos os atributos desse item. Você pode especificar uma ou mais [expressões de projeção](https://docs.aws.amazon.com//amazondynamodb/latest/developerguide/Expressions.ProjectionExpressions.html) no `GetItemRequest` para recuperar atributos específicos.

Você pode usar o `item()` método do `GetItemResponse` objeto retornado para recuperar um [mapa](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/Map.html) dos pares de chave (String [AttributeValue](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/AttributeValue.html)) e valor () associados ao item.

 **Importações** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.GetItemRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
```

 **Código** 

```
    public static void getDynamoDBItem(DynamoDbClient ddb,String tableName,String key,String keyVal ) {

        HashMap<String,AttributeValue> keyToGet = new HashMap<String,AttributeValue>();

        keyToGet.put(key, AttributeValue.builder()
                .s(keyVal).build());

        GetItemRequest request = GetItemRequest.builder()
                .key(keyToGet)
                .tableName(tableName)
                .build();

        try {
            Map<String,AttributeValue> returnedItem = ddb.getItem(request).item();

            if (returnedItem != null) {
                Set<String> keys = returnedItem.keySet();
                System.out.println("Amazon DynamoDB table attributes: \n");

                for (String key1 : keys) {
                    System.out.format("%s: %s\n", key1, returnedItem.get(key1).toString());
                }
            } else {
                System.out.format("No item found with the key %s!\n", key);
            }
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
    }
```

Veja o [exemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/bc964a243276990f05c180618ea8b34777c68f0e/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/GetItem.java) em GitHub.

## Recuperar (obter) um item de uma tabela usando o cliente assíncrono
<a name="id1ddb"></a>

Invoque o `getItem` método do DynamoDbAsyncClient e passe a ele um [GetItemRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/GetItemRequest.html)objeto com o nome da tabela e o valor da chave primária do item desejado.

Você pode retornar uma instância de [Collection](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/Collection.html) com todos os atributos desse item (consulte o exemplo a seguir).

 **Importações** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.GetItemRequest;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
```

 **Código** 

```
    public static void getItem(DynamoDbAsyncClient client, String tableName, String key,  String keyVal) {

        HashMap<String, AttributeValue> keyToGet =
                new HashMap<String, AttributeValue>();

        keyToGet.put(key, AttributeValue.builder()
                .s(keyVal).build());

        try {

            // Create a GetItemRequest instance
            GetItemRequest request = GetItemRequest.builder()
                    .key(keyToGet)
                    .tableName(tableName)
                    .build();

            // Invoke the DynamoDbAsyncClient object's getItem
            java.util.Collection<AttributeValue> returnedItem = client.getItem(request).join().item().values();

            // Convert Set to Map
            Map<String, AttributeValue> map = returnedItem.stream().collect(Collectors.toMap(AttributeValue::s, s->s));
            Set<String> keys = map.keySet();
            for (String sinKey : keys) {
                System.out.format("%s: %s\n", sinKey, map.get(sinKey).toString());
            }

        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
```

Veja o [exemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/bc964a243276990f05c180618ea8b34777c68f0e/javav2/example_code/dynamodbasync/src/main/java/com/example/dynamodbasync/DynamoDBAsyncGetItem.java) em GitHub.

## Adicionar um novo item a uma tabela
<a name="dynamodb-add-item"></a>

Crie um [Mapa](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/Map.html) de pares de chave/valor que representem os atributos do item. Eles devem incluir valores para os campos de chave primária da tabela. Se o item identificado pela chave primária já existir, os campos serão *atualizados* pela requisição.

**nota**  
Se a tabela nomeada não existir para sua conta e região, um [ResourceNotFoundException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html)será lançado.

 **Importações** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.PutItemRequest;
import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException;
import java.util.HashMap;
```

 **Código** 

```
    public static void putItemInTable(DynamoDbClient ddb,
                                      String tableName,
                                      String key,
                                      String keyVal,
                                      String albumTitle,
                                      String albumTitleValue,
                                      String awards,
                                      String awardVal,
                                      String songTitle,
                                      String songTitleVal){

        HashMap<String,AttributeValue> itemValues = new HashMap<String,AttributeValue>();

        // Add all content to the table
        itemValues.put(key, AttributeValue.builder().s(keyVal).build());
        itemValues.put(songTitle, AttributeValue.builder().s(songTitleVal).build());
        itemValues.put(albumTitle, AttributeValue.builder().s(albumTitleValue).build());
        itemValues.put(awards, AttributeValue.builder().s(awardVal).build());

        PutItemRequest request = PutItemRequest.builder()
                .tableName(tableName)
                .item(itemValues)
                .build();

        try {
            ddb.putItem(request);
            System.out.println(tableName +" was successfully updated");

        } catch (ResourceNotFoundException e) {
            System.err.format("Error: The Amazon DynamoDB table \"%s\" can't be found.\n", tableName);
            System.err.println("Be sure that it exists and that you've typed its name correctly!");
            System.exit(1);
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
    }
```

Veja o [exemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/PutItem.java) em GitHub.

## Atualizar um item existente em uma tabela
<a name="dynamodb-update-item"></a>

Você pode atualizar um atributo para um item já existente em uma tabela usando o método `updateItem` do DynamoDbClient, fornecendo um nome de tabela, o valor da chave primária e um mapa de campos a ser atualizado.

**nota**  
Se a tabela nomeada não existir para sua conta e região, ou se o item identificado pela chave primária que você inseriu não existir, um [ResourceNotFoundException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html)será lançado.

 **Importações** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import software.amazon.awssdk.services.dynamodb.model.AttributeAction;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.AttributeValueUpdate;
import software.amazon.awssdk.services.dynamodb.model.ResourceNotFoundException;
import software.amazon.awssdk.services.dynamodb.model.UpdateItemRequest;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import java.util.HashMap;
```

 **Código** 

```
    public static void updateTableItem(DynamoDbClient ddb,
                                       String tableName,
                                       String key,
                                       String keyVal,
                                       String name,
                                       String updateVal){

        HashMap<String,AttributeValue> itemKey = new HashMap<String,AttributeValue>();

        itemKey.put(key, AttributeValue.builder().s(keyVal).build());

        HashMap<String,AttributeValueUpdate> updatedValues =
                new HashMap<String,AttributeValueUpdate>();

        // Update the column specified by name with updatedVal
        updatedValues.put(name, AttributeValueUpdate.builder()
                .value(AttributeValue.builder().s(updateVal).build())
                .action(AttributeAction.PUT)
                .build());

        UpdateItemRequest request = UpdateItemRequest.builder()
                .tableName(tableName)
                .key(itemKey)
                .attributeUpdates(updatedValues)
                .build();

        try {
            ddb.updateItem(request);
        } catch (ResourceNotFoundException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }

        System.out.println("Done!");
    }
```

Veja o [exemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/UpdateItem.java) em GitHub.

## Excluir um item existente em uma tabela
<a name="dynamodb-delete-item"></a>

Você pode excluir um item que existe em uma tabela usando o `deleteItem` método DynamoDbClient's e fornecendo um nome de tabela, bem como o valor da chave primária.

**nota**  
Se a tabela nomeada não existir para sua conta e região, ou se o item identificado pela chave primária que você inseriu não existir, um [ResourceNotFoundException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html)será lançado.

 **Importações** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
import java.util.HashMap;
```

 **Código** 

```
  public static void deleteDynamoDBItem(DynamoDbClient ddb, String tableName, String key, String keyVal) {

        HashMap<String,AttributeValue> keyToGet =
                new HashMap<String,AttributeValue>();

        keyToGet.put(key, AttributeValue.builder()
                .s(keyVal)
                .build());

        DeleteItemRequest deleteReq = DeleteItemRequest.builder()
                .tableName(tableName)
                .key(keyToGet)
                .build();

        try {
            ddb.deleteItem(deleteReq);
        } catch (DynamoDbException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
    }
```

Veja o [exemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/DeleteItem.java) em GitHub.

## Mais informações
<a name="more-information"></a>
+  [Diretrizes para trabalhar com itens](https://docs.aws.amazon.com//amazondynamodb/latest/developerguide/best-practices.html) no Guia do desenvolvedor do Amazon DynamoDB 
+  [Trabalhando com itens DynamoDB no](https://docs.aws.amazon.com//amazondynamodb/latest/developerguide/WorkingWithItems.html) Guia do Amazon DynamoDB desenvolvedor

# Mapeie objetos Java para itens do DynamoDB com o AWS SDK for Java 2.x
<a name="dynamodb-enhanced-client"></a>

A [API do Cliente Aprimorado do DynamoDB](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/package-summary.html) é uma biblioteca de alto nível que é a sucessora da classe `DynamoDBMapper` no SDK para Java v1.x. Ela oferece uma maneira simples de mapear classes do lado do cliente para tabelas do DynamoDB. Basta definir as relações entre as tabelas e suas classes de dados correspondentes no seu código. Depois que você definir essas relações, poderá executar intuitivamente várias operações de criação, leitura, atualização ou exclusão (CRUD) em tabelas ou itens do DynamoDB.

A API do Cliente Aprimorado do DynamoDB também inclui a [API de Documento Aprimorado](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/package-summary.html), que permite trabalhar com itens do tipo documento que não seguem um esquema definido. 

**Topics**
+ [Conceitos básicos da API do Cliente Aprimorado do DynamoDB](ddb-en-client-getting-started.md)
+ [Saiba mais sobre os fundamentos da API do cliente avançado do DynamoDB](ddb-en-client-use.md)
+ [Usar recursos avançados de mapeamento](ddb-en-client-adv-features.md)
+ [Trabalhar com documentos JSON com a API de documentos aprimorados do DynamoDB](ddb-en-client-doc-api.md)
+ [Usar extensões para personalizar as operações do Cliente Aprimorado do DynamoDB](ddb-en-client-extensions.md)
+ [Usar a API do cliente avançado do DynamoDB de forma assíncrona](ddb-en-client-async.md)
+ [Anotações de classes de dados](ddb-en-client-anno-index.md)

# Conceitos básicos da API do Cliente Aprimorado do DynamoDB
<a name="ddb-en-client-getting-started"></a>

O tutorial a seguir apresenta os fundamentos necessários para trabalhar com a API do Cliente Aprimorado do DynamoDB.

## Adicionar dependências
<a name="ddb-en-client-gs-dep"></a>

Para começar a trabalhar com a API do Cliente Aprimorado do DynamoDB em seu projeto, adicione uma dependência no artefato `dynamodb-enhanced` do Maven. Essa ação será mostrada nos exemplos a seguir. 

------
#### [ Maven ]

```
<project>
  <dependencyManagement>
   <dependencies>
      <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>bom</artifactId>
        <version><VERSION></version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
   </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>software.amazon.awssdk</groupId>
      <artifactId>dynamodb-enhanced</artifactId>
    </dependency>
  </dependencies>
  ...
</project>
```

Faça uma pesquisa no repositório central do Maven para obter a [versão mais recente](https://central.sonatype.com/artifact/software.amazon.awssdk/bom) e *<VERSION>* substitua por esse valor.

------
#### [ Gradle ]

```
repositories {
    mavenCentral()
}
dependencies {
    implementation(platform("software.amazon.awssdk:bom:<VERSION>"))
    implementation("software.amazon.awssdk:dynamodb-enhanced")
    ...
}
```

Faça uma pesquisa no repositório central do Maven para obter a [versão mais recente](https://central.sonatype.com/artifact/software.amazon.awssdk/bom) e *<VERSION>* substitua por esse valor.

------

# Gerar um `TableSchema` com base em uma classe de dados
<a name="ddb-en-client-gs-tableschema"></a>

O `[TableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/TableSchema.html)` permite que o cliente avançado mapeie valores de atributos do DynamoDB de e para suas classes do lado do cliente. Neste tutorial, você aprenderá sobre `TableSchema`s derivados de uma classe de dados estática e gerados a partir de código usando um construtor.

## Usar uma classe de dados anotada
<a name="ddb-en-client-gs-tableschema-anno-bean"></a>

O SDK para Java 2.x inclui um [conjunto de anotações](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/annotations/package-summary.html) que você pode usar com uma classe de dados para gerar um `TableSchema` de modo rápido para mapear suas classes usando tabelas.

Comece criando uma classe de dados que esteja em conformidade com a [JavaBean especificação](https://download.oracle.com/otn-pub/jcp/7224-javabeans-1.01-fr-spec-oth-JSpec/beans.101.pdf). A especificação exige que uma classe tenha um construtor público sem argumentos e que tenha getters e setters para cada atributo na classe. Inclua uma anotação de classe para indicar que a classe de dados é uma `DynamoDbBean`. Além disso, no mínimo, inclua uma anotação `DynamoDbPartitionKey` no getter ou setter do atributo da chave primária. 

É possível aplicar [anotações no nível de atributo](ddb-en-client-anno-index.md) a getters ou setters, mas não a ambos.

**nota**  
O termo normalmente `property` é usado para um valor encapsulado em um. JavaBean No entanto, no lugar desse termo, este guia usa o termo `attribute`, para ser consistente com a terminologia usada pelo DynamoDB.

A classe `Customer` a seguir mostra as anotações que vinculam a definição da classe à tabela do DynamoDB.

### Classe `Customer`
<a name="ddb-en-client-gs-tableschema-anno-bean-cust"></a>

```
package org.example.tests.model;

import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;

import java.time.Instant;

@DynamoDbBean
public class Customer {

    private String id;
    private String name;
    private String email;
    private Instant regDate;

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

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

    public String getCustName() { return this.name; }

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

    @DynamoDbSortKey
    public String getEmail() { return this.email; }

    public void setEmail(String email) { this.email = email; }

    public Instant getRegistrationDate() { return this.regDate; }

    public void setRegistrationDate(Instant registrationDate) { this.regDate = registrationDate; }

    @Override
    public String toString() {
        return "Customer [id=" + id + ", name=" + name + ", email=" + email
                + ", regDate=" + regDate + "]";
    }
}
```

Depois de criar uma classe de dados anotada, use-a para criar o `TableSchema`, como será mostrado no trecho a seguir.

```
static final TableSchema<Customer> customerTableSchema = TableSchema.fromBean(Customer.class);
```

Um `TableSchema` é projetado para ser estático e imutável. Normalmente, você pode instanciá-lo no momento do carregamento da classe.

O método de fábrica estático `TableSchema.fromBean()` inspeciona o bean para gerar o mapeamento entre os atributos da classe de dados (propriedades) e os atributos do DynamoDB.

Para ver um exemplo de como trabalhar com um modelo de dados composto por várias classes de dados, consulte a classe `Person` na seção [Trabalhar com atributos que são beans, mapas, listas e conjuntos](ddb-en-client-adv-features-nested.md).

## Usar um construtor
<a name="ddb-en-client-gs-tableschema-builder"></a>

Você pode ignorar o custo da introspecção do bean se definir o esquema da tabela no código. Se você codificar o esquema, sua classe não precisará seguir os padrões de JavaBean nomenclatura nem precisará ser anotada. O exemplo a seguir usa um construtor e é equivalente ao exemplo de classe `Customer` que usa anotações.

```
static final TableSchema<Customer> customerTableSchema =
                TableSchema.builder(Customer.class)
                        .newItemSupplier(Customer::new)
                        .addAttribute(String.class, a -> a.name("id")
                                .getter(Customer::getId)
                                .setter(Customer::setId)
                                .tags(StaticAttributeTags.primaryPartitionKey()))
                        .addAttribute(String.class, a -> a.name("email")
                                .getter(Customer::getEmail)
                                .setter(Customer::setEmail)
                                .tags(StaticAttributeTags.primarySortKey()))
                        .addAttribute(String.class, a -> a.name("name")
                                .getter(Customer::getCustName)
                                .setter(Customer::setCustName))
                        .addAttribute(Instant.class, a -> a.name("registrationDate")
                                .getter(Customer::getRegistrationDate)
                                .setter(Customer::setRegistrationDate))
                        .build();
```

# Criar um cliente aprimorado e uma `DynamoDbTable`
<a name="ddb-en-client-getting-started-dynamodbTable"></a>

## Criar um cliente aprimorado
<a name="ddb-en-client-getting-started-dynamodbTable-eclient"></a>

A [DynamoDbEnhancedClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html)classe, ou sua contraparte assíncrona, [DynamoDbEnhancedAsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedAsyncClient.html), é o ponto de entrada para trabalhar com a API do DynamoDB Enhanced Client.

O cliente aprimorado exige um `[DynamoDbClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html)` padrão para realizar operações. A API oferece duas maneiras de criar uma instância `DynamoDbEnhancedClient`. A primeira opção, mostrada no trecho a seguir, cria um `DynamoDbClient` padrão com configurações padrão retiradas das definições de configuração.

```
DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.create();
```

Se quiser configurar o cliente padrão subjacente, você poderá fornecê-lo ao método construtor do cliente aprimorado, conforme mostrado no trecho a seguir.

```
// Configure an instance of the standard DynamoDbClient.
DynamoDbClient standardClient = DynamoDbClient.builder()
    .region(Region.US_EAST_1)
    .credentialsProvider(ProfileCredentialsProvider.create())
    .build();

// Use the configured standard client with the enhanced client.
DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()
    .dynamoDbClient(standardClient)
    .build();
```

## Criar uma instância `DynamoDbTable`
<a name="ddb-en-client-getting-started-dynamodbTable-table"></a>

Pense em uma [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html) como a representação do lado do cliente de uma tabela do DynamoDB que usa a funcionalidade de mapeamento fornecida por um `TableSchema`. A classe `DynamoDbTable` fornece métodos para operações CRUD que permitem que você interaja com uma única tabela do DynamoDB.

`DynamoDbTable<T>` é uma classe genérica que usa um argumento de tipo único, seja uma classe personalizada ou `EnhancedDocument`, ao trabalhar com itens do tipo documento. Esse tipo de argumento estabelece a relação entre a classe que você usa e a tabela única do DynamoDB.

Use o método de fábrica `table()` do `DynamoDbEnhancedClient` para criar uma instância `DynamoDbTable` conforme será mostrado no trecho a seguir.

```
static final DynamoDbTable<Customer> customerTable = 
        enhancedClient.table("Customer", TableSchema.fromBean(Customer.class));
```

Instâncias `DynamoDbTable` são candidatas a singletons porque são imutáveis e podem ser usadas em toda a sua aplicação.

O código agora tem uma representação na memória de uma tabela do DynamoDB que pode funcionar com instâncias `Customer`. A tabela do DynamoDB pode ou não existir. Se a tabela chamada `Customer` já existir, você poderá começar a realizar operações CRUD nela. Se ela não existir, use a instância `DynamoDbTable` para criar a tabela conforme será discutido na próxima seção.

# Criar uma tabela do DynamoDB se for necessário
<a name="ddb-en-client-gs-ddbtable"></a>

Depois de criar uma instância `DynamoDbTable`, use-a para realizar uma criação *única* de uma tabela no DynamoDB.

## Exemplo de código para criar tabela
<a name="ddb-en-client-gs-ddbtable-createex"></a>

O exemplo a seguir cria uma tabela do DynamoDB com base na classe de dados `Customer`. 

Este exemplo cria uma tabela do DynamoDB com o nome `Customer`, que é idêntico ao nome da classe, mas o nome da tabela pode ser diferente. Seja qual for o nome da tabela, você deverá usar esse nome em aplicativos adicionais para trabalhar com a tabela. Forneça esse nome ao método `table()` sempre que criar outro objeto de `DynamoDbTable` para trabalhar com a tabela subjacente do DynamoDB.

O parâmetro Java lambda, `builder`, passado para o método `createTable`, permite que você [personalize a tabela](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/CreateTableEnhancedRequest.Builder.html). Neste exemplo, o [throughput provisionado](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.html#HowItWorks.ProvisionedThroughput.Manual) está configurado. Se você quiser usar as configurações padrão ao criar uma tabela, ignore o construtor, conforme mostrado no trecho a seguir.

```
customerTable.createTable();
```

Quando as configurações padrão são usadas, os valores para o throughput provisionado não são definidos. Em vez disso, o modo de faturamento da tabela é definido como [sob demanda](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.html#HowItWorks.OnDemand).

O exemplo também usa um `[DynamoDbWaiter](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/waiters/DynamoDbWaiter.html)` antes de tentar imprimir o nome da tabela recebida na resposta. A criação de uma tabela leva algum tempo. Portanto, usar um waiter significa que você não precisa escrever uma lógica que pesquise no DynamoDB para ver se a tabela existe antes de usá-la.

### Importações
<a name="ddb-en-client-gs-ddbtable-imports"></a>

```
import com.example.dynamodb.Customer;
import software.amazon.awssdk.core.internal.waiters.ResponseOrException;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.model.CreateTableEnhancedRequest;
import software.amazon.awssdk.services.dynamodb.model.DescribeTableResponse;
import software.amazon.awssdk.services.dynamodb.waiters.DynamoDbWaiter;
```

### Código
<a name="ddb-en-client-gs-ddbtable-code"></a>

```
 public static void createCustomerTable(DynamoDbTable<Customer> customerTable, DynamoDbClient standardClient) {
     // Create the DynamoDB table using the 'customerTable' DynamoDbTable instance.
     customerTable.createTable(builder -> builder
             .provisionedThroughput(b -> b
                     .readCapacityUnits(10L)
                     .writeCapacityUnits(10L)
                     .build())
     );
     // The DynamoDbClient instance (named 'standardClient') passed to the builder for the DynamoDbWaiter is the same instance
     // that was passed to the builder of the DynamoDbEnhancedClient instance that we created previously.
     // By using the same instance, it ensures that the same Region that was configured on the standard DynamoDbClient 
     // instance is used for other service clients that accept a DynamoDbClient during construction.
     try (DynamoDbWaiter waiter = DynamoDbWaiter.builder().client(standardClient).build()) { // DynamoDbWaiter is Autocloseable
         ResponseOrException<DescribeTableResponse> response = waiter
                 .waitUntilTableExists(builder -> builder.tableName("Customer").build())
                 .matched();
         DescribeTableResponse tableDescription = response.response().orElseThrow(
                 () -> new RuntimeException("Customer table was not created."));
         // The actual error can be inspected in response.exception()
         logger.info("Customer table was created.");
     }
 }
```

**nota**  
Os nomes dos atributos de uma tabela do DynamoDB começam com uma letra minúscula quando a tabela é gerada a partir de uma classe de dados. Se você quiser que o nome do atributo da tabela comece com uma letra maiúscula, use a [anotação `@DynamoDbAttribute(NAME)`](ddb-en-client-adv-features-inex-attr.md) e forneça o nome desejado como parâmetro.

# Executar operações
<a name="ddb-en-client-gs-use"></a>

Depois que a tabela for criada, use a instância `DynamoDbTable` para realizar operações na tabela do DynamoDB. 

No exemplo a seguir, um singleton `DynamoDbTable<Customer>` é passado como parâmetro junto com uma instância de [classe de dados `Customer`](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust) para adicionar um novo item à tabela.

```
    public static void putItemExample(DynamoDbTable<Customer> customerTable, Customer customer){
        logger.info(customer.toString());
        customerTable.putItem(customer);
    }
```

## Objeto `Customer`
<a name="perform_ops_create_customer_instatnce"></a>

```
        Customer customer = new Customer();
        customer.setId("1");
        customer.setCustName("Customer Name");
        customer.setEmail("customer@example.com");
        customer.setRegistrationDate(Instant.parse("2023-07-03T10:15:30.00Z"));
```

Antes de enviar o objeto `customer` para o DynamoDB, registre a saída do método `toString()` do objeto para compará-la com o que é enviado pelo cliente aprimorado.

```
Customer [id=1, name=Customer Name, email=customer@example.com, regDate=2023-07-03T10:15:30Z]
```

O registro em log em nível de conexão mostra a carga útil da solicitação gerada. O cliente aprimorado gerou a representação de nível baixo da classe de dados. O atributo `regDate`, que é um tipo `Instant` em Java, é representado como uma cadeia de caracteres do DynamoDB.

```
{
  "TableName": "Customer",
  "Item": {
    "registrationDate": {
      "S": "2023-07-03T10:15:30Z"
    },
    "id": {
      "S": "1"
    },
    "custName": {
      "S": "Customer Name"
    },
    "email": {
      "S": "customer@example.com"
    }
  }
}
```

# Trabalhar com uma tabela existente
<a name="ddb-en-client-gs-existingtable"></a>

A seção anterior mostrou como criar uma tabela do DynamoDB começando com uma classe de dados Java. Se você já tiver uma tabela existente e quiser usar os atributos do cliente aprimorado, poderá criar uma classe de dados Java para trabalhar com a tabela. Você precisa examinar a tabela do DynamoDB e adicionar as anotações necessárias à classe de dados. 

Antes de trabalhar com uma tabela existente, chame o método `DynamoDbEnhanced.table()`. Isso foi feito no exemplo anterior com a seguinte instrução:

```
DynamoDbTable<Customer> customerTable = enhancedClient.table("Customer", TableSchema.fromBean(Customer.class));
```

Depois que a instância `DynamoDbTable` for retornada, você poderá começar a trabalhar imediatamente com a tabela subjacente. Você não precisa recriar a tabela chamando o método `DynamoDbTable.createTable()`.

O exemplo a seguir demonstra isso recuperando imediatamente uma instância `Customer` da tabela do DynamoDB.

```
DynamoDbTable<Customer> customerTable = enhancedClient.table("Customer", TableSchema.fromBean(Customer.class));
// The Customer table exists already and has an item with a primary key value of "1" and a sort key value of "customer@example.com".
customerTable.getItem(
        Key.builder().
                partitionValue("1").
                sortValue("customer@example.com").build());
```

**Importante**  
O nome da tabela usado no método `table()` deve corresponder ao nome da tabela existente do DynamoDB.

# Saiba mais sobre os fundamentos da API do cliente avançado do DynamoDB
<a name="ddb-en-client-use"></a>

Este tópico discute os atributos básicos da API do Cliente Aprimorado do DynamoDB e a compara com a [API do cliente padrão do DynamoDB](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/package-summary.html).

Se você não conhece a API do Cliente Aprimorado do DynamoDB, recomendamos que leia o [tutorial introdutório](ddb-en-client-getting-started.md) para se familiarizar com as classes fundamentais.

## Itens do DynamoDB em Java
<a name="ddb-en-client-use-usecase"></a>

As tabelas do DynamoDB armazenam itens. Dependendo do seu caso de uso, os itens no lado Java podem assumir a forma de dados estruturados por estatísticas ou estruturas criadas de modo dinâmico. 

Se seu caso de uso exigir itens com um conjunto consistente de atributos, use [classes anotadas](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean) ou use um [construtor](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-builder) para gerar a tipagem estática apropriada `TableSchema`. 

Como alternativa, se você precisar armazenar itens que consistem em estruturas variadas, crie um `DocumentTableSchema`. `DocumentTableSchema` faz parte da [API de Documento Aprimorado](ddb-en-client-doc-api.md) e requer somente uma chave primária digitada estaticamente e funciona com instâncias `EnhancedDocument` para armazenar os elementos de dados. A API de Documento Aprimorado é abordada em outro [tópico.](ddb-en-client-doc-api.md)

## Tipos de atributos para classes de modelo de dados
<a name="ddb-en-client-use-types"></a>

Embora o DynamoDB ofereça suporte a [um pequeno número de tipos de atributos](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes) em comparação com o sistema de tipos avançados do Java, a API do Cliente Aprimorado do DynamoDB fornece mecanismos para converter membros de uma classe Java de e para tipos de atributos do DynamoDB.

Os tipos de atributos (propriedades) de suas classes de dados do Java devem ser tipos de objetos, não primitivos. Por exemplo, sempre use tipos de dados de objetos `Long` e `Integer`, não primitivos `long` e `int`.

[Por padrão, a API do DynamoDB Enhanced Client suporta conversores de atributos para um grande número de tipos, [como Integer[,](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html) String](https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html) e Instant. [BigDecimal](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/BigDecimalAttributeConverter.html)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/InstantAsStringAttributeConverter.html) A lista aparece nas [classes de implementação conhecidas da AttributeConverter interface](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverter.html). A lista inclui muitos tipos e coleções, como mapas, listas e conjuntos.

Para armazenar os dados de um tipo de atributo que não é suportado por padrão ou não está em conformidade com a JavaBean convenção, você pode escrever uma `AttributeConverter` implementação personalizada para fazer a conversão. Consulte a seção de conversão de atributos para ver um [exemplo](ddb-en-client-adv-features-conversion.md#ddb-en-client-adv-features-conversion-example).

Para armazenar os dados de um tipo de atributo cuja classe está em conformidade com a especificação Java Beans (ou uma [classe de dados imutável](ddb-en-client-use-immut.md)), você pode adotar duas abordagens. 
+ Se você tiver acesso ao arquivo de origem, poderá anotar a classe com `@DynamoDbBean` (ou `@DynamoDbImmutable`). A seção que discute atributos aninhados mostra [exemplos](ddb-en-client-adv-features-nested.md#ddb-en-client-adv-features-nested-map-anno) do uso de classes anotadas.
+ Se você não tiver acesso ao arquivo de origem da classe de JavaBean dados do atributo (ou não quiser anotar o arquivo de origem de uma classe à qual você tem acesso), você pode usar a abordagem do construtor. Isso cria um esquema de tabela sem definir as chaves. Em seguida, você pode aninhar esse esquema de tabela dentro de outro esquema de tabela para realizar o mapeamento. A seção de atributos aninhados tem um [exemplo](ddb-en-client-adv-features-nested.md#ddb-en-client-adv-features-nested-map-builder) que mostra o uso de esquemas aninhados.

### Valores nulos
<a name="ddb-en-client-use-types-nulls"></a>

Quando você usa o método `putItem`, o cliente aprimorado não inclui atributos de valor nulo de um objeto de dados mapeado na solicitação ao DynamoDB.

O comportamento padrão do SDK para solicitações `updateItem` remove atributos do item no DynamoDB que estão definidos como nulos no objeto que você envia no método `updateItem`. Se você pretende atualizar alguns valores de atributos e manter os outros inalterados, existem duas opções:
+ Recuperar o item (usando `getItem`) antes de fazer alterações nos valores. Ao usar essa abordagem, o SDK envia todos os valores antigos e atualizados para o DynamoDB.
+ Usar o `[IgnoreNullsMode](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/IgnoreNullsMode.html).SCALAR_ONLY` ou `IgnoreNullsMode.MAPS_ONLY` ao criar a solicitação para atualizar o item. Ambos os modos ignoram propriedades de valor nulo no objeto que representam atributos escalares no DynamoDB. O tópico [Atualizar itens que contêm tipos complexos](ddb-en-client-adv-features-nested.md#ddb-en-client-adv-features-nested-updates) deste guia contém mais informações sobre os valores de `IgnoreNullsMode` e como trabalhar com tipos complexos.

O exemplo a seguir demonstra `ignoreNullsMode()` para o método `updateItem()`.

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

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

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

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

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

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


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

## Métodos básicos do Cliente Aprimorado do DynamoDB
<a name="ddb-en-client-use-basic-ops"></a>

Os métodos básicos do cliente aprimorado mapeiam as operações de serviço do DynamoDB que deram nome a eles. Os exemplos a seguir mostram a variação mais simples de cada método. Você pode personalizar cada método passando um objeto de solicitação aprimorado. Os objetos de solicitação aprimorada oferecem a maioria dos atributos disponíveis no cliente padrão do DynamoDB. Eles estão totalmente documentados na Referência da API do AWS SDK for Java 2.x .

O exemplo usa o [Classe `Customer`](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust) mostrado anteriormente.

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

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

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

// PutItem
customerTable.putItem(customer);

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

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

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

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

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

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

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

## Comparar o Cliente Aprimorado do DynamoDB com o cliente padrão do DynamoDB
<a name="ddb-en-client-use-compare"></a>

Tanto o APIs cliente DynamoDB [— padrão [quanto](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/package-summary.html) aprimorado —](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/package-summary.html) permitem que você trabalhe com tabelas do DynamoDB para realizar operações CRUD (criar, ler, atualizar e excluir) em nível de dados. A diferença entre o cliente APIs é como isso é feito. Usando o cliente padrão, você trabalha diretamente com atributos de dados de nível baixo. A API do cliente aprimorado usa classes Java conhecidas e mapeia a API de nível baixo nos bastidores.

Embora ambos os clientes ofereçam APIs suporte a operações em nível de dados, o cliente padrão do DynamoDB também oferece suporte a operações em nível de recursos. As operações em nível de recurso gerenciam o banco de dados, como criar backups, listar e atualizar tabelas. A API do cliente aprimorado fornece suporte a um número selecionado de operações em nível de recurso, como criar, descrever e excluir tabelas.

Para ilustrar as diferentes abordagens usadas pelos dois clientes APIs, os exemplos de código a seguir mostram a criação da mesma `ProductCatalog` tabela usando o cliente padrão e o cliente aprimorado.

### Comparar: criar uma tabela usando o cliente do DynamoDB padrão
<a name="ddb-en-client-use-compare-cs1"></a>

```
DependencyFactory.dynamoDbClient().createTable(builder -> builder
        .tableName(TABLE_NAME)
        .attributeDefinitions(
                b -> b.attributeName("id").attributeType(ScalarAttributeType.N),
                b -> b.attributeName("title").attributeType(ScalarAttributeType.S),
                b -> b.attributeName("isbn").attributeType(ScalarAttributeType.S)
        )
        .keySchema(
                builder1 -> builder1.attributeName("id").keyType(KeyType.HASH),
                builder2 -> builder2.attributeName("title").keyType(KeyType.RANGE)
        )
        .globalSecondaryIndexes(builder3 -> builder3
                        .indexName("products_by_isbn")
                        .keySchema(builder2 -> builder2
                                .attributeName("isbn").keyType(KeyType.HASH))
                        .projection(builder2 -> builder2
                                .projectionType(ProjectionType.INCLUDE)
                                .nonKeyAttributes("price", "authors"))
                        .provisionedThroughput(builder4 -> builder4
                                .writeCapacityUnits(5L).readCapacityUnits(5L))
        )
        .provisionedThroughput(builder1 -> builder1
                .readCapacityUnits(5L).writeCapacityUnits(5L))
);
```

### Comparar: criar uma tabela usando o Cliente Aprimorado do DynamoDB
<a name="ddb-en-client-use-compare-cs2"></a>

```
DynamoDbEnhancedClient enhancedClient = DependencyFactory.enhancedClient();
productCatalog = enhancedClient.table(TABLE_NAME, TableSchema.fromImmutableClass(ProductCatalog.class));
productCatalog.createTable(b -> b
        .provisionedThroughput(b1 -> b1.readCapacityUnits(5L).writeCapacityUnits(5L))
        .globalSecondaryIndices(b2 -> b2.indexName("products_by_isbn")
                .projection(b4 -> b4
                        .projectionType(ProjectionType.INCLUDE)
                        .nonKeyAttributes("price", "authors"))
                .provisionedThroughput(b3 -> b3.writeCapacityUnits(5L).readCapacityUnits(5L))
        )
);
```

O cliente aprimorado usa a seguinte classe de dados anotada: O Cliente Aprimorado do DynamoDB mapeia tipos de dados Java para tipos de dados do DynamoDB para obter um código menos detalhado e mais fácil de seguir. `ProductCatalog` é um exemplo do uso de uma classe imutável com o Cliente Aprimorado do DynamoDB. O uso de classes imutáveis para classes de dados mapeados será [discutido posteriormente](ddb-en-client-use-immut.md) neste tópico.

### Classe `ProductCatalog`
<a name="ddb-en-client-use-compare-cs3"></a>

```
package org.example.tests.model;

import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbIgnore;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbImmutable;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondaryPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;

import java.math.BigDecimal;
import java.util.Objects;
import java.util.Set;

@DynamoDbImmutable(builder = ProductCatalog.Builder.class)
public class ProductCatalog implements Comparable<ProductCatalog> {
    private Integer id;
    private String title;
    private String isbn;
    private Set<String> authors;
    private BigDecimal price;


    private ProductCatalog(Builder builder){
        this.authors = builder.authors;
        this.id = builder.id;
        this.isbn = builder.isbn;
        this.price = builder.price;
        this.title = builder.title;
    }

    public static Builder builder(){ return new Builder(); }

    @DynamoDbPartitionKey
    public Integer id() { return id; }
    
    @DynamoDbSortKey
    public String title() { return title; }
    
    @DynamoDbSecondaryPartitionKey(indexNames = "products_by_isbn")
    public String isbn() { return isbn; }
    public Set<String> authors() { return authors; }
    public BigDecimal price() { return price; }


    public static final class Builder {
      private Integer id;
      private String title;
      private String isbn;
      private Set<String> authors;
      private BigDecimal price;
      private Builder(){}

      public Builder id(Integer id) { this.id = id; return this; }
      public Builder title(String title) { this.title = title; return this; }
      public Builder isbn(String ISBN) { this.isbn = ISBN; return this; }
      public Builder authors(Set<String> authors) { this.authors = authors; return this; }
      public Builder price(BigDecimal price) { this.price = price; return this; }
      public ProductCatalog build() { return new ProductCatalog(this); }
  }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("ProductCatalog{");
        sb.append("id=").append(id);
        sb.append(", title='").append(title).append('\'');
        sb.append(", isbn='").append(isbn).append('\'');
        sb.append(", authors=").append(authors);
        sb.append(", price=").append(price);
        sb.append('}');
        return sb.toString();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ProductCatalog that = (ProductCatalog) o;
        return id.equals(that.id) && title.equals(that.title) && Objects.equals(isbn, that.isbn) && Objects.equals(authors, that.authors) && Objects.equals(price, that.price);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, title, isbn, authors, price);
    }

    @Override
    @DynamoDbIgnore
    public int compareTo(ProductCatalog other) {
        if (this.id.compareTo(other.id) != 0){
            return this.id.compareTo(other.id);
        } else {
            return this.title.compareTo(other.title);
        }
    }
}
```

Os dois exemplos de código de uma gravação em lote a seguir ilustram a verbosidade e a falta de segurança de tipo ao usar o cliente padrão em vez do cliente aprimorado.

### Comparar: operação de gravação em lote usando o cliente DynamoDB padrão
<a name="ddb-en-client-use-compare-cs4"></a>

```
    public static void batchWriteStandard(DynamoDbClient dynamoDbClient, String tableName) {

        Map<String, AttributeValue> catalogItem = Map.of(
                "authors", AttributeValue.builder().ss("a", "b").build(),
                "id", AttributeValue.builder().n("1").build(),
                "isbn", AttributeValue.builder().s("1-565-85698").build(),
                "title", AttributeValue.builder().s("Title 1").build(),
                "price", AttributeValue.builder().n("52.13").build());

        Map<String, AttributeValue> catalogItem2 = Map.of(
                "authors", AttributeValue.builder().ss("a", "b", "c").build(),
                "id", AttributeValue.builder().n("2").build(),
                "isbn", AttributeValue.builder().s("1-208-98073").build(),
                "title", AttributeValue.builder().s("Title 2").build(),
                "price", AttributeValue.builder().n("21.99").build());

        Map<String, AttributeValue> catalogItem3 = Map.of(
                "authors", AttributeValue.builder().ss("g", "k", "c").build(),
                "id", AttributeValue.builder().n("3").build(),
                "isbn", AttributeValue.builder().s("7-236-98618").build(),
                "title", AttributeValue.builder().s("Title 3").build(),
                "price", AttributeValue.builder().n("42.00").build());

        Set<WriteRequest> writeRequests = Set.of(
                WriteRequest.builder().putRequest(b -> b.item(catalogItem)).build(),
                WriteRequest.builder().putRequest(b -> b.item(catalogItem2)).build(),
                WriteRequest.builder().putRequest(b -> b.item(catalogItem3)).build());

        Map<String, Set<WriteRequest>> productCatalogItems = Map.of(
                "ProductCatalog", writeRequests);

        BatchWriteItemResponse response = dynamoDbClient.batchWriteItem(b -> b.requestItems(productCatalogItems));

        logger.info("Unprocessed items: " + response.unprocessedItems().size());
    }
```

### Comparar: operação de gravação em lote usando o Cliente Aprimorado do DynamoDB
<a name="ddb-en-client-use-compare-cs5"></a>

```
    public static void batchWriteEnhanced(DynamoDbTable<ProductCatalog> productCatalog) {
        ProductCatalog prod = ProductCatalog.builder()
                .id(1)
                .isbn("1-565-85698")
                .authors(new HashSet<>(Arrays.asList("a", "b")))
                .price(BigDecimal.valueOf(52.13))
                .title("Title 1")
                .build();
        ProductCatalog prod2 = ProductCatalog.builder()
                .id(2)
                .isbn("1-208-98073")
                .authors(new HashSet<>(Arrays.asList("a", "b", "c")))
                .price(BigDecimal.valueOf(21.99))
                .title("Title 2")
                .build();
        ProductCatalog prod3 = ProductCatalog.builder()
                .id(3)
                .isbn("7-236-98618")
                .authors(new HashSet<>(Arrays.asList("g", "k", "c")))
                .price(BigDecimal.valueOf(42.00))
                .title("Title 3")
                .build();

        BatchWriteResult batchWriteResult = DependencyFactory.enhancedClient()
                .batchWriteItem(b -> b.writeBatches(
                        WriteBatch.builder(ProductCatalog.class)
                                .mappedTableResource(productCatalog)
                                .addPutItem(prod).addPutItem(prod2).addPutItem(prod3)
                                .build()
                ));
        logger.info("Unprocessed items: " + batchWriteResult.unprocessedPutItemsForTable(productCatalog).size());
    }
```

# Trabalhar com classes de dados imutáveis
<a name="ddb-en-client-use-immut"></a>

O atributo de mapeamento da API do Cliente Aprimorado do DynamoDB funciona com classes de dados imutáveis. Uma classe imutável tem apenas getters e requer uma classe construtora que o SDK usa para criar instâncias da classe. Em vez de usar a anotação `@DynamoDbBean` conforme mostrado na [classe Customer](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust), as classes imutáveis usam a anotação `@DynamoDbImmutable`, a qual usa um parâmetro que indica a classe do construtor a ser usada.

A classe a seguir é uma versão imutável de `Customer`.

```
package org.example.tests.model.immutable;

import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbImmutable;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondaryPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondarySortKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;

import java.time.Instant;

@DynamoDbImmutable(builder = CustomerImmutable.Builder.class)
public class CustomerImmutable {
    private final String id;
    private final String name;
    private final String email;
    private final Instant regDate;

    private CustomerImmutable(Builder b) {
        this.id = b.id;
        this.email = b.email;
        this.name = b.name;
        this.regDate = b.regDate;
    }

    // This method will be automatically discovered and used by the TableSchema.
    public static Builder builder() { return new Builder(); }

    @DynamoDbPartitionKey
    public String id() { return this.id; }

    @DynamoDbSortKey
    public String email() { return this.email; }

    @DynamoDbSecondaryPartitionKey(indexNames = "customers_by_name")
    public String name() { return this.name; }

    @DynamoDbSecondarySortKey(indexNames = {"customers_by_date", "customers_by_name"})
    public Instant regDate() { return this.regDate; }

    public static final class Builder {
        private String id;
        private String email;
        private String name;
        private Instant regDate;

        // The private Builder constructor is visible to the enclosing CustomerImmutable class.
        private Builder() {}

        public Builder id(String id) { this.id = id; return this; }
        public Builder email(String email) { this.email = email; return this; }
        public Builder name(String name) { this.name = name; return this; }
        public Builder regDate(Instant regDate) { this.regDate = regDate; return this; }

        // This method will be automatically discovered and used by the TableSchema.
        public CustomerImmutable build() { return new CustomerImmutable(this); }
    }
}
```

Você deve atender aos seguintes requisitos ao anotar uma classe de dados com `@DynamoDbImmutable`.

1. Todo método que não é uma substituição de `Object.class` e não foi anotado com `@DynamoDbIgnore` deve ser um getter de um atributo da tabela do DynamoDB.

1. Cada getter deve ter um setter correspondente com distinção entre maiúsculas e minúsculas na classe builder.

1. Somente uma das seguintes condições de estrutura precisa ser atendida:
   + A classe builder deve ter um construtor padrão público.
   + A classe de dados deve ter um método estático público chamado `builder()` que não usa parâmetros e que retorna uma instância da classe builder. Essa opção é mostrada na classe imutável `Customer`.

1.  A classe builder deve ter um método público chamado `build()` que não use parâmetros e retorne uma instância da classe imutável. 

Para criar um `TableSchema` para sua classe imutável, use o método `fromImmutableClass()` no `TableSchema` conforme mostrado no trecho a seguir.

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

Assim como você pode criar uma tabela do DynamoDB a partir de uma classe mutável, você pode criar uma a partir de uma classe imutável com uma chamada *única* para `createTable()` do `DynamoDbTable` conforme mostrado no exemplo de trecho a seguir.

```
static void createTableFromImmutable(DynamoDbEnhancedClient enhancedClient, String tableName, DynamoDbWaiter waiter){
    // First, create an in-memory representation of the table using the 'table()' method of the DynamoDb Enhanced Client.
    // 'table()' accepts a name for the table and a TableSchema instance that you created previously.
    DynamoDbTable<CustomerImmutable> customerDynamoDbTable = enhancedClient
            .table(tableName, TableSchema.fromImmutableClass(CustomerImmutable.class));
        
    // Second, call the 'createTable()' method on the DynamoDbTable instance.
    customerDynamoDbTable.createTable();
    waiter.waitUntilTableExists(b -> b.tableName(tableName));
}
```

## Usar bibliotecas de terceiros, como Lombok
<a name="ddb-en-client-use-immut-lombok"></a>

Bibliotecas de terceiros, como [Project Lombok](https://projectlombok.org/), ajudam a gerar código clichê associado a objetos imutáveis. A API do Cliente Aprimorado do DynamoDB funciona com essas bibliotecas, desde que as classes de dados sigam as convenções detalhadas nesta seção. 

O exemplo a seguir mostra a classe `CustomerImmutable` imutável com anotações do Lombok. Observe como o atributo `onMethod` do Lombok copia anotações do DynamoDB baseadas em atributos, como `@DynamoDbPartitionKey`, no código gerado.

```
@Value
@Builder
@DynamoDbImmutable(builder = Customer.CustomerBuilder.class)
public class Customer {
    @Getter(onMethod_=@DynamoDbPartitionKey)
    private String id;

    @Getter(onMethod_=@DynamoDbSortKey)
    private String email;

    @Getter(onMethod_=@DynamoDbSecondaryPartitionKey(indexNames = "customers_by_name"))
    private String name;

    @Getter(onMethod_=@DynamoDbSecondarySortKey(indexNames = {"customers_by_date", "customers_by_name"}))
    private Instant createdDate;
}
```

# Usar expressões e condições
<a name="ddb-en-client-expressions"></a>

As expressões na API do Cliente Aprimorado do DynamoDB são representações Java das [expressões do DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.html).

A API do Cliente Aprimorado do DynamoDB usa três tipos de expressões:

[Expressão](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Expression.html)  
A classe `Expression` é usada quando você define condições e filtros.

[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html)  
Esse tipo de expressão representa as [principais condições](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.KeyConditionExpressions) para operações de consulta.

[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/update/UpdateExpression.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/update/UpdateExpression.html)  
Essa classe ajuda você a escrever [expressões de atualização](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html) do DynamoDB e é usada atualmente na estrutura de extensão quando você atualiza um item.

## Anatomia da expressão
<a name="ddb-en-client-expressions-compoonents"></a>

Uma expressão é composta do seguinte:
+ Uma expressão em cadeia de caracteres (obrigatório). Uma cadeia de caracteres contém uma expressão lógica do DynamoDB com nomes de espaço reservado para nomes e valores de atributos.
+ Um mapa dos valores da expressão (geralmente obrigatório).
+ Um mapa dos nomes das expressões (opcional).

Use um construtor para gerar um objeto `Expression` que tenha a seguinte forma geral.

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

`Expression`s geralmente exigem um mapa dos valores da expressão. O mapa fornece os valores dos espaços reservados na expressão da cadeia de caracteres. A chave do mapa consiste no nome do espaço reservado precedido por dois pontos (`:`) e o valor do mapa é uma instância de. [AttributeValue](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/AttributeValue.html) A [AttributeValues](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/AttributeValues.html)classe tem métodos convenientes para gerar uma `AttributeValue` instância a partir de um literal. Como alternativa, você pode usar o `AttributeValue.Builder` para gerar uma instância `AttributeValue`.

O trecho a seguir mostra um mapa com duas entradas após a linha de comentário 2. A cadeia de caracteres passada para o método `expression()`, mostrada após a linha de comentário 1, contém os espaços reservados que o DynamoDB determina antes de realizar a operação. Esse trecho não contém um mapa dos nomes das expressões, porque *price* é um nome de atributo permissível.

```
    public static void scanAsync(DynamoDbAsyncTable productCatalog) {
        ScanEnhancedRequest request = ScanEnhancedRequest.builder()
                .consistentRead(true)
                .attributesToProject("id", "title", "authors", "price")
                .filterExpression(Expression.builder()
                        // 1. :min_value and :max_value are placeholders for the values provided by the map
                        .expression("price >= :min_value AND price <= :max_value")
                        // 2. Two values are needed for the expression and each is supplied as a map entry.
                        .expressionValues(
                                Map.of( ":min_value", numberValue(8.00),
                                        ":max_value", numberValue(400_000.00)))
                        .build())
                .build();
```

Se um nome de atributo na tabela do DynamoDB for uma palavra reservada, começar com um número ou contiver um espaço, um mapa dos nomes das expressões será necessário para a `Expression`.

Por exemplo, se o nome do atributo fosse `1price` em vez de `price` no exemplo de código anterior, o exemplo precisaria ser modificado conforme mostrado no exemplo a seguir.

```
        ScanEnhancedRequest request = ScanEnhancedRequest.builder()
                .filterExpression(Expression.builder()
                        .expression("#price >= :min_value AND #price <= :max_value")
                        .expressionNames( Map.of("#price", "1price") )
                        .expressionValues(
                                Map.of(":min_value", numberValue(8.00),
                                        ":max_value", numberValue(400_000.00)))
                        .build())
                .build();
```

Um espaço reservado para o nome de uma expressão começa com o sinal de libra (`#`). Uma entrada para o mapa de nomes de expressão usa o espaço reservado como chave e o nome do atributo como valor. O mapa é adicionado ao construtor de expressões com o método `expressionNames()`. O DynamoDB resolve o nome do atributo antes de realizar a operação.

Os valores de expressão não são necessários se uma função for usada na expressão da cadeia de caracteres. Um exemplo de função de expressão é `attribute_exists(<attribute_name>)`.

O exemplo a seguir cria uma `Expression` que usa uma [função do DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions). Uma cadeia de caracteres de expressão neste exemplo não usa espaços reservados. Essa expressão pode ser usada em uma operação `putItem` para verificar se um item já existe no banco de dados com um valor de atributo `movie` igual ao atributo `movie` do objeto de dados.

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

O Guia do desenvolvedor do DynamoDB contém informações completas sobre as expressões[ de nível baixo](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.html) usadas com o DynamoDB.

## Expressões de condição e condicionais
<a name="ddb-en-client-expressions-cond"></a>

Ao usar os métodos `putItem()`, `updateItem()`, `deleteItem()` e também ao usar operações de transação e em lote, você usa objetos de `[Expression](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Expression.html)` para especificar as condições que o DynamoDB deve atender para continuar com a operação. Essas expressões são chamadas de expressões de condição. Para ver um exemplo, consulte a expressão de condição usada no método `addDeleteItem()` (após a linha de comentário 1) do [exemplo de transação](ddb-en-client-use-multiop-trans.md#ddb-en-client-use-multiop-trans-writeitems-opcondition) mostrado neste guia.

Quando você trabalha com os métodos `query()`, uma condição é expressa como uma [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html). A classe `QueryConditional` tem vários métodos estáticos de conveniência que ajudam você a escrever os critérios que determinam quais itens ler no DynamoDB.

Para ver exemplos de `QueryConditionals`, consulte o primeiro exemplo de código da seção [Exemplos de métodos `Query`](ddb-en-client-use-multirecord.md#ddb-en-client-use-multirecord-query-example) deste guia.

## Expressões de filtro
<a name="ddb-en-client-expressions-filter"></a>

As expressões de filtro são usadas em operações de digitalização e consulta para filtrar os itens retornados. 

Uma expressão de filtro é aplicada depois que todos os dados são lidos do banco de dados, de modo que o custo de leitura é o mesmo que se não houvesse filtro. O *Guia do desenvolvedor do Amazon DynamoDB* tem mais informações sobre o uso de expressões de filtro para operações de [consulta](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.FilterExpression) e [verificação](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.FilterExpression).

O exemplo a seguir mostra uma expressão de filtro adicionada a uma solicitação de verificação. O critério restringe os itens devolvidos a itens com um preço de 8,00 a 80,00.

```
        Map<String, AttributeValue> expressionValues = Map.of(
                ":min_value", numberValue(8.00),
                ":max_value", numberValue(80.00));

        ScanEnhancedRequest request = ScanEnhancedRequest.builder()
                .consistentRead(true)
                // 1. the 'attributesToProject()' method allows you to specify which values you want returned.
                .attributesToProject("id", "title", "authors", "price")
                // 2. Filter expression limits the items returned that match the provided criteria.
                .filterExpression(Expression.builder()
                        .expression("price >= :min_value AND price <= :max_value")
                        .expressionValues(expressionValues)
                        .build())
                .build();
```

## Expressões de atualização
<a name="ddb-en-client-expressions-update"></a>

O método `updateItem()` do Cliente Aprimorado do DynamoDB fornece uma forma padrão de atualizar itens no DynamoDB. [No entanto, quando você precisar de mais funcionalidades, [UpdateExpressions](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/update/UpdateExpression.html)forneça uma representação segura da sintaxe da expressão de atualização do DynamoDB.](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html) Por exemplo, você pode usar `UpdateExpressions` para aumentar valores sem primeiro ler itens do DynamoDB ou adicionar membros individuais a uma lista. Atualmente, as expressões de atualização estão disponíveis em extensões personalizadas para o método `updateItem()`.

Para ver um exemplo que usa expressões de atualização, consulte o [exemplo de extensão personalizada](ddb-en-client-extensions-custom.md) neste guia.

Mais informações sobre expressões de atualização estão disponíveis no [Guia do desenvolvedor do Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html).

# Trabalhar com resultados paginados: verificações e consultas
<a name="ddb-en-client-use-multirecord"></a>

Os métodos `scan`, `query` e `batch` da API do Cliente Aprimorado do DynamoDB retornam respostas com uma ou mais *páginas*. Uma página contém um ou mais itens. Seu código pode processar a resposta por página ou pode processar itens individuais.

Uma resposta paginada retornada pelo `DynamoDbEnhancedClient` cliente síncrono retorna um [PageIterable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/PageIterable.html)objeto, enquanto uma resposta retornada pelo `DynamoDbEnhancedAsyncClient` assíncrono retorna um objeto. [PagePublisher](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/PagePublisher.html)

Esta seção analisa o processamento de resultados paginados e fornece exemplos que usam a verificação e a consulta. APIs

## Verificar uma tabela
<a name="ddb-en-client-use-multirecord-scan"></a>

O método do SDK [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbAsyncTable.html#scan(java.util.function.Consumer)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbAsyncTable.html#scan(java.util.function.Consumer)) corresponde à [operação do DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html) com o mesmo nome. A API do Cliente Aprimorado do DynamoDB oferece as mesmas opções, mas usa um modelo de objeto familiar e gerencia a paginação para você.

Primeiro, exploramos a `PageIterable` interface examinando o `scan` método da classe de mapeamento síncrono, [DynamoDbTable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html).

### Usar a API síncrona
<a name="ddb-en-client-use-multirecord-scan-sync"></a>

O exemplo a seguir mostra o método `scan` que usa uma [expressão](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Expression.html) para filtrar os itens retornados. [ProductCatalog](ddb-en-client-use.md#ddb-en-client-use-compare-cs3)É o objeto do modelo que foi mostrado anteriormente.

A expressão de filtragem mostrada após a linha de comentário 2 limita os `ProductCatalog` itens que são retornados àqueles com um valor de preço entre 8,00 e 80,00, inclusive.

Este exemplo também exclui os `isbn` valores usando o `attributesToProject` método mostrado após a linha de comentário 1.

Depois da linha de comentário 3, o objeto `PageIterable`, `pagedResults`, é retornado pelo método `scan`. O método `stream` de `PageIterable` retorna um objeto [https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html), que você pode usar para processar as páginas. Neste exemplo, o número de páginas é contado e registrado.

Começando com a linha de comentários 4, o exemplo mostra duas variações de acesso aos itens `ProductCatalog`. A versão após a linha de comentário 4a percorre cada página e classifica e registra os itens em cada página. A versão após a linha de comentário 4b ignora a iteração da página e acessa os itens diretamente.

A interface `PageIterable` oferece várias maneiras de processar resultados por causa de suas duas interfaces principais: [https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html](https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html) e [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/pagination/sync/SdkIterable.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/pagination/sync/SdkIterable.html). `Iterable` traz os métodos `forEach`, `iterator` e `spliterator`, e `SdkIterable` traz o método `stream`.

```
    public static void scanSync(DynamoDbTable<ProductCatalog> productCatalog) {

        Map<String, AttributeValue> expressionValues = Map.of(
                ":min_value", numberValue(8.00),
                ":max_value", numberValue(80.00));

        ScanEnhancedRequest request = ScanEnhancedRequest.builder()
                .consistentRead(true)
                // 1. the 'attributesToProject()' method allows you to specify which values you want returned.
                .attributesToProject("id", "title", "authors", "price")
                // 2. Filter expression limits the items returned that match the provided criteria.
                .filterExpression(Expression.builder()
                        .expression("price >= :min_value AND price <= :max_value")
                        .expressionValues(expressionValues)
                        .build())
                .build();

        // 3. A PageIterable object is returned by the scan method.
        PageIterable<ProductCatalog> pagedResults = productCatalog.scan(request);
        logger.info("page count: {}", pagedResults.stream().count());

        // 4. Log the returned ProductCatalog items using two variations.
        // 4a. This version sorts and logs the items of each page.
        pagedResults.stream().forEach(p -> p.items().stream()
                .sorted(Comparator.comparing(ProductCatalog::price))
                .forEach(
                        item -> logger.info(item.toString())
                ));
        // 4b. This version sorts and logs all items for all pages.
        pagedResults.items().stream()
                .sorted(Comparator.comparing(ProductCatalog::price))
                .forEach(
                        item -> logger.info(item.toString())
                );
    }
```

### Usar a API assíncrona
<a name="ddb-en-client-use-multirecord-scan-async"></a>

O método `scan` assíncrono retorna os resultados como um objeto `PagePublisher`. A interface `PagePublisher` tem dois métodos `subscribe` que você pode usar para processar páginas de resposta. Um método `subscribe` vem da interface principal `org.reactivestreams.Publisher`. Para processar páginas usando essa primeira opção, transmita uma instância `[Subscriber](https://www.reactive-streams.org/reactive-streams-1.0.0-javadoc/org/reactivestreams/Subscriber.html)` para o método `subscribe`. O primeiro exemplo a seguir mostra o uso do método `subscribe`.

O segundo `subscribe` método vem da [SdkPublisher](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/SdkPublisher.html)interface. Esta versão de `subscribe` aceita um [https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html](https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html) em vez de um `Subscriber`. Essa variação do método `subscribe` é mostrada no segundo exemplo.

O exemplo a seguir mostra a versão assíncrona do método `scan` que usa a mesma expressão de filtro mostrada no exemplo anterior. 

Após a linha de comentário 3, `DynamoDbAsyncTable.scan` retorna um objeto `PagePublisher`. Na próxima linha, o código cria uma instância da interface `org.reactivestreams.Subscriber`, `ProductCatalogSubscriber`, que assina a `PagePublisher` após o comentário na linha 4.

O objeto `Subscriber` coleta os itens `ProductCatalog` de cada página no método `onNext` após a linha de comentário 8 no exemplo da classe `ProductCatalogSubscriber`. Os itens são armazenados na varável `List` privada e acessados no código de chamada com o método `ProductCatalogSubscriber.getSubscribedItems()`. A chamado é feita após a linha de comentários 5.

Depois que a lista é recuperada, o código classifica todos os itens `ProductCatalog` por preço e registra cada item.

O [CountDownLatch](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html)in the `ProductCatalogSubscriber` class bloqueia o tópico de chamada até que todos os itens tenham sido adicionados à lista antes de continuar após a linha de comentários 5. 

```
    public static void scanAsync(DynamoDbAsyncTable productCatalog) {
        ScanEnhancedRequest request = ScanEnhancedRequest.builder()
                .consistentRead(true)
                .attributesToProject("id", "title", "authors", "price")
                .filterExpression(Expression.builder()
                        // 1. :min_value and :max_value are placeholders for the values provided by the map
                        .expression("price >= :min_value AND price <= :max_value")
                        // 2. Two values are needed for the expression and each is supplied as a map entry.
                        .expressionValues(
                                Map.of( ":min_value", numberValue(8.00),
                                        ":max_value", numberValue(400_000.00)))
                        .build())
                .build();

        // 3. A PagePublisher object is returned by the scan method.
        PagePublisher<ProductCatalog> pagePublisher = productCatalog.scan(request);
        ProductCatalogSubscriber subscriber = new ProductCatalogSubscriber();
        // 4. Subscribe the ProductCatalogSubscriber to the PagePublisher.
        pagePublisher.subscribe(subscriber);
        // 5. Retrieve all collected ProductCatalog items accumulated by the subscriber.
        subscriber.getSubscribedItems().stream()
                .sorted(Comparator.comparing(ProductCatalog::price))
                .forEach(item ->
                        logger.info(item.toString()));
        // 6. Use a Consumer to work through each page.
        pagePublisher.subscribe(page -> page
                        .items().stream()
                        .sorted(Comparator.comparing(ProductCatalog::price))
                        .forEach(item ->
                                logger.info(item.toString())))
                .join(); // If needed, blocks the subscribe() method thread until it is finished processing.
        // 7. Use a Consumer to work through each ProductCatalog item.
        pagePublisher.items()
                .subscribe(product -> logger.info(product.toString()))
                .exceptionally(failure -> {
                    logger.error("ERROR  - ", failure);
                    return null;
                })
                .join(); // If needed, blocks the subscribe() method thread until it is finished processing.
    }
```

```
    private static class ProductCatalogSubscriber implements Subscriber<Page<ProductCatalog>> {
        private CountDownLatch latch = new CountDownLatch(1);
        private Subscription subscription;
        private List<ProductCatalog> itemsFromAllPages = new ArrayList<>();

        @Override
        public void onSubscribe(Subscription sub) {
            subscription = sub;
            subscription.request(1L);
            try {
                latch.await(); // Called by main thread blocking it until latch is released.
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        @Override
        public void onNext(Page<ProductCatalog> productCatalogPage) {
            // 8. Collect all the ProductCatalog instances in the page, then ask the publisher for one more page.
            itemsFromAllPages.addAll(productCatalogPage.items());
            subscription.request(1L);
        }

        @Override
        public void onError(Throwable throwable) {
        }

        @Override
        public void onComplete() {
            latch.countDown(); // Call by subscription thread; latch releases.
        }

        List<ProductCatalog> getSubscribedItems() {
            return this.itemsFromAllPages;
        }
    }
```

O exemplo de trecho a seguir usa a versão do método `PagePublisher.subscribe` que aceita um `Consumer` após a linha de comentário 6. O parâmetro lambda em Java consome páginas, que processam ainda mais cada item. Neste exemplo, cada página é processada e os itens em cada página são classificados e, em seguida, registrados.

```
        // 6. Use a Consumer to work through each page.
        pagePublisher.subscribe(page -> page
                        .items().stream()
                        .sorted(Comparator.comparing(ProductCatalog::price))
                        .forEach(item ->
                                logger.info(item.toString())))
                .join(); // If needed, blocks the subscribe() method thread until it is finished processing.
```

O método `items` do `PagePublisher` desempacota as instâncias do modelo para que seu código possa processar os itens diretamente. Essa abordagem é mostrada no trecho a seguir.

```
        // 7. Use a Consumer to work through each ProductCatalog item.
        pagePublisher.items()
                .subscribe(product -> logger.info(product.toString()))
                .exceptionally(failure -> {
                    logger.error("ERROR  - ", failure);
                    return null;
                })
                .join(); // If needed, blocks the subscribe() method thread until it is finished processing.
```

## Consultar uma tabela
<a name="ddb-en-client-use-multirecord-query"></a>

É possível usar o Cliente Aprimorado do DynamoDB para consultar sua tabela e recuperar vários itens que correspondam a critérios específicos. O método [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html#query(software.amazon.awssdk.enhanced.dynamodb.model.QueryEnhancedRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html#query(software.amazon.awssdk.enhanced.dynamodb.model.QueryEnhancedRequest)) encontra itens com base nos valores da chave primária usando as anotações `@DynamoDbPartitionKey` e, opcionalmente, `@DynamoDbSortKey` definidas em sua classe de dados.

O método `query()` requer um valor de chave de partição e, opcionalmente, aceita condições de chave de classificação para refinar ainda mais os resultados. Assim como a API `scan`, as consultas retornam um `PageIterable` para chamadas síncronas e um `PagePublisher` para chamadas assíncronas.

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

O exemplo de código do método `query()` a seguir usa a classe `MovieActor`. A classe de dados define uma chave primária composta constituída pelo atributo **`movie`** como a chave de partição e pelo atributo **`actor`** como a chave de classificação. 

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

```
package org.example.tests.model;

import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbAttribute;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondaryPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondarySortKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;

import java.util.Objects;

@DynamoDbBean
public class MovieActor implements Comparable<MovieActor> {

    private String movieName;
    private String actorName;
    private String actingAward;
    private Integer actingYear;
    private String actingSchoolName;

    @DynamoDbPartitionKey
    @DynamoDbAttribute("movie")
    public String getMovieName() {
        return movieName;
    }

    public void setMovieName(String movieName) {
        this.movieName = movieName;
    }

    @DynamoDbSortKey
    @DynamoDbAttribute("actor")
    public String getActorName() {
        return actorName;
    }

    public void setActorName(String actorName) {
        this.actorName = actorName;
    }

    @DynamoDbSecondaryPartitionKey(indexNames = "acting_award_year")
    @DynamoDbAttribute("actingaward")
    public String getActingAward() {
        return actingAward;
    }

    public void setActingAward(String actingAward) {
        this.actingAward = actingAward;
    }

    @DynamoDbSecondarySortKey(indexNames = {"acting_award_year", "movie_year"})
    @DynamoDbAttribute("actingyear")
    public Integer getActingYear() {
        return actingYear;
    }

    public void setActingYear(Integer actingYear) {
        this.actingYear = actingYear;
    }

    @DynamoDbAttribute("actingschoolname")
    public String getActingSchoolName() {
        return actingSchoolName;
    }

    public void setActingSchoolName(String actingSchoolName) {
        this.actingSchoolName = actingSchoolName;
    }

    @Override
    public String toString() {
        final StringBuffer sb = new StringBuffer("MovieActor{");
        sb.append("movieName='").append(movieName).append('\'');
        sb.append(", actorName='").append(actorName).append('\'');
        sb.append(", actingAward='").append(actingAward).append('\'');
        sb.append(", actingYear=").append(actingYear);
        sb.append(", actingSchoolName='").append(actingSchoolName).append('\'');
        sb.append('}');
        return sb.toString();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        MovieActor that = (MovieActor) o;
        return Objects.equals(movieName, that.movieName) && Objects.equals(actorName, that.actorName) && Objects.equals(actingAward, that.actingAward) && Objects.equals(actingYear, that.actingYear) && Objects.equals(actingSchoolName, that.actingSchoolName);
    }

    @Override
    public int hashCode() {
        return Objects.hash(movieName, actorName, actingAward, actingYear, actingSchoolName);
    }

    @Override
    public int compareTo(MovieActor o) {
        if (this.movieName.compareTo(o.movieName) != 0){
            return this.movieName.compareTo(o.movieName);
        } else {
            return this.actorName.compareTo(o.actorName);
        }
    }
}
```

Os exemplos de código a seguir são consultados com base nos itens a seguir.

#### Itens na tabela `MovieActor`
<a name="ddb-en-client-use-movieactor-items"></a>

```
MovieActor{movieName='movie01', actorName='actor0', actingAward='actingaward0', actingYear=2001, actingSchoolName='null'}
MovieActor{movieName='movie01', actorName='actor1', actingAward='actingaward1', actingYear=2001, actingSchoolName='actingschool1'}
MovieActor{movieName='movie01', actorName='actor2', actingAward='actingaward2', actingYear=2001, actingSchoolName='actingschool2'}
MovieActor{movieName='movie01', actorName='actor3', actingAward='actingaward3', actingYear=2001, actingSchoolName='null'}
MovieActor{movieName='movie01', actorName='actor4', actingAward='actingaward4', actingYear=2001, actingSchoolName='actingschool4'}
MovieActor{movieName='movie02', actorName='actor0', actingAward='actingaward0', actingYear=2002, actingSchoolName='null'}
MovieActor{movieName='movie02', actorName='actor1', actingAward='actingaward1', actingYear=2002, actingSchoolName='actingschool1'}
MovieActor{movieName='movie02', actorName='actor2', actingAward='actingaward2', actingYear=2002, actingSchoolName='actingschool2'}
MovieActor{movieName='movie02', actorName='actor3', actingAward='actingaward3', actingYear=2002, actingSchoolName='null'}
MovieActor{movieName='movie02', actorName='actor4', actingAward='actingaward4', actingYear=2002, actingSchoolName='actingschool4'}
MovieActor{movieName='movie03', actorName='actor0', actingAward='actingaward0', actingYear=2003, actingSchoolName='null'}
MovieActor{movieName='movie03', actorName='actor1', actingAward='actingaward1', actingYear=2003, actingSchoolName='actingschool1'}
MovieActor{movieName='movie03', actorName='actor2', actingAward='actingaward2', actingYear=2003, actingSchoolName='actingschool2'}
MovieActor{movieName='movie03', actorName='actor3', actingAward='actingaward3', actingYear=2003, actingSchoolName='null'}
MovieActor{movieName='movie03', actorName='actor4', actingAward='actingaward4', actingYear=2003, actingSchoolName='actingschool4'}
```

O código a seguir define duas instâncias `QueryConditional`: `keyEqual` (após a linha de comentário 1) e `sortGreaterThanOrEqualTo` (após a linha de comentário 1a).

#### Consultar itens por chave de partição
<a name="keyEqual-query-conditional-example"></a>

A instância `keyEqual` combina itens com um valor de chave de partição de **`movie01`**. 

Esse exemplo também define uma expressão de filtro após a linha de comentário 2 que filtra qualquer item que não tenha um valor **`actingschoolname`**.

O `QueryEnhancedRequest` combina a condição da chave e a expressão do filtro para a consulta.

```
    public static void query(DynamoDbTable movieActorTable) {

        // 1. Define a QueryConditional instance to return items matching a partition value.
        QueryConditional keyEqual = QueryConditional.keyEqualTo(b -> b.partitionValue("movie01"));
        // 1a. Define a QueryConditional that adds a sort key criteria to the partition value criteria.
        QueryConditional sortGreaterThanOrEqualTo = QueryConditional.sortGreaterThanOrEqualTo(b -> b.partitionValue("movie01").sortValue("actor2"));
        // 2. Define a filter expression that filters out items whose attribute value is null.
        final Expression filterOutNoActingschoolname = Expression.builder().expression("attribute_exists(actingschoolname)").build();

        // 3. Build the query request.
        QueryEnhancedRequest tableQuery = QueryEnhancedRequest.builder()
                .queryConditional(keyEqual)
                .filterExpression(filterOutNoActingschoolname)
                .build();
        // 4. Perform the query using the "keyEqual" conditional and filter expression.
        PageIterable<MovieActor> pagedResults = movieActorTable.query(tableQuery);
        logger.info("page count: {}", pagedResults.stream().count()); // Log  number of pages.

        pagedResults.items().stream()
                .sorted()
                .forEach(
                        item -> logger.info(item.toString()) // Log the sorted list of items.
                );
```

**Example – Saída usando a consulta condicional `keyEqual`**  
Esta é a saída gerada pela execução do método. A saída exibe itens com um valor `movieName` de **movie01** e não exibe nenhum item com `actingSchoolName` igual a **`null`**.  

```
2023-03-05 13:11:05 [main] INFO  org.example.tests.QueryDemo:46 - page count: 1
2023-03-05 13:11:05 [main] INFO  org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor1', actingAward='actingaward1', actingYear=2001, actingSchoolName='actingschool1'}
2023-03-05 13:11:05 [main] INFO  org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor2', actingAward='actingaward2', actingYear=2001, actingSchoolName='actingschool2'}
2023-03-05 13:11:05 [main] INFO  org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor4', actingAward='actingaward4', actingYear=2001, actingSchoolName='actingschool4'}
```

#### Consultar itens por chave de partição e chave de classificação
<a name="sort-type-query-conditional-example"></a>

O `sortGreaterThanOrEqualTo` `QueryConditional` refina uma correspondência de chave de partição (**movie01**) adicionando uma condição de chave de classificação para valores maiores ou iguais a **actor2**.

Os [métodos `QueryConditional`](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html) que começam com `sort` exigem que o valor da chave de partição corresponda e refinam ainda mais a consulta por meio de uma comparação com base no valor da chave de classificação. `Sort` no nome do método não significa que os resultados estão classificados, mas que um valor de chave de classificação será usado para comparação.

No trecho a seguir, alteramos a solicitação de consulta mostrada anteriormente após a linha de comentário 3. Esse trecho substitui a condicional de consulta “KeyEqual” pela condicional de consulta "sortGreaterThanOrEqualTo" que foi definida após a linha de comentário 1a. O código a seguir também remove a expressão do filtro.

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

**Example – Saída usando a consulta condicional `sortGreaterThanOrEqualTo`**  
A saída a seguir exibe os resultados da consulta. A consulta retorna itens que têm um valor `movieName` igual a **movie01** e somente itens que têm um valor `actorName` maior ou igual a **actor2**. Como removemos o filtro, a consulta retorna itens que não têm valor para o atributo `actingSchoolName`.  

```
2023-03-05 13:15:00 [main] INFO  org.example.tests.QueryDemo:46 - page count: 1
2023-03-05 13:15:00 [main] INFO  org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor2', actingAward='actingaward2', actingYear=2001, actingSchoolName='actingschool2'}
2023-03-05 13:15:00 [main] INFO  org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor3', actingAward='actingaward3', actingYear=2001, actingSchoolName='null'}
2023-03-05 13:15:00 [main] INFO  org.example.tests.QueryDemo:51 - MovieActor{movieName='movie01', actorName='actor4', actingAward='actingaward4', actingYear=2001, actingSchoolName='actingschool4'}
```

# Realizar operações em lote
<a name="ddb-en-client-use-multiop-batch"></a>

A API do Cliente Aprimorado do DynamoDB oferece dois métodos em lote, [`batchGetItem`()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#batchGetItem(java.util.function.Consumer)) e [`batchWriteItem`()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#batchWriteItem(java.util.function.Consumer)).

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

Com o método [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#batchGetItem(java.util.function.Consumer)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#batchGetItem(java.util.function.Consumer)), você pode recuperar até 100 itens individuais em várias tabelas com uma solicitação geral. O exemplo a seguir usa as classes de dados [`Customer`](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust) e [`MovieActor`](ddb-en-client-use-multirecord.md#ddb-en-client-use-movieactor-class) mostradas anteriormente.

No exemplo após as linhas 1 e 2, você cria objetos `[ReadBatch](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/ReadBatch.html)` que são adicionados posteriormente como parâmetros ao método `batchGetItem()` após a linha de comentário 3. 

O código após a linha de comentário 1 cria o lote para ser lido na tabela `Customer`. O código após a linha de comentário 1a mostra o uso de um compilador `[GetItemEnhancedRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/GetItemEnhancedRequest.Builder.html)` que usa um valor de chave primária e um valor de chave de classificação para especificar o item a ser lido. Se a classe de dados tiver uma chave composta, você deverá fornecer o valor da chave de partição e o valor da chave de classificação. 

Ao contrário de especificar valores-chave para solicitar um item, você pode usar uma classe de dados para solicitar um item, conforme mostrado após a linha de comentário 1b. O SDK extrai os valores-chave nos bastidores antes de enviar a solicitação.

Ao especificar o item usando a abordagem baseada em chaves, conforme mostrado nas duas instruções após 2a, você também pode especificar que o DynamoDB deve realizar uma [leitura altamente consistente](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html). Quando o método `consistentRead()` é usado, ele deve ser usado em todos os itens solicitados para a mesma tabela.

Para recuperar os itens encontrados pelo DynamoDB, use o método `[resultsForTable() ](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchGetResultPage.html#resultsForTable(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource))` mostrado após a linha de comentário 4. Chame o método para cada tabela que foi lida na solicitação. `resultsForTable()` retorna uma lista de itens encontrados que você pode processar usando qualquer método `java.util.List`. Este exemplo registra cada item.

Para descobrir itens que o DynamoDB não processou, use a abordagem após a linha de comentários 5. A classe `BatchGetResultPage` tem o método `[unprocessedKeysForTable()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchGetResultPage.html#unprocessedKeysForTable(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource))` que dá acesso a cada chave que não foi processada. A [referência BatchGetItem da API](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchGetItem.html) tem mais informações sobre situações que resultam em itens não processados.

```
    public static void batchGetItemExample(DynamoDbEnhancedClient enhancedClient,
                                           DynamoDbTable<Customer> customerTable,
                                           DynamoDbTable<MovieActor> movieActorTable) {

        Customer customer2 = new Customer();
        customer2.setId("2");
        customer2.setEmail("cust2@example.org");

        // 1. Build a batch to read from the Customer table.
        ReadBatch customerBatch = ReadBatch.builder(Customer.class)
                .mappedTableResource(customerTable)
                // 1a. Specify the primary key value and sort key value for the item.
                .addGetItem(b -> b.key(k -> k.partitionValue("1").sortValue("cust1@orgname.org")))
                // 1b. Alternatively, supply a data class instances to provide the primary key values.
                .addGetItem(customer2)
                .build();

        // 2. Build a batch to read from the MovieActor table.
        ReadBatch moveActorBatch = ReadBatch.builder(MovieActor.class)
                .mappedTableResource(movieActorTable)
                // 2a. Call consistentRead(Boolean.TRUE) for each item for the same table.
                .addGetItem(b -> b.key(k -> k.partitionValue("movie01").sortValue("actor1")).consistentRead(Boolean.TRUE))
                .addGetItem(b -> b.key(k -> k.partitionValue("movie01").sortValue("actor4")).consistentRead(Boolean.TRUE))
                .build();

        // 3. Add ReadBatch objects to the request.
        BatchGetResultPageIterable resultPages = enhancedClient.batchGetItem(b -> b.readBatches(customerBatch, moveActorBatch));

        // 4. Retrieve the successfully requested items from each table.
        resultPages.resultsForTable(customerTable).forEach(item -> logger.info(item.toString()));
        resultPages.resultsForTable(movieActorTable).forEach(item -> logger.info(item.toString()));

        // 5. Retrieve the keys of the items requested but not processed by the service.
        resultPages.forEach((BatchGetResultPage pageResult) -> {
            pageResult.unprocessedKeysForTable(customerTable).forEach(key -> logger.info("Unprocessed item key: " + key.toString()));
            pageResult.unprocessedKeysForTable(movieActorTable).forEach(key -> logger.info("Unprocessed item key: " + key.toString()));
        });
    }
```

Suponha que os itens a seguir estejam nas duas tabelas antes de executar o código de exemplo.

### Itens em tabelas
<a name="ddb-en-client-use-multiop-batch-get-tableitems"></a>

```
Customer [id=1, name=CustName1, email=cust1@example.org, regDate=2023-03-31T15:46:27.688Z]
Customer [id=2, name=CustName2, email=cust2@example.org, regDate=2023-03-31T15:46:28.688Z]
Customer [id=3, name=CustName3, email=cust3@example.org, regDate=2023-03-31T15:46:29.688Z]
Customer [id=4, name=CustName4, email=cust4@example.org, regDate=2023-03-31T15:46:30.688Z]
Customer [id=5, name=CustName5, email=cust5@example.org, regDate=2023-03-31T15:46:31.689Z]
MovieActor{movieName='movie01', actorName='actor0', actingAward='actingaward0', actingYear=2001, actingSchoolName='null'}
MovieActor{movieName='movie01', actorName='actor1', actingAward='actingaward1', actingYear=2001, actingSchoolName='actingschool1'}
MovieActor{movieName='movie01', actorName='actor2', actingAward='actingaward2', actingYear=2001, actingSchoolName='actingschool2'}
MovieActor{movieName='movie01', actorName='actor3', actingAward='actingaward3', actingYear=2001, actingSchoolName='null'}
MovieActor{movieName='movie01', actorName='actor4', actingAward='actingaward4', actingYear=2001, actingSchoolName='actingschool4'}
```

A saída a seguir mostra os itens retornados e registrados após a linha de comentários 4.

```
Customer [id=1, name=CustName1, email=cust1@example.org, regDate=2023-03-31T15:46:27.688Z]
Customer [id=2, name=CustName2, email=cust2@example.org, regDate=2023-03-31T15:46:28.688Z]
MovieActor{movieName='movie01', actorName='actor4', actingAward='actingaward4', actingYear=2001, actingSchoolName='actingschool4'}
MovieActor{movieName='movie01', actorName='actor1', actingAward='actingaward1', actingYear=2001, actingSchoolName='actingschool1'}
```

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

O método `batchWriteItem()` coloca ou exclui vários itens em uma ou mais tabelas. Você pode especificar até 25 operações individuais de colocação ou exclusão na solicitação. O exemplo a seguir usa as classes modelo [`ProductCatalog`](ddb-en-client-use.md#ddb-en-client-use-compare-cs3) e [`MovieActor`](ddb-en-client-use-multirecord.md#ddb-en-client-use-movieactor-class) mostradas anteriormente.

Os objetos `WriteBatch` são construídos após as linhas de comentário 1 e 2. Para a tabela `ProductCatalog`, o código coloca um item e exclui um item. Para a tabela `MovieActor` após a linha de comentário 2, o código coloca dois itens e exclui um.

O método `batchWriteItem` é chamado após a linha de comentário 3. O parâmetro `[builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchWriteItemEnhancedRequest.Builder.html)` fornece as solicitações em lote para cada tabela.

O objeto `[BatchWriteResult](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchWriteResult.html)` retornado fornece métodos separados para cada operação para visualizar solicitações não processadas. O código após a linha de comentário 4a fornece as chaves para solicitações de exclusão não processadas e o código após a linha de comentário 4b fornece os itens de venda não processados.

```
    public static void batchWriteItemExample(DynamoDbEnhancedClient enhancedClient,
                                             DynamoDbTable<ProductCatalog> catalogTable,
                                             DynamoDbTable<MovieActor> movieActorTable) {

        // 1. Build a batch to write to the ProductCatalog table.
        WriteBatch products = WriteBatch.builder(ProductCatalog.class)
                .mappedTableResource(catalogTable)
                .addPutItem(b -> b.item(getProductCatItem1()))
                .addDeleteItem(b -> b.key(k -> k
                        .partitionValue(getProductCatItem2().id())
                        .sortValue(getProductCatItem2().title())))
                .build();

        // 2. Build a batch to write to the MovieActor table.
        WriteBatch movies = WriteBatch.builder(MovieActor.class)
                .mappedTableResource(movieActorTable)
                .addPutItem(getMovieActorYeoh())
                .addPutItem(getMovieActorBlanchettPartial())
                .addDeleteItem(b -> b.key(k -> k
                        .partitionValue(getMovieActorStreep().getMovieName())
                        .sortValue(getMovieActorStreep().getActorName())))
                .build();

        // 3. Add WriteBatch objects to the request.
        BatchWriteResult batchWriteResult = enhancedClient.batchWriteItem(b -> b.writeBatches(products, movies));
        // 4. Retrieve keys for items the service did not process.
        // 4a. 'unprocessedDeleteItemsForTable()' returns keys for delete requests that did not process.
        if (batchWriteResult.unprocessedDeleteItemsForTable(movieActorTable).size() > 0) {
            batchWriteResult.unprocessedDeleteItemsForTable(movieActorTable).forEach(key ->
                    logger.info(key.toString()));
        }
        // 4b. 'unprocessedPutItemsForTable()' returns keys for put requests that did not process.
        if (batchWriteResult.unprocessedPutItemsForTable(catalogTable).size() > 0) {
            batchWriteResult.unprocessedPutItemsForTable(catalogTable).forEach(key ->
                    logger.info(key.toString()));
        }
    }
```

Os métodos auxiliares a seguir fornecem os objetos de modelo para as operações colocar e excluir.

### Métodos auxiliares
<a name="ddb-en-client-use-multiop-batch-write-helpers"></a>

```
 1.     public static ProductCatalog getProductCatItem1() {
 2.         return ProductCatalog.builder()
 3.                 .id(2)
 4.                 .isbn("1-565-85698")
 5.                 .authors(new HashSet<>(Arrays.asList("a", "b")))
 6.                 .price(BigDecimal.valueOf(30.22))
 7.                 .title("Title 55")
 8.                 .build();
 9.     }
10. 
11.     public static ProductCatalog getProductCatItem2() {
12.         return ProductCatalog.builder()
13.                 .id(4)
14.                 .price(BigDecimal.valueOf(40.00))
15.                 .title("Title 1")
16.                 .build();
17.     }  
18. 
19.     public static MovieActor getMovieActorBlanchettPartial() {
20.         MovieActor movieActor = new MovieActor();
21.         movieActor.setActorName("Cate Blanchett");
22.         movieActor.setMovieName("Blue Jasmine");
23.         movieActor.setActingYear(2023);
24.         movieActor.setActingAward("Best Actress");
25.         return movieActor;
26.     }
27. 
28.     public static MovieActor getMovieActorStreep() {
29.         MovieActor movieActor = new MovieActor();
30.         movieActor.setActorName("Meryl Streep");
31.         movieActor.setMovieName("Sophie's Choice");
32.         movieActor.setActingYear(1982);
33.         movieActor.setActingAward("Best Actress");
34.         movieActor.setActingSchoolName("Yale School of Drama");
35.         return movieActor;
36.     }
37. 
38.     public static MovieActor getMovieActorYeoh(){
39.         MovieActor movieActor = new MovieActor();
40.         movieActor.setActorName("Michelle Yeoh");
41.         movieActor.setMovieName("Everything Everywhere All at Once");
42.         movieActor.setActingYear(2023);
43.         movieActor.setActingAward("Best Actress");
44.         movieActor.setActingSchoolName("Royal Academy of Dance");
45.         return movieActor;
46.     }
```

Suponha que as tabelas contenham os itens a seguir antes de você executar o código de exemplo.

```
MovieActor{movieName='Blue Jasmine', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2013, actingSchoolName='National Institute of Dramatic Art'}
MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
```

Depois que o código de exemplo for concluído, as tabelas conterão os itens a seguir.

```
MovieActor{movieName='Blue Jasmine', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2013, actingSchoolName='null'}
MovieActor{movieName='Everything Everywhere All at Once', actorName='Michelle Yeoh', actingAward='Best Actress', actingYear=2023, actingSchoolName='Royal Academy of Dance'}
ProductCatalog{id=2, title='Title 55', isbn='1-565-85698', authors=[a, b], price=30.22}
```

Observe na tabela `MovieActor` que o item do filme `Blue Jasmine` foi substituído pelo item usado na solicitação de colocação adquirida por meio do método `getMovieActorBlanchettPartial()` auxiliar. Se um valor de atributo do bean de dados não tiver sido fornecido, o valor no banco de dados será removido. É por isso que o resultado `actingSchoolName` é nulo para o item do filme `Blue Jasmine`.

**nota**  
Embora a documentação da API sugira que expressões de condição possam ser usadas e que a capacidade consumida e as métricas de coleta possam ser retornadas com solicitações individuais de [colocar](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/PutItemEnhancedRequest.html) e [excluir](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/DeleteItemEnhancedRequest.html), esse não é o caso em um cenário de gravação em lote. Para melhorar o desempenho das operações em lote, essas opções individuais são ignoradas.

# Realizar operações de transação
<a name="ddb-en-client-use-multiop-trans"></a>

A API do Cliente Aprimorado do DynamoDB fornece o `transactGetItems()` e os métodos `transactWriteItems()`. Os métodos de transação do SDK para Java fornecem atomicidade, consistência, isolamento e durabilidade (ACID) nas tabelas do DynamoDB, ajudando você a manter a correção dos dados em suas aplicações.

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

O método `[transactGetItems()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#transactGetItems(java.util.function.Consumer))` aceita até 100 solicitações individuais de itens. Todos os itens são lidos em uma única transação atômica. O *Guia do desenvolvedor do Amazon DynamoDB* tem informações sobre as [condições que causam a falha do método `transactGetItems()`](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-txgetitems) e também sobre o nível de isolamento usado ao chamar `[transactGetItem()](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-isolation)`.

Depois da linha de comentário 1 no exemplo a seguir, o código chama o método `transactGetItems()` com um parâmetro `[builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactGetItemsEnhancedRequest.Builder.html)`. O `[addGetItem()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactGetItemsEnhancedRequest.Builder.html#addGetItem(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource,T))` do construtor é chamado três vezes com um objeto de dados contendo os valores-chave que o SDK usará para gerar a solicitação final.

A solicitação retorna uma lista de objetos `[Document](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Document.html)` após a linha de comentário 2. A lista de documentos que é retornada contém instâncias de [Documentos](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Document.html) não nulas dos dados do item na mesma ordem solicitada. O método `[Document.getItem(MappedTableResource<T> mappedTableResource)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Document.html#getItem(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource))` converte um objeto não digitado em um objeto `Document` Java digitado se os dados do item forem retornados, caso contrário, o método retornará nulo.

```
    public static void transactGetItemsExample(DynamoDbEnhancedClient enhancedClient,
                                               DynamoDbTable<ProductCatalog> catalogTable,
                                               DynamoDbTable<MovieActor> movieActorTable) {

        // 1. Request three items from two tables using a builder.
        final List<Document> documents = enhancedClient.transactGetItems(b -> b
                .addGetItem(catalogTable, Key.builder().partitionValue(2).sortValue("Title 55").build())
                .addGetItem(movieActorTable, Key.builder().partitionValue("Sophie's Choice").sortValue("Meryl Streep").build())
                .addGetItem(movieActorTable, Key.builder().partitionValue("Blue Jasmine").sortValue("Cate Blanchett").build())
                .build());

        // 2. A list of Document objects is returned in the same order as requested.
        ProductCatalog title55 = documents.get(0).getItem(catalogTable);
        if (title55 != null) {
            logger.info(title55.toString());
        }

        MovieActor sophiesChoice = documents.get(1).getItem(movieActorTable);
        if (sophiesChoice != null) {
            logger.info(sophiesChoice.toString());
        }

        // 3. The getItem() method returns null if the Document object contains no item from DynamoDB.
        MovieActor blueJasmine = documents.get(2).getItem(movieActorTable);
        if (blueJasmine != null) {
            logger.info(blueJasmine.toString());
        }
    }
```

As tabelas do DynamoDB contêm os seguintes itens antes da execução do exemplo de código.

```
ProductCatalog{id=2, title='Title 55', isbn='orig_isbn', authors=[b, g], price=10}
MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
```

A saída a seguir é registrada. Se um item for solicitado, mas não encontrado, ele não será retornado, como é o caso da solicitação do filme nomeado `Blue Jasmine`.

```
ProductCatalog{id=2, title='Title 55', isbn='orig_isbn', authors=[b, g], price=10}
MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
```

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

O `[transactWriteItems()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#transactWriteItems(java.util.function.Consumer))` aceita até 100 ações de colocar, atualizar ou excluir em uma única transação atômica em várias tabelas. O *Guia do desenvolvedor do Amazon DynamoDB *contém detalhes sobre restrições e condições de falha da [operação do serviço subjacente do DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-txwriteitems).

### Exemplo básico
<a name="ddb-en-client-use-multiop-trans-writeitems-basic"></a>

No exemplo a seguir, quatro operações são solicitadas para duas tabelas. As classes de modelo correspondentes [`ProductCatalog`](ddb-en-client-use.md#ddb-en-client-use-compare-cs3) e [`MovieActor`](ddb-en-client-use-multirecord.md#ddb-en-client-use-movieactor-class) foram mostradas anteriormente.

Cada uma das três operações possíveis: colocar, atualizar e excluir usa um parâmetro de solicitação dedicado para especificar os detalhes. 

O código após a linha de comentário 1 mostra a variação simples do método `addPutItem()`. O método aceita um objeto `[MappedTableResource](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/MappedTableResource.html)` e a instância do objeto de dados onde colocar. A declaração após a linha de comentário 2 mostra a variação que aceita uma instância `[TransactPutItemEnhancedRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactPutItemEnhancedRequest.html)`. Essa variação permite adicionar mais opções na solicitação, como uma expressão de condição. Um [exemplo](#ddb-en-client-use-multiop-trans-writeitems-opcondition) subsequente mostra uma expressão de condição para uma operação individual.

Uma operação de atualização é solicitada após a linha de comentários 3. `[TransactUpdateItemEnhancedRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactUpdateItemEnhancedRequest.Builder.html)` tem um método `ignoreNulls()` que permite configurar o que o SDK faz com os valores `null` no objeto modelo. Se o método `ignoreNulls()` retornar true, o SDK não removerá os valores dos atributos da tabela para os atributos do objeto de dados que são `null`. Se o método `ignoreNulls()` retornar false, o SDK solicitará que o serviço do DynamoDB remova os atributos do item na tabela. O valor padrão para `ignoreNulls` é false.

A afirmação após a linha de comentário 4 mostra a variação de uma solicitação de exclusão que usa um objeto de dados. O cliente aprimorado extrai os valores-chave antes de enviar a solicitação final.

```
    public static void transactWriteItems(DynamoDbEnhancedClient enhancedClient,
                                          DynamoDbTable<ProductCatalog> catalogTable,
                                          DynamoDbTable<MovieActor> movieActorTable) {

        enhancedClient.transactWriteItems(b -> b
                // 1. Simplest variation of put item request.
                .addPutItem(catalogTable, getProductCatId2())
                // 2. Put item request variation that accommodates condition expressions.
                .addPutItem(movieActorTable, TransactPutItemEnhancedRequest.builder(MovieActor.class)
                        .item(getMovieActorStreep())
                        .conditionExpression(Expression.builder().expression("attribute_not_exists (movie)").build())
                        .build())
                // 3. Update request that does not remove attribute values on the table if the data object's value is null.
                .addUpdateItem(catalogTable, TransactUpdateItemEnhancedRequest.builder(ProductCatalog.class)
                        .item(getProductCatId4ForUpdate())
                        .ignoreNulls(Boolean.TRUE)
                        .build())
                // 4. Variation of delete request that accepts a data object. The key values are extracted for the request.
                .addDeleteItem(movieActorTable, getMovieActorBlanchett())
        );
    }
```

Os métodos auxiliares a seguir fornecem os objetos de dados para os parâmetros `add*Item`.

#### Métodos auxiliares
<a name="ddb-en-client-use-multiop-trans-writeitems-basic-helpers"></a>

```
    public static ProductCatalog getProductCatId2() {
        return ProductCatalog.builder()
                .id(2)
                .isbn("1-565-85698")
                .authors(new HashSet<>(Arrays.asList("a", "b")))
                .price(BigDecimal.valueOf(30.22))
                .title("Title 55")
                .build();
    }

    public static ProductCatalog getProductCatId4ForUpdate() {
        return ProductCatalog.builder()
                .id(4)
                .price(BigDecimal.valueOf(40.00))
                .title("Title 1")
                .build();
    }

    public static MovieActor getMovieActorBlanchett() {
        MovieActor movieActor = new MovieActor();
        movieActor.setActorName("Cate Blanchett");
        movieActor.setMovieName("Tar");
        movieActor.setActingYear(2022);
        movieActor.setActingAward("Best Actress");
        movieActor.setActingSchoolName("National Institute of Dramatic Art");
        return movieActor;
    }

    public static MovieActor getMovieActorStreep() {
        MovieActor movieActor = new MovieActor();
        movieActor.setActorName("Meryl Streep");
        movieActor.setMovieName("Sophie's Choice");
        movieActor.setActingYear(1982);
        movieActor.setActingAward("Best Actress");
        movieActor.setActingSchoolName("Yale School of Drama");
        return movieActor;
    }
```

As tabelas do DynamoDB contêm os seguintes itens antes da execução do exemplo de código.

```
1 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
2 | MovieActor{movieName='Tar', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2022, actingSchoolName='National Institute of Dramatic Art'}
```

Os itens a seguir estão nas tabelas após a conclusão da execução do código.

```
3 | ProductCatalog{id=2, title='Title 55', isbn='1-565-85698', authors=[a, b], price=30.22}
4 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=40.0}
5 | MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
```

O item na linha 2 foi excluído e as linhas 3 e 5 mostram os itens que foram colocados. A linha 4 mostra a atualização da linha 1. O valor `price` é o único valor que foi alterado no item. Se `ignoreNulls()` tivesse retornado false, a linha 4 ficaria como a seguinte linha:

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

### Exemplo de verificação de condição
<a name="ddb-en-client-use-multiop-trans-writeitems-checkcond"></a>

O exemplo a seguir mostra o uso de uma verificação de condição. Uma verificação de condição é usada para verificar se um item existe ou para verificar a condição de atributos específicos de um item no banco de dados. O item verificado na verificação de condição não pode ser usado em outra operação na transação.

**nota**  
Você não pode visar o mesmo item com várias operações dentro da mesma transação. Por exemplo, você não pode executar uma verificação de condição e também tentar atualizar o item na mesma transação.

O exemplo mostra um tipo de cada operação em uma solicitação transacional de itens de gravação. Depois da linha de comentário 2, o método `addConditionCheck()` fornece a condição que falha na transação se o parâmetro `conditionExpression` for avaliado como `false`. A expressão de condição que é retornada do método mostrado no bloco de métodos auxiliares verifica se o ano de premiação do filme `Sophie's Choice` não é igual a `1982`. Se for igual, a expressão será avaliada como `false` e a transação falhará.

Este guia discutirá [expressões](ddb-en-client-expressions.md) em profundidade em outro tópico.

```
    public static void conditionCheckFailExample(DynamoDbEnhancedClient enhancedClient,
                                                 DynamoDbTable<ProductCatalog> catalogTable,
                                                 DynamoDbTable<MovieActor> movieActorTable) {

        try {
            enhancedClient.transactWriteItems(b -> b
                    // 1. Perform one of each type of operation with the next three methods.
                    .addPutItem(catalogTable, TransactPutItemEnhancedRequest.builder(ProductCatalog.class)
                            .item(getProductCatId2()).build())
                    .addUpdateItem(catalogTable, TransactUpdateItemEnhancedRequest.builder(ProductCatalog.class)
                            .item(getProductCatId4ForUpdate())
                            .ignoreNulls(Boolean.TRUE).build())
                    .addDeleteItem(movieActorTable, TransactDeleteItemEnhancedRequest.builder()
                            .key(b1 -> b1
                                    .partitionValue(getMovieActorBlanchett().getMovieName())
                                    .sortValue(getMovieActorBlanchett().getActorName())).build())
                    // 2. Add a condition check on a table item that is not involved in another operation in this request.
                    .addConditionCheck(movieActorTable, ConditionCheck.builder()
                            .conditionExpression(buildConditionCheckExpression())
                            .key(k -> k
                                    .partitionValue("Sophie's Choice")
                                    .sortValue("Meryl Streep"))
                            // 3. Specify the request to return existing values from the item if the condition evaluates to true.
                            .returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD)
                            .build())
                    .build());
        // 4. Catch the exception if the transaction fails and log the information.
        } catch (TransactionCanceledException ex) {
            ex.cancellationReasons().stream().forEach(cancellationReason -> {
                logger.info(cancellationReason.toString());
            });
        }
    }
```

Os métodos auxiliares a seguir são usados no exemplo de código anterior.

#### Métodos auxiliares
<a name="ddb-en-client-use-multiop-trans-writeitems-checkcond-helpers"></a>

```
    private static Expression buildConditionCheckExpression() {
        Map<String, AttributeValue> expressionValue = Map.of(
                ":year", numberValue(1982));

        return Expression.builder()
                .expression("actingyear <> :year")
                .expressionValues(expressionValue)
                .build();
    }

    public static ProductCatalog getProductCatId2() {
        return ProductCatalog.builder()
                .id(2)
                .isbn("1-565-85698")
                .authors(new HashSet<>(Arrays.asList("a", "b")))
                .price(BigDecimal.valueOf(30.22))
                .title("Title 55")
                .build();
    }

    public static ProductCatalog getProductCatId4ForUpdate() {
        return ProductCatalog.builder()
                .id(4)
                .price(BigDecimal.valueOf(40.00))
                .title("Title 1")
                .build();
    }

    public static MovieActor getMovieActorBlanchett() {
        MovieActor movieActor = new MovieActor();
        movieActor.setActorName("Cate Blanchett");
        movieActor.setMovieName("Blue Jasmine");
        movieActor.setActingYear(2013);
        movieActor.setActingAward("Best Actress");
        movieActor.setActingSchoolName("National Institute of Dramatic Art");
        return movieActor;
    }
```

As tabelas do DynamoDB contêm os seguintes itens antes da execução do exemplo de código.

```
1 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
2 | MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
3 | MovieActor{movieName='Tar', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2022, actingSchoolName='National Institute of Dramatic Art'}
```

Os itens a seguir estão nas tabelas após a conclusão da execução do código.

```
ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
MovieActor{movieName='Sophie's Choice', actorName='Meryl Streep', actingAward='Best Actress', actingYear=1982, actingSchoolName='Yale School of Drama'}
MovieActor{movieName='Tar', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2022, actingSchoolName='National Institute of Dramatic Art'}
```

Os itens permanecem inalterados nas tabelas porque a transação falhou. O valor `actingYear` do filme `Sophie's Choice` é `1982`, conforme mostrado na linha 2 dos itens na tabela antes da chamada do método `transactWriteItem()`.

Para capturar as informações de cancelamento da transação, inclua a chamada do método `transactWriteItems()` em um bloco `try` e `catch` o [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/TransactionCanceledException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/TransactionCanceledException.html). Depois da linha de comentário 4 do exemplo, o código registra cada objeto `[CancellationReason](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/CancellationReason.html)`. Como o código após a linha de comentário 3 do exemplo especifica que os valores devem ser retornados para o item que causou a falha da transação, o registro exibe os valores brutos do banco de dados do item do filme `Sophie's Choice`.

```
CancellationReason(Code=None)
CancellationReason(Code=None)
CancellationReason(Code=None)
CancellationReason(Item={actor=AttributeValue(S=Meryl Streep), movie=AttributeValue(S=Sophie's Choice), actingaward=AttributeValue(S=Best Actress), actingyear=AttributeValue(N=1982), actingschoolname=AttributeValue(S=Yale School of Drama)}, ¬
    Code=ConditionalCheckFailed, Message=The conditional request failed.)
```

### Exemplo de condição de operação única
<a name="ddb-en-client-use-multiop-trans-writeitems-opcondition"></a>

O exemplo a seguir mostra o uso de uma condição em uma única operação em uma solicitação de transação. A operação de exclusão após a linha de comentário 1 contém uma condição que verifica o valor do item de destino da operação em relação ao banco de dados. Neste exemplo, a expressão de condição criada com o método auxiliar após a linha de comentário 2 especifica que o item deve ser excluído do banco de dados se o ano de atuação do filme não for igual a 2013.

As [expressões](ddb-en-client-expressions.md) serão discutidas posteriormente neste guia.

```
    public static void singleOperationConditionFailExample(DynamoDbEnhancedClient enhancedClient,
                                                           DynamoDbTable<ProductCatalog> catalogTable,
                                                           DynamoDbTable<MovieActor> movieActorTable) {
        try {
            enhancedClient.transactWriteItems(b -> b
                    .addPutItem(catalogTable, TransactPutItemEnhancedRequest.builder(ProductCatalog.class)
                            .item(getProductCatId2())
                            .build())
                    .addUpdateItem(catalogTable, TransactUpdateItemEnhancedRequest.builder(ProductCatalog.class)
                            .item(getProductCatId4ForUpdate())
                            .ignoreNulls(Boolean.TRUE).build())
                    // 1. Delete operation that contains a condition expression
                    .addDeleteItem(movieActorTable, TransactDeleteItemEnhancedRequest.builder()
                            .key((Key.Builder k) -> {
                                MovieActor blanchett = getMovieActorBlanchett();
                                k.partitionValue(blanchett.getMovieName())
                                        .sortValue(blanchett.getActorName());
                            })
                            .conditionExpression(buildDeleteItemExpression())
                            .returnValuesOnConditionCheckFailure(ReturnValuesOnConditionCheckFailure.ALL_OLD)
                            .build())
                    .build());
        } catch (TransactionCanceledException ex) {
            ex.cancellationReasons().forEach(cancellationReason -> logger.info(cancellationReason.toString()));
        }
    }

    // 2. Provide condition expression to check if 'actingyear' is not equal to 2013.
    private static Expression buildDeleteItemExpression() {
        Map<String, AttributeValue> expressionValue = Map.of(
                ":year", numberValue(2013));

        return Expression.builder()
                .expression("actingyear <> :year")
                .expressionValues(expressionValue)
                .build();
    }
```

Os métodos auxiliares a seguir são usados no exemplo de código anterior.

#### Métodos auxiliares
<a name="ddb-en-client-use-multiop-trans-writeitems-opcondition-helpers"></a>

```
    public static ProductCatalog getProductCatId2() {
        return ProductCatalog.builder()
                .id(2)
                .isbn("1-565-85698")
                .authors(new HashSet<>(Arrays.asList("a", "b")))
                .price(BigDecimal.valueOf(30.22))
                .title("Title 55")
                .build();
    }

    public static ProductCatalog getProductCatId4ForUpdate() {
        return ProductCatalog.builder()
                .id(4)
                .price(BigDecimal.valueOf(40.00))
                .title("Title 1")
                .build();
    }
    public static MovieActor getMovieActorBlanchett() {
        MovieActor movieActor = new MovieActor();
        movieActor.setActorName("Cate Blanchett");
        movieActor.setMovieName("Blue Jasmine");
        movieActor.setActingYear(2013);
        movieActor.setActingAward("Best Actress");
        movieActor.setActingSchoolName("National Institute of Dramatic Art");
        return movieActor;
    }
```

As tabelas do DynamoDB contêm os seguintes itens antes da execução do exemplo de código.

```
1 | ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
2 | MovieActor{movieName='Blue Jasmine', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2013, actingSchoolName='National Institute of Dramatic Art'}
```

Os itens a seguir estão nas tabelas após a conclusão da execução do código.

```
ProductCatalog{id=4, title='Title 1', isbn='orig_isbn', authors=[b, g], price=10}
2023-03-15 11:29:07 [main] INFO  org.example.tests.TransactDemoTest:168 - MovieActor{movieName='Blue Jasmine', actorName='Cate Blanchett', actingAward='Best Actress', actingYear=2013, actingSchoolName='National Institute of Dramatic Art'}
```

Os itens permanecem inalterados nas tabelas porque a transação falhou. O valor `actingYear` do filme `Blue Jasmine` é `2013` como mostrado na linha 2 da lista de itens antes da execução do código de exemplo.

As linhas a seguir são registradas no console.

```
CancellationReason(Code=None)
CancellationReason(Code=None)
CancellationReason(Item={actor=AttributeValue(S=Cate Blanchett), movie=AttributeValue(S=Blue Jasmine), actingaward=AttributeValue(S=Best Actress), actingyear=AttributeValue(N=2013), actingschoolname=AttributeValue(S=National Institute of Dramatic Art)}, 
    Code=ConditionalCheckFailed, Message=The conditional request failed)
```

# Usar índices secundários
<a name="ddb-en-client-use-secindex"></a>

Os índices secundários melhoram o acesso aos dados definindo chaves alternativas que você usa nas operações de consulta e verificação. Índices secundários globais (GSI) têm uma chave de partição e uma chave de classificação que podem ser diferentes daquelas contidas na tabela base. Por outro lado, os índices secundários locais (LSI) usam a chave de partição do índice primário.

## Anotar a classe de dados com anotações de índice secundário
<a name="ddb-en-client-use-secindex-annomodel"></a>

Os atributos que participam de índices secundários exigem a anotação `@DynamoDbSecondaryPartitionKey` ou `@DynamoDbSecondarySortKey`.

A classe a seguir mostra anotações para dois índices. O GSI nomeado *SubjectLastPostedDateIndex*usa o `Subject` atributo para a chave de partição e o `LastPostedDateTime` para a chave de classificação. O LSI nomeado *ForumLastPostedDateIndex*usa o `ForumName` como chave de partição e `LastPostedDateTime` como chave de classificação.

Observe que o atributo `Subject` tem uma função dupla. É a chave de classificação da chave primária e a chave de partição do GSI nomeado *SubjectLastPostedDateIndex*.

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

A classe `MessageThread` é adequada para ser usada como uma classe de dados para a [tabela Thread de exemplo](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AppendixSampleTables.html) no *Guia de desenvolvedor do Amazon DynamoDB*.

#### Importações
<a name="ddb-en-client-use-secindex-classimports"></a>

```
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondaryPartitionKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSecondarySortKey;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbSortKey;

import java.util.List;
```

```
@DynamoDbBean
public class MessageThread {
    private String ForumName;
    private String Subject;
    private String Message;
    private String LastPostedBy;
    private String LastPostedDateTime;
    private Integer Views;
    private Integer Replies;
    private Integer Answered;
    private List<String> Tags;

    @DynamoDbPartitionKey
    public String getForumName() {
        return ForumName;
    }

    public void setForumName(String forumName) {
        ForumName = forumName;
    }

    // Sort key for primary index and partition key for GSI "SubjectLastPostedDateIndex".
    @DynamoDbSortKey
    @DynamoDbSecondaryPartitionKey(indexNames = "SubjectLastPostedDateIndex")
    public String getSubject() {
        return Subject;
    }

    public void setSubject(String subject) {
        Subject = subject;
    }

    // Sort key for GSI "SubjectLastPostedDateIndex" and sort key for LSI "ForumLastPostedDateIndex".
    @DynamoDbSecondarySortKey(indexNames = {"SubjectLastPostedDateIndex", "ForumLastPostedDateIndex"})
    public String getLastPostedDateTime() {
        return LastPostedDateTime;
    }

    public void setLastPostedDateTime(String lastPostedDateTime) {
        LastPostedDateTime = lastPostedDateTime;
    }
    public String getMessage() {
        return Message;
    }

    public void setMessage(String message) {
        Message = message;
    }

    public String getLastPostedBy() {
        return LastPostedBy;
    }

    public void setLastPostedBy(String lastPostedBy) {
        LastPostedBy = lastPostedBy;
    }

    public Integer getViews() {
        return Views;
    }

    public void setViews(Integer views) {
        Views = views;
    }

    @DynamoDbSecondaryPartitionKey(indexNames = "ForumRepliesIndex")
    public Integer getReplies() {
        return Replies;
    }

    public void setReplies(Integer replies) {
        Replies = replies;
    }

    public Integer getAnswered() {
        return Answered;
    }

    public void setAnswered(Integer answered) {
        Answered = answered;
    }

    public List<String> getTags() {
        return Tags;
    }

    public void setTags(List<String> tags) {
        Tags = tags;
    }

    public MessageThread() {
        this.Answered = 0;
        this.LastPostedBy = "";
        this.ForumName = "";
        this.Message = "";
        this.LastPostedDateTime = "";
        this.Replies = 0;
        this.Views = 0;
        this.Subject = "";
    }

    @Override
    public String toString() {
        return "MessageThread{" +
                "ForumName='" + ForumName + '\'' +
                ", Subject='" + Subject + '\'' +
                ", Message='" + Message + '\'' +
                ", LastPostedBy='" + LastPostedBy + '\'' +
                ", LastPostedDateTime='" + LastPostedDateTime + '\'' +
                ", Views=" + Views +
                ", Replies=" + Replies +
                ", Answered=" + Answered +
                ", Tags=" + Tags +
                '}';
    }
}
```

## Criar o índice
<a name="ddb-en-client-use-secindex-confindex"></a>

A partir da versão 2.20.86 do SDK para Java, o método `createTable()` gera automaticamente índices secundários a partir de anotações de classes de dados. Por padrão, todos os atributos da tabela base são copiados para um índice e os valores de throughput provisionados são 20 unidades de capacidade de leitura e 20 unidades de capacidade de gravação.

No entanto, se você usar uma versão do SDK anterior à 2.20.86, precisará criar o índice junto com a tabela, conforme mostrado no exemplo a seguir. Este exemplo cria os dois índices para a tabela `Thread`. O parâmetro [builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/CreateTableEnhancedRequest.Builder.html) tem métodos para configurar os dois tipos de índices, conforme mostrado após as linhas de comentário 1 e 2. Você usa o método `indexName()` do construtor de índices para associar os nomes de índice especificados nas anotações da classe de dados ao tipo de índice pretendido.

Esse código configura todos os atributos da tabela para terminam nos dois índices após as linhas de comentário 3 e 4. Mais informações sobre [projeções de atributos](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSI.html#LSI.Projections) estão disponíveis no *Guia do desenvolvedor do Amazon DynamoDB*.

```
    public static void createMessageThreadTable(DynamoDbTable<MessageThread> messageThreadDynamoDbTable, DynamoDbClient dynamoDbClient) {
        messageThreadDynamoDbTable.createTable(b -> b
                // 1. Generate the GSI.
                .globalSecondaryIndices(gsi -> gsi.indexName("SubjectLastPostedDateIndex")
                        // 3. Populate the GSI with all attributes.
                        .projection(p -> p
                                .projectionType(ProjectionType.ALL))
                )
                // 2. Generate the LSI.
                .localSecondaryIndices(lsi -> lsi.indexName("ForumLastPostedDateIndex")
                        // 4. Populate the LSI with all attributes.
                        .projection(p -> p
                                .projectionType(ProjectionType.ALL))
                )
        );
```

## Consultar usando um índice
<a name="ddb-en-client-use-secindex-query"></a>

O exemplo a seguir consulta o índice secundário local *ForumLastPostedDateIndex*.

Seguindo a linha de comentário 2, você cria um [QueryConditional](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html)objeto que é necessário ao chamar o método [DynamoDbIndex.query ()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbIndex.html#query(java.util.function.Consumer)). 

Você obtém uma referência ao índice que deseja consultar após a linha de comentário 3 ao passar o nome do índice. Seguindo a linha de comentário 4, você chama o método `query()` no índice que passa o objeto `QueryConditional`. 

Você também configura a consulta para retornar três valores de atributos, conforme mostrado após a linha de comentário 5. Se `attributesToProject()` não for chamado, a consulta retornará todos os valores dos atributos. Observe que os nomes dos atributos especificados começam com letras minúsculas. Esses nomes de atributos correspondem aos usados na tabela, não necessariamente aos nomes dos atributos da classe de dados.

Seguindo a linha de comentário 6, revise os resultados e registre cada item retornado pela consulta e os armazene na lista para retornar ao chamador.

```
public class IndexScanExamples {
    private static Logger logger = LoggerFactory.getLogger(IndexScanExamples.class);

    public static List<MessageThread> queryUsingSecondaryIndices(String lastPostedDate,
                                                                 DynamoDbTable<MessageThread> threadTable) {
        // 1. Log the parameter value.
        logger.info("lastPostedDate value: {}", lastPostedDate);

        // 2. Create a QueryConditional whose sort key value must be greater than or equal to the parameter value.
        QueryConditional queryConditional = QueryConditional.sortGreaterThanOrEqualTo(qc ->
                qc.partitionValue("Forum02").sortValue(lastPostedDate));

        // 3. Specify the index name to query.
        final DynamoDbIndex<MessageThread> forumLastPostedDateIndex = threadTable.index("ForumLastPostedDateIndex");

        // 4. Perform the query using the QueryConditional object.
        final SdkIterable<Page<MessageThread>> pagedResult = forumLastPostedDateIndex.query(q -> q
                .queryConditional(queryConditional)
                // 5. Request three attribute in the results.
                .attributesToProject("forumName", "subject", "lastPostedDateTime"));

        List<MessageThread> collectedItems = new ArrayList<>();
        // 6. Iterate through pages response and sort the items.
        pagedResult.stream().forEach(page -> page.items().stream()
                .sorted(Comparator.comparing(MessageThread::getLastPostedDateTime))
                .forEach(mt -> {
                    // 7. Log the returned items and add the collection to return to the caller.
                    logger.info(mt.toString());
                    collectedItems.add(mt);
                }));
        return collectedItems;
    }
```

Os itens a seguir existem no banco de dados antes da execução da consulta.

```
MessageThread{ForumName='Forum01', Subject='Subject01', Message='Message01', LastPostedBy='', LastPostedDateTime='2023.03.28', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum02', Subject='Subject02', Message='Message02', LastPostedBy='', LastPostedDateTime='2023.03.29', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum02', Subject='Subject04', Message='Message04', LastPostedBy='', LastPostedDateTime='2023.03.31', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum02', Subject='Subject08', Message='Message08', LastPostedBy='', LastPostedDateTime='2023.04.04', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum02', Subject='Subject10', Message='Message10', LastPostedBy='', LastPostedDateTime='2023.04.06', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum03', Subject='Subject03', Message='Message03', LastPostedBy='', LastPostedDateTime='2023.03.30', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum03', Subject='Subject06', Message='Message06', LastPostedBy='', LastPostedDateTime='2023.04.02', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum03', Subject='Subject09', Message='Message09', LastPostedBy='', LastPostedDateTime='2023.04.05', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum05', Subject='Subject05', Message='Message05', LastPostedBy='', LastPostedDateTime='2023.04.01', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum07', Subject='Subject07', Message='Message07', LastPostedBy='', LastPostedDateTime='2023.04.03', Views=0, Replies=0, Answered=0, Tags=null}
```

As instruções de registro nas linhas 1 e 6 resultam na seguinte saída do console:

```
lastPostedDate value: 2023.03.31
MessageThread{ForumName='Forum02', Subject='Subject04', Message='', LastPostedBy='', LastPostedDateTime='2023.03.31', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum02', Subject='Subject08', Message='', LastPostedBy='', LastPostedDateTime='2023.04.04', Views=0, Replies=0, Answered=0, Tags=null}
MessageThread{ForumName='Forum02', Subject='Subject10', Message='', LastPostedBy='', LastPostedDateTime='2023.04.06', Views=0, Replies=0, Answered=0, Tags=null}
```

A consulta retornou itens com um valor `forumName` de *Forum02* e um valor `lastPostedDateTime` maior ou igual a *2023.03.31*. Os resultados mostram valores `message` com uma cadeia de caracteres vazia, embora os atributos `message` tenham valores no índice. Isso ocorre porque o atributo da mensagem não foi projetado pelo código após a linha de comentário 5. 

# Usar recursos avançados de mapeamento
<a name="ddb-en-client-adv-features"></a>

Saiba mais sobre os atributos avançados do esquema de tabelas na API do Cliente Aprimorado do DynamoDB.

## Entender os tipos de esquema de tabela
<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)` é a interface para a funcionalidade de mapeamento da API do Cliente Aprimorado do DynamoDB. Ele pode mapear um objeto de dados de e para um mapa de [AttributeValues](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/AttributeValue.html). Um objeto `TableSchema` precisa conhecer a estrutura da tabela que está mapeando. Essas informações de estrutura são armazenadas em um objeto [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).

A API de cliente aprimorado tem várias implementações de `TableSchema`, conforme será visto a seguir. 

### Esquema de tabela gerado a partir de classes anotadas
<a name="ddb-en-client-adv-features-schema-mapped"></a>

Criar uma `TableSchema` a partir de classes anotadas é uma operação moderadamente dispendiosa, portanto, recomendamos fazer isso uma vez, na inicialização do aplicativo.

 [ BeanTableSchema ](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/BeanTableSchema.html)   
Essa implementação é construída com base nos atributos e anotações de uma classe de bean. Um exemplo dessa abordagem é demonstrado na [seção Conceitos básicos](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean).  
Se uma `BeanTableSchema` não estiver se comportando conforme o esperado, habilite o registro de depuração para `software.amazon.awssdk.enhanced.dynamodb.beans`.

[ImmutableTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/ImmutableTableSchema.html)  
Essa implementação é criada a partir de uma classe de dados imutável. Esse método é descrito na seção [Trabalhar com classes de dados imutáveis](ddb-en-client-use-immut.md).

### Esquema de tabela gerado com um construtor
<a name="ddb-en-client-adv-features-schema-static"></a>

As seguintes `TableSchema`s são criadas a partir do código usando um construtor. Essa abordagem é menos dispendiosa do que a abordagem que usa classes de dados anotadas. A abordagem do construtor evita o uso de anotações e não exige JavaBean padrões de nomenclatura.

[StaticTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticTableSchema.html)  
Essa implementação foi criada para classes de dados mutáveis. A seção de introdução deste guia demonstrou como [gerar uma `StaticTableSchema` usando um construtor](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)  
Da mesma forma que você cria um `StaticTableSchema`, você gera uma implementação desse tipo de `TableSchema` usando um [construtor](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticImmutableTableSchema.html) para uso com classes de dados imutáveis.

### Esquema de tabela para dados sem um esquema fixo
<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)  
Ao contrário de outras implementações de `TableSchema`, você não define atributos para uma instância `DocumentTableSchema`. Normalmente, você especifica somente chaves primárias e provedores de conversão de atributos. Uma instância `EnhancedDocument` fornece os atributos que você cria a partir de elementos individuais ou de uma cadeia de caracteres JSON.

# Inclua ou exclua atributos explicitamente
<a name="ddb-en-client-adv-features-inex-attr"></a>

A API do Cliente Aprimorado do DynamoDB oferece anotações para impedir que atributos da classe de dados se tornem atributos em uma tabela. Com a API, você também pode usar um nome de atributo diferente do nome do atributo da classe de dados.

## Excluir atributos
<a name="ddb-en-client-adv-features-inex-attr-ex"></a>

Para ignorar atributos que não devem ser mapeados para uma tabela do DynamoDB, marque o atributo com a anotação `@DynamoDbIgnore`.

```
private String internalKey;

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

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

Para alterar o nome de um atributo usado na tabela do DynamoDB, marque-o com a anotação `@DynamoDbAttribute` e forneça um nome diferente.

```
private String internalKey;

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

# Conversão de atributo de controle
<a name="ddb-en-client-adv-features-conversion"></a>

Por padrão, um esquema de tabela fornece conversores para muitos tipos comuns de Java por meio de uma implementação padrão da interface `[AttributeConverterProvider](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverterProvider.html)`. Você pode alterar o comportamento padrão geral com uma implementação `AttributeConverterProvider` personalizada. Você também pode alterar o conversor para um único atributo.

Para obter uma lista dos conversores disponíveis, consulte a [AttributeConverter](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverter.html)interface Java doc.

## Forneça provedores de conversão de atributos personalizados
<a name="ddb-en-client-adv-features-conversion-prov"></a>

Você pode fornecer um único `AttributeConverterProvider` ou uma cadeia de `AttributeConverterProvider`s ordenados por meio da anotação `@DynamoDbBean` `(converterProviders = {…})`. Qualquer personalização do `AttributeConverterProvider` deve estender a interface do `AttributeConverterProvider`.

Observe que, se fornecer sua própria cadeia de provedores de conversão de atributos, você substituirá o provedor de conversão padrão, `DefaultAttributeConverterProvider`. Se quiser usar a funcionalidade do `DefaultAttributeConverterProvider`, você deverá incluí-la na cadeia. 

Também é possível anotar o bean com uma matriz vazia `{}`. Isso desativa o uso de qualquer provedor de conversão de atributos, incluindo o padrão. Nesse caso, todos os atributos a serem mapeados devem ter seu próprio conversor de atributos.

O trecho a seguir mostra um único provedor de conversor.

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

}
```

O trecho a seguir mostra o uso de uma cadeia de provedores de conversores. Como o SDK padrão é fornecido por último, ele tem a menor prioridade.

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

}
```

Os criadores de esquemas de tabelas estáticas têm um método `attributeConverterProviders()` que funciona da mesma maneira. Isso é mostrado no trecho a seguir.

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

## Substituir o mapeamento de um único atributo
<a name="ddb-en-client-adv-features-conversion-single"></a>

Para substituir a forma como um único atributo é mapeado, forneça um `AttributeConverter` para o atributo. Essa adição substitui todos os conversores fornecidos pelo `AttributeConverterProviders` no esquema da tabela. A ação adiciona um conversor personalizado somente para esse atributo. Outros atributos, mesmo aqueles do mesmo tipo, não usarão esse conversor, a menos que ele seja explicitamente especificado para esses outros atributos.

A anotação `@DynamoDbConvertedBy` é usada para especificar a classe `AttributeConverter` personalizada, conforme mostrado no trecho a seguir.

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

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

Os construtores de esquemas estáticos têm um método construtor de atributos `attributeConverter()` equivalente. Esse método usa uma instância de um `AttributeConverter`, conforme mostrado a seguir.

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

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

Este exemplo mostra uma implementação `AttributeConverterProvider` que fornece um conversor de atributos para objetos [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). 

A classe `SimpleUser` a seguir contém um atributo chamado `lastUsedCookie` que é uma instância de `HttpCookie`.

O parâmetro para as anotações `@DynamoDbBean` lista as duas classes `AttributeConverterProvider` que fornecem conversores.

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

------

O `CookieConverterProvider` no exemplo a seguir fornece uma instância de um `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);
        }
    }
```

### Código de conversão
<a name="ddb-en-client-adv-features-conversion-example-code"></a>

No método `transformFrom()` da classe `HttpCookieConverter` a seguir, o código recebe uma instância `HttpCookie` e a transforma em um mapa do DynamoDB que é armazenado como um atributo.

O método `transformTo()` recebe um parâmetro de mapa do DynamoDB e, em seguida, invoca o construtor `HttpCookie` que exige um nome e um valor.

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

# Alterar o comportamento de atualização dos atributos
<a name="ddb-en-client-adv-features-upd-behavior"></a>

Você pode personalizar o comportamento de atualização de atributos individuais ao realizar uma operação de *atualização*. [Alguns exemplos de operações de atualização na API de cliente aprimorada do DynamoDB [são 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))

Por exemplo, imagine que você queira armazenar um carimbo de data/hora *criado em* seu registro. No entanto, você deseja que o valor seja gravado somente se ainda não houver nenhum valor existente para o atributo no banco de dados. Nesse caso, você usa o comportamento de atualização `[WRITE\$1IF\$1NOT\$1EXISTS](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/UpdateBehavior.html#WRITE_IF_NOT_EXISTS)`.

O exemplo a seguir mostra a anotação que adiciona o comportamento ao atributo `createdOn`.

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

Você pode declarar o mesmo comportamento de atualização ao criar um esquema de tabela estática, conforme mostrado no exemplo a seguir após a linha de comentário 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();
```

# Nivelar atributos de outras classes
<a name="ddb-en-client-adv-features-flatmap"></a>

Se os atributos da sua tabela estiverem espalhados por várias classes Java diferentes, seja por herança ou composição, a API do Cliente Aprimorado do DynamoDB fornece suporte para nivelar os atributos em uma classe.

## Usar herança
<a name="ddb-en-client-adv-features-flatmap-inheritance"></a>

Se suas classes usam herança, use as seguintes abordagens para nivelar a hierarquia.

### Usar beans anotados
<a name="ddb-en-client-adv-features-flatmap-inheritance-anno"></a>

Para a abordagem de anotação, ambas as classes devem conter a anotação `@DynamoDbBean` e uma classe deve conter uma ou mais anotações de chave primária.

Veja a seguir exemplos de classes de dados que têm uma relação de herança.

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

A [opção `onMethod`](https://projectlombok.org/features/experimental/onX) do Lombok copia anotações do DynamoDB baseadas em atributos, como `@DynamoDbPartitionKey`, no código gerado.

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

------

### Usar esquemas estáticos
<a name="ddb-en-client-adv-features-flatmap-inheritance-static"></a>

Para a abordagem do esquema estático, use o método `extend()` do construtor para reduzir os atributos da classe principal para a classe secundária. Isso é mostrado depois da linha de comentário 1 no seguinte exemplo:

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

O exemplo anterior do esquema estático usa as seguintes classes de dados: Uma vez que o mapeamento é definido quando você cria o esquema de tabela estática, as classes de dados não exigem anotações.

#### Classes de dados
<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;
}
```

------

## Usar composição
<a name="ddb-en-client-adv-features-flatmap-comp"></a>

Se suas classes usam composição, use as seguintes abordagens para nivelar a hierarquia.

### Usar beans anotados
<a name="ddb-en-client-adv-features-flatmap-comp-anno"></a>

A anotação `@DynamoDbFlatten` nivela a classe contida.

Os exemplos de classes de dados a seguir usam a anotação `@DynamoDbFlatten` para adicionar efetivamente todos os atributos da classe `GenericRecord` contida à 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;
}
```

------

Você pode usar a anotação de nivelamento para nivelar quantas classes elegíveis forem necessárias. As limitações a seguir aplicam-se:
+ Todos os nomes de atributos devem ser exclusivos depois de serem nivelados.
+ Nunca deve haver mais de uma chave de partição, chave de classificação ou nome de tabela.

### Usar esquemas estáticos
<a name="ddb-en-client-adv-features-flatmap-comp-static"></a>

Ao criar um esquema de tabela estático, use o método `flatten()` do construtor. Você também fornece os métodos getter e setter que identificam a classe contida.

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

O exemplo anterior do esquema estático usa as seguintes classes de dados:

#### Classes de dados
<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;
}
```

------

Você pode usar o padrão do construtor para nivelar quantas classes elegíveis forem necessárias.

## Implicações para outro código
<a name="ddb-en-client-adv-features-flatmap-compare"></a>

Quando você usa o atributo `@DynamoDbFlatten` (ou método `flatten()` do construtor), o item no DynamoDB contém um atributo para cada atributo do objeto composto. Isso também inclui os atributos do objeto de composição. 

Por outro lado, se você anotar uma classe de dados com uma classe composta e não usar `@DynamoDbFlatten`, o item será salvo com o objeto composto como um único atributo.

Por exemplo, compare a classe `Customer` mostrada no [exemplo de nivelamento com composição](#ddb-en-client-adv-features-flatmap-comp-anno) com e sem nivelamento do atributo `record`. Você pode visualizar a diferença com JSON conforme mostrado na tabela a seguir.


****  

| Com nivelamento | Sem nivelamento | 
| --- | --- | 
| 3 atributos | 2 atributos | 
|  <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>  | 

A diferença se torna importante se você tiver outro código acessando a tabela do DynamoDB que espera encontrar certos atributos.

# Trabalhar com atributos que são beans, mapas, listas e conjuntos
<a name="ddb-en-client-adv-features-nested"></a>

Uma definição de bean, como a classe `Person` mostrada abaixo, pode definir propriedades (ou atributos) que se referem a tipos com atributos adicionais. Por exemplo, na classe `Person`, `mainAddress` é uma propriedade que se refere a um bean `Address` que define atributos de valor adicionais. `addresses` refere-se a um mapa Java, cujos elementos se referem a beans `Address`. Esses tipos complexos podem ser considerados contêineres de atributos simples que você usa como valor de dados no contexto do DynamoDB. 

O DynamoDB se refere às propriedades de valor de elementos aninhados, como mapas, listas ou beans, como *atributos aninhados*. O [Guia do desenvolvedor do Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes) se refere à forma salva de um mapa, lista ou bean Java como um *tipo de documento*. Os atributos simples que você usa como valor de dados em Java são chamados de *tipos escalares* no DynamoDB. Conjuntos, que contêm vários elementos escalares do mesmo tipo e chamados de *tipos de conjuntos*. 

É importante saber que a API do Cliente Aprimorado do DynamoDB converte uma propriedade que é bean em um tipo de documento de mapa do DynamoDB quando é salva.

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

## Salvar tipos complexos
<a name="ddb-en-client-adv-features-nested-mapping"></a>

### Usar classes de dados anotadas
<a name="ddb-en-client-adv-features-nested-map-anno"></a>

Salve atributos aninhados para classes personalizadas simplesmente fazendo anotações neles. A classe `Address` e a classe `PhoneNumber` mostradas anteriormente são anotadas somente com a anotação `@DynamoDbBean`. Quando a API do Cliente Aprimorado do DynamoDB cria o esquema de tabela para a classe `Person` com o seguinte trecho, a API descobre o uso das classes `Address` e `PhoneNumber` e cria os mapeamentos correspondentes para funcionar com o DynamoDB.

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

### Usar esquemas abstratos com compiladores
<a name="ddb-en-client-adv-features-nested-map-builder"></a>

A abordagem alternativa é usar compiladores de esquemas de tabelas estáticas para cada classe de bean aninhada, conforme mostrado no código a seguir.

Os esquemas de tabela das classes `Address` e `PhoneNumber` são abstratos, uma vez que não podem ser usados com uma tabela do DynamoDB. Isso ocorre porque eles não têm definições para a chave primária. No entanto, eles são usados como esquemas aninhados no esquema de tabela da classe `Person`.

Depois de comentar as linhas 1 e 2 na definição de `PERSON_TABLE_SCHEMA`, você vê o código que usa os esquemas de tabela abstrata. O uso de `documentOf` no método `EnhanceType.documentOf(...)` não indica que o método retorne um tipo `EnhancedDocument` da API de documento aprimorado. O método `documentOf(...)` nesse contexto retorna um objeto que sabe como mapear seu argumento de classe de e para os atributos da tabela do DynamoDB usando o argumento do esquema da tabela.

#### Código de esquema estático
<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();
```

## Atributos de projeto de tipos complexos
<a name="ddb-en-client-adv-features-nested-projection"></a>

Para os métodos `query()` e `scan()`, você pode especificar quais atributos você deseja que sejam retornados nos resultados usando chamadas de método como `addNestedAttributeToProject()` e `attributesToProject()`. A API do Cliente Aprimorado do DynamoDB converte os parâmetros de chamada do método Java em [expressões de projeção](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ProjectionExpressions.html) antes do envio da solicitação.

O exemplo a seguir preenche a tabela `Person` com dois itens e, em seguida, executa três operações de verificação. 

A primeira verificação acessa todos os itens da tabela para comparar os resultados com as outras operações de verificação. 

A segunda verificação usa o método do construtor [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)) para retornar somente o valor do atributo `street`.

A terceira operação de varredura usa o método do construtor [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...)) para retornar os dados do atributo de primeiro nível, `hobbies`. O tipo do atributo de `hobbies` é uma lista. Para acessar itens individuais da lista, execute uma operação `get()` na lista.

```
        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 o método `attributesToProject()` seguir qualquer outro método do construtor que adiciona atributos que você deseja projetar, a lista de nomes de atributos fornecida ao `attributesToProject()` substitui todos os outros nomes de atributos.  
Uma verificação realizada com a instância `ScanEnhancedRequest` no trecho a seguir retorna somente dados de 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]}
```
O trecho de código a seguir usa o método `attributesToProject()` primeiro. Essa ordenação preserva todos os outros atributos obrigatórios.  

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

## Usar tipos complexos em expressões
<a name="ddb-en-client-adv-features-nested-expressions"></a>

É possível usar tipos complexos em expressões, como expressões de filtro e expressões condicionais, usando operadores de desreferenciamento para navegar pela estrutura do tipo complexo. Para objetos e mapas, use `. (dot)` e para elementos da lista use `[n]` (colchetes ao redor do número de sequência do elemento). Não é possível se referir a elementos individuais de um conjunto, mas você pode usar a [função `contains`](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions).

O exemplo a seguir mostra duas expressões de filtro usadas em operações de varredura. As expressões de filtro especificam as condições de correspondência dos itens que você deseja nos resultados. O exemplo usa as classes `Person`, `Address` e `PhoneNumber` mostradas anteriormente.

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

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

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


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

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

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

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

### Método auxiliar que preenche a tabela
<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);
    }
```

### Representação JSON de itens no banco de dados
<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"
  }
 ]
}
```

## Atualizar itens que contêm tipos complexos
<a name="ddb-en-client-adv-features-nested-updates"></a>

Para atualizar um item que contém tipos complexos, você tem duas abordagens básicas:
+ Abordagem 1: primeiro recupere o item (usando `getItem`), atualize o objeto e, depois, chame `DynamoDbTable#updateItem`.
+ Abordagem 2: não recupere o item, mas construa uma nova instância, defina as propriedades que você deseja atualizar e envie a instância para `DynamoDbTable#updateItem` definindo o valor apropriado de [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/IgnoreNullsMode.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/IgnoreNullsMode.html). Essa abordagem não exige que você busque o item antes de atualizá-lo.

Os exemplos mostrados nesta seção usam as classes `Person`, `Address` e `PhoneNumber` mostradas anteriormente.

### Abordagem de atualização 1: recuperar e, depois, atualizar
<a name="ddb-en-client-adv-features-nested-updates-retreive"></a>

Ao usar essa abordagem, você garante que nenhum dado seja perdido na atualização. A API do Cliente Aprimorado do DynamoDB recria o bean com os atributos do item salvo no DynamoDB, incluindo valores de tipos complexos. Depois, você precisa usar os getters e setters para atualizar o bean. A desvantagem dessa abordagem é o custo em que você incorre ao recuperar o item primeiro.

O exemplo a seguir demonstra que nenhum dado será perdido se você recuperar o item antes de atualizá-lo.

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

### Abordagem de atualização 2: use uma enumeração `IgnoreNullsMode` sem recuperar o item primeiro
<a name="ddb-en-client-adv-features-nested-updates-nullmode"></a>

Para atualizar um item no DynamoDB, é possível fornecer um novo objeto que tenha somente as propriedades que você deseja atualizar e deixar os outros valores como nulos. Com essa abordagem, você precisa estar ciente de como os valores nulos no objeto são tratados pelo SDK e como controlar o comportamento.

Para especificar quais propriedades de valor nulo você deseja que o SDK ignore, forneça uma enumeração `IgnoreNullsMode` ao criar o [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). Como exemplo do uso de um dos valores enumerados, o trecho a seguir usa o modo `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"
    }
  }
*/
```

O Guia do desenvolvedor do Amazon DynamoDB contém mais informações sobre [expressões de atualização](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html).

#### Descrições das opções `IgnoreNullsMode`
<a name="ignore-nulls-mode-descriptions"></a>
+ `IgnoreNullsMode.SCALAR_ONLY`: use essa configuração para atualizar atributos escalares em qualquer nível. O SDK cria uma declaração de atualização que envia somente atributos escalares não nulos para o DynamoDB. O SDK ignora os atributos escalares de valor nulo de um bean ou mapa, mantendo o valor salvo no DynamoDB.

  Quando você atualiza um atributo escalar de map ou bean, o mapa já deve existir no DynamoDB. Se adicionar um mapa ou um bean ao objeto que ainda não existe para o objeto no DynamoDB, você receberá uma `DynamoDbException` com a mensagem *O caminho do documento fornecido na expressão de atualização é inválido para atualização*. Você deve usar o modo `MAPS_ONLY` para adicionar um bean ou um mapa ao DynamoDB antes de atualizar qualquer um de seus atributos.
+ `IgnoreNullsMode.MAPS_ONLY`: use essa configuração para adicionar ou substituir propriedades que são um bean ou um mapa. O SDK substitui ou adiciona qualquer mapa ou bean fornecido no objeto. Todos os beans ou mapas que são nulos no objeto são ignorados, mantendo o mapa que existe no DynamoDB.
+ `IgnoreNullsMode.DEFAULT`: com essa configuração, o SDK nunca ignora valores nulos. Os atributos escalares em qualquer nível que sejam nulos são atualizados para nulos. O SDK atualiza qualquer bean, mapa, lista ou propriedade de conjunto com valor nulo no objeto para nulo no DynamoDB. Quando você usa esse modo, ou não informa nenhum modo, já que ele é o padrão, é recomendável recuperar o item antes. Isso evita que valores existentes no DynamoDB sejam substituídos por nulo devido aos valores nulos presentes no objeto usado para a atualização, a menos que a sua intenção seja realmente definir esses valores como nulos.

Em todos os modos, se você fornecer um objeto `updateItem` que tenha uma lista ou conjunto não nulo, a lista ou o conjunto será salvo no DynamoDB. 

#### Por que os modos?
<a name="ddb-en-client-adv-features-nested-updates-nullmodes-why"></a>

Quando você fornece um objeto com um bean ou um mapa para o `updateItem` método, o SDK não sabe se deve usar os valores das propriedades no bean (ou os valores de entrada no mapa) para atualizar o item ou se o todo bean/map deve substituir o que foi salvo no DynamoDB.

Trabalhando com nosso exemplo anterior, que mostra primeiro a recuperação do item, vamos tentar atualizar o atributo `city` de `mainAddress` sem a recuperação.

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

Os dois exemplos a seguir mostram os usos dos valores `MAPS_ONLY` e `SCALAR_ONLY` enumerados. `MAPS_ONLY` adiciona um mapa e `SCALAR_ONLY` atualiza um mapa.

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

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

        personDynamoDbTable.putItem(person);

        // Assume that we are running this code now.

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


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

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

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

        personDynamoDbTable.putItem(person);

        // Assume that we are running this code now.

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

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

Consulte na tabela a seguir quais valores nulos são ignorados em cada modo. Muitas vezes, você pode trabalhar com `SCALAR_ONLY` e `MAPS_ONLY`, exceto quando trabalha com beans ou mapas.


**Quais propriedades de valor nulo no objeto enviado para `updateItem` o SDK ignora em cada modo?**  

| Tipo de propriedade | no modo SCALAR\$1ONLY | no modo MAPS\$1ONLY | no modo DEFAULT | 
| --- | --- | --- | --- | 
| Escalar superior | Sim | Sim | Não | 
| Bean ou mapa | Sim | Sim | Não | 
| Valor escalar de uma entrada de bean ou mapa | Sim1 | Número2 | Não | 
| Listar ou definir | Sim | Sim | Não | 

1Isso pressupõe que o mapa já exista no DynamoDB. Qualquer valor escalar, seja nulo ou não, do bean ou mapa que você fornece no objeto para atualização exige que exista um caminho para o valor no DynamoDB. O SDK constrói um caminho para o atributo usando o operador de desreferenciação `. (dot)` antes de enviar a solicitação.

2Como você usa o modo `MAPS_ONLY` para substituir totalmente ou adicionar um bean ou mapa, todos os valores nulos no bean ou no mapa são retidos no mapa salvo no DynamoDB.

# Preservar objetos vazios com `@DynamoDbPreserveEmptyObject`
<a name="ddb-en-client-adv-features-empty"></a>

Se você salvar um bean no Amazon DynamoDB com objetos vazios e quiser que o SDK recrie os objetos vazios após a recuperação, anote o getter do bean interno com `@DynamoDbPreserveEmptyObject`.

Para ilustrar como a anotação funciona, o exemplo de código usa os dois beans a seguir.

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

A classe de dados a seguir contém dois campos `InnerBean`. O método getter, `getInnerBeanWithoutAnno()`, não é anotado com `@DynamoDbPreserveEmptyObject`. O método `getInnerBeanWithAnno()` é anotado.

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

As instâncias da classe `InnerBean` a seguir são campos de `MyBean` e são inicializadas como objetos vazios no código de exemplo.

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

O exemplo de código a seguir salva um objeto `MyBean` com beans internos inicializados no DynamoDB e, em seguida, recupera o item. A saída registrada mostra que o `innerBeanWithoutAnno` não foi inicializado, mas `innerBeanWithAnno` foi criado.

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

## Esquema estático alternativo
<a name="ddb-en-client-adv-features-empty-ex1-static"></a>

Você pode usar a seguinte versão `StaticTableSchema` dos esquemas de tabela no lugar das anotações nos beans.

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

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

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

# Evitar salvar atributos nulos de objetos aninhados
<a name="ddb-en-client-adv-features-ignore-null"></a>

Você pode ignorar atributos nulos de objetos aninhados ao salvar um objeto de classe de dados no DynamoDB aplicando a anotação `@DynamoDbIgnoreNulls`. Por outro lado, atributos de nível superior com valores nulos nunca são salvos no banco de dados.

Para ilustrar como a anotação funciona, o exemplo de código usa os dois beans a seguir.

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

A classe de dados a seguir contém dois campos `InnerBean`. O método getter, `getInnerBeanWithoutAnno()`, não é anotado. O método `getInnerBeanWithIgnoreNullsAnno()` é anotado com `@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();
    }
}
```

As instâncias da classe `InnerBean` a seguir são campos de `MyBean` e são usadas no código de exemplo a seguir.

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

O exemplo de código a seguir cria um objeto `InnerBean` e define somente um de seus dois atributos com um valor. 

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

Para visualizar os dados de nível baixo enviados ao DynamoDB, o código registra o mapa de atributos antes de salvar o objeto `MyBean`.

A saída registrada mostra que `innerBeanWithIgnoreNullsAnno` gera um atributo,

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

A instância `innerBeanWithoutAnno` gera dois atributos. Um atributo tem um valor de 200 e o outro é um atributo de valor nulo.

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

## Representação JSON do mapa de atributo
<a name="ddb-en-client-adv-features-ignore-null-ex2"></a>

A representação JSON a seguir facilita a visualização dos dados salvos no DynamoDB.

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

## Esquema estático alternativo
<a name="ddb-en-client-adv-features-empty-ex1-static"></a>

Você pode usar a seguinte versão `StaticTableSchema` dos esquemas de tabela nas anotações da classe de dados no local.

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

# Trabalhar com documentos JSON com a API de documentos aprimorados do DynamoDB
<a name="ddb-en-client-doc-api"></a>

A [API de documentos aprimorada](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/package-summary.html) para foi AWS SDK for Java 2.x projetada para funcionar com dados orientados a documentos que não têm um esquema fixo. No entanto, ela também permite que você use classes personalizadas para mapear atributos individuais.

 A API de documentos aprimorada é a sucessora da [API de documentos](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/document/DynamoDB.html) da AWS SDK para Java v1.x.

**Contents**
+ [Começar a usar a API de documentos aprimorados](ddb-en-client-doc-api-steps.md)
  + [Criar um `DocumentTableSchema` e uma `DynamoDbTable`](ddb-en-client-doc-api-steps.md#ddb-en-client-doc-api-steps-createschema)
+ [Criar documentos aprimorados](ddb-en-client-doc-api-steps-create-ed.md)
  + [Criar a partir de uma cadeia de caracteres JSON](ddb-en-client-doc-api-steps-create-ed.md#ddb-en-client-doc-api-steps-create-ed-fromJson)
  + [Criar a partir de elementos individuais](ddb-en-client-doc-api-steps-create-ed.md#ddb-en-client-doc-api-steps-create-ed-fromparts)
+ [Executar operações de CRUD](ddb-en-client-doc-api-steps-use.md)
+ [Acessar atributos do documento aprimorado como objetos personalizados](ddb-en-client-doc-api-convert.md)
+ [Usar um `EnhancedDocument` sem o DynamoDB](ddb-en-client-doc-api-standalone.md)

# Começar a usar a API de documentos aprimorados
<a name="ddb-en-client-doc-api-steps"></a>

A API de Documento Aprimorado exige as mesmas [dependências](ddb-en-client-getting-started.md#ddb-en-client-gs-dep) necessárias para a API do Cliente Aprimorado do DynamoDB. Ela também requer uma [instância `DynamoDbEnhancedClient`](ddb-en-client-getting-started-dynamodbTable.md#ddb-en-client-getting-started-dynamodbTable-eclient), conforme mostrado no início deste tópico.

Como a API de documentos aprimorada foi lançada com a versão 2.20.3 do AWS SDK for Java 2.x, você precisa dessa versão ou superior.

## Criar um `DocumentTableSchema` e uma `DynamoDbTable`
<a name="ddb-en-client-doc-api-steps-createschema"></a>

Para invocar comandos em uma tabela do DynamoDB usando a Enhanced Document API, associe a tabela a um objeto de recurso < > do lado do [DynamoDbTablecliente EnhancedDocument](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html). 

O método `table()` do cliente aprimorado cria uma instância `DynamoDbTable<EnhancedDocument>` e exige parâmetros para o nome da tabela do DynamoDB e uma `DocumentTableSchema`. 

O construtor de um [DocumentTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/DocumentTableSchema.html)requer uma chave de índice primária e um ou mais provedores de conversão de atributos. O método `AttributeConverterProvider.defaultProvider()` fornece conversores para [tipos padrão](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/package-summary.html). Ele deve ser especificado mesmo se você fornecer um provedor de conversão de atributos personalizado. Você pode adicionar uma chave de índice secundária opcional ao construtor.

O trecho de código a seguir mostra o código que gera a representação do lado do cliente de uma tabela `person` do DynamoDB que armazena objetos `EnhancedDocument` sem esquema.

```
DynamoDbTable<EnhancedDocument> documentDynamoDbTable = 
                enhancedClient.table("person",
                        TableSchema.documentSchemaBuilder()
                            // Specify the primary key attributes.
                            .addIndexPartitionKey(TableMetadata.primaryIndexName(),"id", AttributeValueType.S)
                            .addIndexSortKey(TableMetadata.primaryIndexName(), "lastName", AttributeValueType.S)
                            // Specify attribute converter providers. Minimally add the default one.
                            .attributeConverterProviders(AttributeConverterProvider.defaultProvider())
                            .build());
                                                         
// Call documentTable.createTable() if "person" does not exist in DynamoDB.
// createTable() should be called only one time.
```

Veja a seguir a representação JSON de um objeto `person` usado em toda esta seção.

### Objeto JSON `person`
<a name="ddb-en-client-doc-api-steps-createschema-obj"></a>

```
{
  "id": 1,
  "firstName": "Richard",
  "lastName": "Roe",
  "age": 25,
  "addresses":
    {
      "home": {
        "zipCode": "00000",
        "city": "Any Town",
        "state": "FL",
        "street": "123 Any Street"
      },
      "work": {
        "zipCode": "00001",
        "city": "Anywhere",
        "state": "FL",
        "street": "100 Main Street"
      }
    },
  "hobbies": [
    "Hobby 1",
    "Hobby 2"
  ],
  "phoneNumbers": [
    {
      "type": "Home",
      "number": "555-0100"
    },
    {
      "type": "Work",
      "number": "555-0119"
    }
  ]
}
```

# Criar documentos aprimorados
<a name="ddb-en-client-doc-api-steps-create-ed"></a>

Um `[EnhancedDocument](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html)` representa um objeto do tipo documento que tem uma estrutura complexa com atributos aninhados. Um `EnhancedDocument` requer atributos de nível superior que correspondam aos atributos da chave primária especificados para o `DocumentTableSchema`. O conteúdo restante é arbitrário e pode consistir em atributos de nível superior e também em atributos profundamente aninhados.

Você cria uma instância `EnhancedDocument` usando um construtor que fornece várias maneiras de adicionar elementos.

## Criar a partir de uma cadeia de caracteres JSON
<a name="ddb-en-client-doc-api-steps-create-ed-fromJson"></a>

Com uma cadeia de caracteres JSON, você pode criar um `EnhancedDocument` com uma chamada do método. O trecho a seguir cria um `EnhancedDocument` a partir de uma cadeia de caracteres JSON retornada pelo método auxiliar `jsonPerson()`. O método `jsonPerson()` retorna a versão da cadeia de caracteres JSON do [objeto person](ddb-en-client-doc-api-steps.md#ddb-en-client-doc-api-steps-createschema-obj) mostrado anteriormente.

```
EnhancedDocument document = 
        EnhancedDocument.builder()
                        .json( jsonPerson() )
                        .build());
```

## Criar a partir de elementos individuais
<a name="ddb-en-client-doc-api-steps-create-ed-fromparts"></a>

Como alternativa, você pode criar uma instância `EnhancedDocument` a partir de componentes individuais usando métodos seguros de tipo do construtor.

O exemplo a seguir cria um documento aprimorado `person` semelhante ao documento aprimorado criado a partir da cadeia de caracteres JSON no exemplo anterior.

```
        /* Define the shape of an address map whose JSON representation looks like the following.
           Use 'addressMapEnhancedType' in the following EnhancedDocument.builder() to simplify the code.
           "home": {
             "zipCode": "00000",
             "city": "Any Town",
             "state": "FL",
             "street": "123 Any Street"
           }*/
        EnhancedType<Map<String, String>> addressMapEnhancedType =
                EnhancedType.mapOf(EnhancedType.of(String.class), EnhancedType.of(String.class));


        //  Use the builder's typesafe methods to add elements to the enhanced document.
        EnhancedDocument personDocument = EnhancedDocument.builder()
                .putNumber("id", 50)
                .putString("firstName", "Shirley")
                .putString("lastName", "Rodriguez")
                .putNumber("age", 53)
                .putNull("nullAttribute")
                .putJson("phoneNumbers", phoneNumbersJSONString())
                /* Add the map of addresses whose JSON representation looks like the following.
                        {
                          "home": {
                            "zipCode": "00000",
                            "city": "Any Town",
                            "state": "FL",
                            "street": "123 Any Street"
                          }
                        } */
                .putMap("addresses", getAddresses(), EnhancedType.of(String.class), addressMapEnhancedType)
                .putList("hobbies", List.of("Theater", "Golf"), EnhancedType.of(String.class))
                .build();
```

### Métodos auxiliares
<a name="ddb-en-client-doc-api-steps-use-fromparts-helpers"></a>

```
    private static String phoneNumbersJSONString() {
        return "  [" +
                "    {" +
                "      \"type\": \"Home\"," +
                "      \"number\": \"555-0140\"" +
                "    }," +
                "    {" +
                "      \"type\": \"Work\"," +
                "      \"number\": \"555-0155\"" +
                "    }" +
                "  ]";
    }

    private static Map<String, Map<String, String>> getAddresses() {
        return Map.of(
                "home", Map.of(
                        "zipCode", "00002",
                        "city", "Any Town",
                        "state", "ME",
                        "street", "123 Any Street"));

    }
```

# Executar operações de CRUD
<a name="ddb-en-client-doc-api-steps-use"></a>

Depois que definir uma instância `EnhancedDocument`, você poderá salvá-la em uma tabela do DynamoDB. O trecho de código a seguir usa o [personDocument](ddb-en-client-doc-api-steps-create-ed.md#ddb-en-client-doc-api-steps-create-ed-fromparts) que foi criado a partir de elementos individuais.

```
documentDynamoDbTable.putItem(personDocument);
```

Depois de ler uma instância de documento aprimorado a partir do DynamoDB, você pode extrair os valores dos atributos individuais usando getters, conforme mostrado no trecho de código a seguir, que acessam os dados salvos do `personDocument`. Como alternativa, você pode extrair o conteúdo completo em uma cadeia de caracteres JSON, conforme mostrado na última parte do código de exemplo.

```
        // Read the item.
        EnhancedDocument personDocFromDb = documentDynamoDbTable.getItem(Key.builder().partitionValue(50).build());

        // Access top-level attributes.
        logger.info("Name: {} {}", personDocFromDb.getString("firstName"), personDocFromDb.getString("lastName"));
        // Name: Shirley Rodriguez

        // Typesafe access of a deeply nested attribute. The addressMapEnhancedType shown previously defines the shape of an addresses map.
        Map<String, Map<String, String>> addresses = personDocFromDb.getMap("addresses", EnhancedType.of(String.class), addressMapEnhancedType);
        addresses.keySet().forEach(k -> logger.info(addresses.get(k).toString()));
        // {zipCode=00002, city=Any Town, street=123 Any Street, state=ME}

        // Alternatively, work with AttributeValue types checking along the way for deeply nested attributes.
        Map<String, AttributeValue> addressesMap = personDocFromDb.getMapOfUnknownType("addresses");
        addressesMap.keySet().forEach((String k) -> {
            logger.info("Looking at data for [{}] address", k);
            // Looking at data for [home] address
            AttributeValue value = addressesMap.get(k);
            AttributeValue cityValue = value.m().get("city");
            if (cityValue != null) {
                logger.info(cityValue.s());
                // Any Town
            }
        });

        List<AttributeValue> phoneNumbers = personDocFromDb.getListOfUnknownType("phoneNumbers");
        phoneNumbers.forEach((AttributeValue av) -> {
            if (av.hasM()) {
                AttributeValue type = av.m().get("type");
                if (type.s() != null) {
                    logger.info("Type of phone: {}", type.s());
                    // Type of phone: Home
                    // Type of phone: Work
                }
            }
        });

        String jsonPerson = personDocFromDb.toJson();
        logger.info(jsonPerson);
        // {"firstName":"Shirley","lastName":"Rodriguez","addresses":{"home":{"zipCode":"00002","city":"Any Town","street":"123 Any Street","state":"ME"}},"hobbies":["Theater","Golf"],
        //     "id":50,"nullAttribute":null,"age":53,"phoneNumbers":[{"number":"555-0140","type":"Home"},{"number":"555-0155","type":"Work"}]}
```

`EnhancedDocument`as instâncias podem ser usadas com qualquer método de `[DynamoDbTable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html)` ou [DynamoDbEnhancedClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html)no lugar de classes de dados mapeadas.

# Acessar atributos do documento aprimorado como objetos personalizados
<a name="ddb-en-client-doc-api-convert"></a>

Além de fornecer uma API para ler e gravar atributos com estruturas sem esquemas, a API de Documento Aprimorado permite converter atributos de e para instâncias de classes personalizadas.

A API de Documento Aprimorado usa `AttributeConverterProvider`s e `AttributeConverter`s que foram mostrados na seção de [conversão de atributos de controle](ddb-en-client-adv-features-conversion.md) como parte da API do Cliente Aprimorado do DynamoDB.

No exemplo a seguir, usamos um `CustomAttributeConverterProvider` com sua classe `AddressConverter` aninhada para converter objetos `Address`. 

Este exemplo mostra que você pode misturar dados de classes e também dados de estruturas que são criadas conforme necessário. Esse exemplo também mostra que classes personalizadas podem ser usadas em qualquer nível de uma estrutura aninhada. Os objetos `Address` neste exemplo são valores usados em um mapa.

```
    public static void attributeToAddressClassMappingExample(DynamoDbEnhancedClient enhancedClient, DynamoDbClient standardClient) {
        String tableName = "customer";

        // Define the DynamoDbTable for an enhanced document.
        // The schema builder provides methods for attribute converter providers and keys.
        DynamoDbTable<EnhancedDocument> documentDynamoDbTable = enhancedClient.table(tableName,
                DocumentTableSchema.builder()
                        // Add the CustomAttributeConverterProvider along with the default when you build the table schema.
                        .attributeConverterProviders(
                                List.of(
                                        new CustomAttributeConverterProvider(),
                                        AttributeConverterProvider.defaultProvider()))
                        .addIndexPartitionKey(TableMetadata.primaryIndexName(), "id", AttributeValueType.N)
                        .addIndexSortKey(TableMetadata.primaryIndexName(), "lastName", AttributeValueType.S)
                        .build());
        // Create the DynamoDB table if needed.
        documentDynamoDbTable.createTable();
        waitForTableCreation(tableName, standardClient);


        // The getAddressesForCustomMappingExample() helper method that provides 'addresses' shows the use of a custom Address class
        // rather than using a Map<String, Map<String, String> to hold the address data.
        Map<String, Address> addresses = getAddressesForCustomMappingExample();

        // Build an EnhancedDocument instance to save an item with a mix of structures defined as needed and static classes.
        EnhancedDocument personDocument = EnhancedDocument.builder()
                .putNumber("id", 50)
                .putString("firstName", "Shirley")
                .putString("lastName", "Rodriguez")
                .putNumber("age", 53)
                .putNull("nullAttribute")
                .putJson("phoneNumbers", phoneNumbersJSONString())
                // Note the use of 'EnhancedType.of(Address.class)' instead of the more generic
                // 'EnhancedType.mapOf(EnhancedType.of(String.class), EnhancedType.of(String.class))' that was used in a previous example.
                .putMap("addresses", addresses, EnhancedType.of(String.class), EnhancedType.of(Address.class))
                .putList("hobbies", List.of("Hobby 1", "Hobby 2"), EnhancedType.of(String.class))
                .build();
        // Save the item to DynamoDB.
        documentDynamoDbTable.putItem(personDocument);

        // Retrieve the item just saved.
        EnhancedDocument srPerson = documentDynamoDbTable.getItem(Key.builder().partitionValue(50).sortValue("Rodriguez").build());

        // Access the addresses attribute.
        Map<String, Address> srAddresses = srPerson.get("addresses",
                EnhancedType.mapOf(EnhancedType.of(String.class), EnhancedType.of(Address.class)));

        srAddresses.keySet().forEach(k -> logger.info(addresses.get(k).toString()));

        documentDynamoDbTable.deleteTable();

// The content logged to the console shows that the saved maps were converted to Address instances.
Address{street='123 Main Street', city='Any Town', state='NC', zipCode='00000'}
Address{street='100 Any Street', city='Any Town', state='NC', zipCode='00000'}
```

## Código `CustomAttributeConverterProvider`
<a name="ddb-en-client-doc-api-convert-provider"></a>

```
public class CustomAttributeConverterProvider implements AttributeConverterProvider {

    private final Map<EnhancedType<?>, AttributeConverter<?>> converterCache = ImmutableMap.of(
            // 1. Add AddressConverter to the internal cache.
            EnhancedType.of(Address.class), new AddressConverter());

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

    // 2. The enhanced client queries the provider for attribute converters if it
    //    encounters a type that it does not know how to convert.
    @SuppressWarnings("unchecked")
    @Override
    public <T> AttributeConverter<T> converterFor(EnhancedType<T> enhancedType) {
        return (AttributeConverter<T>) converterCache.get(enhancedType);
    }

    // 3. Custom attribute converter
    private class AddressConverter implements AttributeConverter<Address> {
        // 4. Transform an Address object into a DynamoDB map.
        @Override
        public AttributeValue transformFrom(Address address) {

            Map<String, AttributeValue> attributeValueMap = Map.of(
                    "street", AttributeValue.fromS(address.getStreet()),
                    "city", AttributeValue.fromS(address.getCity()),
                    "state", AttributeValue.fromS(address.getState()),
                    "zipCode", AttributeValue.fromS(address.getZipCode()));

            return AttributeValue.fromM(attributeValueMap);
        }

        // 5. Transform the DynamoDB map attribute to an Address oject.
        @Override
        public Address transformTo(AttributeValue attributeValue) {
            Map<String, AttributeValue> m = attributeValue.m();
            Address address = new Address();
            address.setStreet(m.get("street").s());
            address.setCity(m.get("city").s());
            address.setState(m.get("state").s());
            address.setZipCode(m.get("zipCode").s());

            return address;
        }

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

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

## Classe `Address`
<a name="ddb-en-client-doc-api-convert-address"></a>

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

## Método auxiliar que fornece endereços
<a name="ddb-en-client-doc-api-convert-helper"></a>

O método auxiliar a seguir fornece o mapa que usa instâncias `Address` personalizadas para valores em vez de instâncias `Map<String, String>` genéricas para valores.

```
    private static Map<String, Address> getAddressesForCustomMappingExample() {
        Address homeAddress = new Address();
        homeAddress.setStreet("100 Any Street");
        homeAddress.setCity("Any Town");
        homeAddress.setState("NC");
        homeAddress.setZipCode("00000");

        Address workAddress = new Address();
        workAddress.setStreet("123 Main Street");
        workAddress.setCity("Any Town");
        workAddress.setState("NC");
        workAddress.setZipCode("00000");

        return Map.of("home", homeAddress,
                "work", workAddress);
    }
```

# Usar um `EnhancedDocument` sem o DynamoDB
<a name="ddb-en-client-doc-api-standalone"></a>

Embora você geralmente use uma instância de um `EnhancedDocument` para ler e gravar itens do tipo documento do DynamoDB, ela também pode ser usada independentemente do DynamoDB. 

Você pode usar `EnhancedDocuments` pela sua capacidade de converter desde cadeias de caracteres JSON ou objetos personalizados a mapas de nível baixo de `AttributeValues`, conforme mostrado no exemplo a seguir.

```
    public static void conversionWithoutDynamoDbExample() {
        Address address = new Address();
        address.setCity("my city");
        address.setState("my state");
        address.setStreet("my street");
        address.setZipCode("00000");

        // Build an EnhancedDocument instance for its conversion functionality alone.
        EnhancedDocument addressEnhancedDoc = EnhancedDocument.builder()
                // Important: You must specify attribute converter providers when you build an EnhancedDocument instance not used with a DynamoDB table.
                .attributeConverterProviders(new CustomAttributeConverterProvider(), DefaultAttributeConverterProvider.create())
                .put("addressDoc", address, Address.class)
                .build();

        // Convert address to a low-level item representation.
        final Map<String, AttributeValue> addressAsAttributeMap = addressEnhancedDoc.getMapOfUnknownType("addressDoc");
        logger.info("addressAsAttributeMap: {}", addressAsAttributeMap.toString());

        // Convert address to a JSON string.
        String addressAsJsonString = addressEnhancedDoc.getJson("addressDoc");
        logger.info("addressAsJsonString: {}", addressAsJsonString);
        // Convert addressEnhancedDoc back to an Address instance.
        Address addressConverted =  addressEnhancedDoc.get("addressDoc", Address.class);
        logger.info("addressConverted: {}", addressConverted.toString());
    }

   /* Console output:
          addressAsAttributeMap: {zipCode=AttributeValue(S=00000), state=AttributeValue(S=my state), street=AttributeValue(S=my street), city=AttributeValue(S=my city)}
          addressAsJsonString: {"zipCode":"00000","state":"my state","street":"my street","city":"my city"}
          addressConverted: Address{street='my street', city='my city', state='my state', zipCode='00000'}
   */
```

**nota**  
Ao usar um documento aprimorado independente de uma tabela do DynamoDB, defina explicitamente os provedores de conversão de atributos no construtor.  
Por outro lado, o esquema da tabela de documentos fornece os provedores de conversão quando um documento aprimorado é usado com uma tabela do DynamoDB.

# Usar extensões para personalizar as operações do Cliente Aprimorado do DynamoDB
<a name="ddb-en-client-extensions"></a>

A API do Cliente Aprimorado do DynamoDB fornece suporte a extensões de complemento que fornecem funcionalidades além das operações de mapeamento. As extensões usam dois métodos de hook para modificar dados durante as operações de leitura e gravação:
+ `beforeWrite()`: modifica uma operação de gravação antes que ela aconteça
+ `afterRead()`: modifica os resultados de uma operação de leitura depois que ela acontece

Algumas operações (como atualizações de itens) realizam uma gravação e depois uma leitura, então os dois métodos de hook são chamados.

## Como as extensões são carregadas
<a name="ddb-en-client-extensions-loading"></a>

As extensões são carregadas na ordem em que você especifica no compilador do cliente aprimorado. A ordem de carregamento pode ser importante porque uma extensão pode atuar em valores que foram transformados por uma extensão anterior.

Por padrão, o cliente aprimorado carrega duas extensões:
+ `[VersionedRecordExtension](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/extensions/VersionedRecordExtension.html)`: proporciona um bloqueio positivo
+ `[AtomicCounterExtension](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/extensions/AtomicCounterExtension.html)`: incrementa automaticamente os atributos do contador

Você pode substituir o comportamento padrão com o compilador de cliente aprimorado e carregar qualquer extensão. Você também pode especificar nenhuma se não quiser as extensões padrão.

**Importante**  
Se você carregar suas próprias extensões, o cliente aprimorado não carregará nenhuma extensão padrão. Se você quiser o comportamento fornecido por uma extensão padrão, precisará adicioná-la explicitamente à lista de extensões.

O exemplo a seguir mostra como carregar uma extensão personalizada chamada `verifyChecksumExtension` depois de `VersionedRecordExtension`. A `AtomicCounterExtension` não está carregada neste exemplo.

```
DynamoDbEnhancedClientExtension versionedRecordExtension = VersionedRecordExtension.builder().build();

DynamoDbEnhancedClient enhancedClient = 
    DynamoDbEnhancedClient.builder()
                          .dynamoDbClient(dynamoDbClient)
                          .extensions(versionedRecordExtension, verifyChecksumExtension)
                          .build();
```

## Detalhes e configuração da extensão disponível
<a name="ddb-en-client-extensions-details"></a>

As seções a seguir fornecem informações detalhadas sobre cada extensão disponível no SDK.

### Implementar um bloqueio positivo com o `VersionedRecordExtension`
<a name="ddb-en-client-extensions-VRE"></a>

A extensão `VersionedRecordExtension` fornece bloqueio positivo incrementando e rastreando um número de versão do item à medida que os itens são gravados no banco de dados. É adicionada uma condição a cada operação de gravação que faz com que ela falhe caso o número de versão do item armazenado não corresponda ao valor que a aplicação leu por último.

#### Configuração
<a name="ddb-en-client-extensions-VRE-conf"></a>

Para especificar qual atributo usar para rastrear o número da versão do item, marque um atributo numérico no esquema da tabela.

O trecho a seguir especifica que o atributo `version` deve conter o número da versão do item.

```
    @DynamoDbVersionAttribute
    public Integer getVersion() {...};
    public void setVersion(Integer version) {...};
```

A abordagem equivalente do esquema de tabela estática é mostrada no trecho a seguir.

```
    .addAttribute(Integer.class, a -> a.name("version")
                                       .getter(Customer::getVersion)
                                       .setter(Customer::setVersion)
                                        // Apply the 'version' tag to the attribute.
                                       .tags(VersionedRecordExtension.AttributeTags.versionAttribute())
```

#### Como funciona
<a name="ddb-en-client-extensions-VRE-how-it-works"></a>

O bloqueio positivo com a `VersionedRecordExtension` tem o seguinte impacto sobre estes métodos `DynamoDbEnhancedClient` e `DynamoDbTable`:

**`putItem`**  
Novos itens recebem um valor de versão inicial de 0. Isso pode ser configurado com `@DynamoDbVersionAttribute(startAt = X)`.

**`updateItem`**  
Se você recuperar um item, atualizar uma ou mais das suas propriedades e tentar salvar as alterações, a operação será bem-sucedida somente se o número de versão no lado do cliente e no lado do servidor corresponder.  
Se for bem-sucedida, o número da versão é incrementado automaticamente em 1. Isso pode ser configurado com `@DynamoDbVersionAttribute(incrementBy = X)`.

**`deleteItem`**  
A anotação `DynamoDbVersionAttribute` não tem efeito. Você deve adicionar uma expressão de condição manualmente ao excluir um item.  
O exemplo a seguir adiciona uma expressão condicional para garantir que o item excluído seja o que foi lido. No exemplo a seguir, `recordVersion` é o atributo do bean anotado com `@DynamoDbVersionAttribute`.  

```
// 1. Read the item and get its current version.
Customer item = customerTable.getItem(Key.builder().partitionValue("someId").build());
// `recordVersion` is the bean's attribute that is annotated with `@DynamoDbVersionAttribute`.
AttributeValue currentVersion = item.getRecordVersion();

// 2. Create conditional delete with the `currentVersion` value.
DeleteItemEnhancedRequest deleteItemRequest =
    DeleteItemEnhancedRequest.builder()
       .key(KEY)
       .conditionExpression(Expression.builder()
           .expression("recordVersion = :current_version_value")
           .putExpressionValue(":current_version_value", currentVersion)
           .build()).build();

customerTable.deleteItem(deleteItemRequest);
```

**`transactWriteItems`**  
+ `addPutItem`: esse método tem o mesmo comportamento de `putItem`.
+ `addUpdateItem`: esse método tem o mesmo comportamento de `updateItem`.
+ `addDeleteItem`: esse método tem o mesmo comportamento de `deleteItem`.

**`batchWriteItem`**  
+ `addPutItem`: esse método tem o mesmo comportamento de `putItem`.
+ `addDeleteItem`: esse método tem o mesmo comportamento de `deleteItem`.

**nota**  
As tabelas globais do DynamoDB usam uma [reconciliação do tipo “o último a gravar vence”](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/V2globaltables_HowItWorks.html#V2globaltables_HowItWorks.consistency-modes) entre as atualizações simultâneas. Com ela, o DynamoDB faz o melhor esforço para determinar o último a gravar. Se você usa tabelas globais, essa política de “o último a gravar vence” significa que as estratégias de bloqueio podem não funcionar conforme o esperado, pois todas as réplicas acabarão por convergir com base na última gravação determinada pelo DynamoDB. 

#### Como desabilitar
<a name="ddb-en-client-extensions-VRE-how-to-disable"></a>

Para desabilitar o bloqueio positivo, não use a anotação `@DynamoDbVersionAttribute`.

### Implementar contadores com a `AtomicCounterExtension`
<a name="ddb-en-client-extensions-ACE"></a>

A extensão `AtomicCounterExtension` incrementa um atributo numérico marcado sempre que um registro é gravado no banco de dados. É possível especificar valores de início e de incremento. Se nenhum valor for especificado, o valor inicial será definido como 0 e o valor do atributo será incrementado em 1.

#### Configuração
<a name="ddb-en-client-extensions-ACE-conf"></a>

Para especificar qual atributo é um contador, marque um atributo do tipo `Long` no esquema da tabela.

O trecho a seguir mostra o uso dos valores padrão de início e de incremento para o atributo `counter`.

```
    @DynamoDbAtomicCounter
    public Long getCounter() {...};
    public void setCounter(Long counter) {...};
```

A abordagem do esquema de tabela estática é mostrada no trecho a seguir. A extensão do contador atômico usa um valor inicial de 10 e incrementa o valor em 5 cada vez que o registro é gravado.

```
    .addAttribute(Integer.class, a -> a.name("counter")
                                       .getter(Customer::getCounter)
                                       .setter(Customer::setCounter)
                                        // Apply the 'atomicCounter' tag to the attribute with start and increment values.
                                       .tags(StaticAttributeTags.atomicCounter(10L, 5L))
```

### Adicionar carimbos de data e hora com a `AutoGeneratedTimestampRecordExtension`
<a name="ddb-en-client-extensions-AGTE"></a>

A extensão `AutoGeneratedTimestampRecordExtension` atualiza automaticamente os atributos marcados do tipo `[Instant](https://docs.oracle.com/javase/8/docs/api/java/time/Instant.html)` com um carimbo de data e hora atual sempre que o item é gravado com sucesso no banco de dados. Essa extensão não é carregada por padrão.

#### Configuração
<a name="ddb-en-client-extensions-AGTE-conf"></a>

Para especificar qual atributo atualizar com o carimbo de data/hora atual, marque o atributo `Instant` no esquema da tabela.

O atributo `lastUpdate` é o alvo do comportamento da extensão no trecho a seguir. Observe a exigência de que o atributo seja um tipo `Instant`.

```
    @DynamoDbAutoGeneratedTimestampAttribute
    public Instant getLastUpdate() {...}
    public void setLastUpdate(Instant lastUpdate) {...}
```

A abordagem equivalente do esquema de tabela estática é mostrada no trecho a seguir.

```
     .addAttribute(Instant.class, a -> a.name("lastUpdate")
                                        .getter(Customer::getLastUpdate)
                                        .setter(Customer::setLastUpdate)
                                        // Applying the 'autoGeneratedTimestamp' tag to the attribute.
                                        .tags(AutoGeneratedTimestampRecordExtension.AttributeTags.autoGeneratedTimestampAttribute())
```

### Gere um UUID com o AutoGeneratedUuidExtension
<a name="ddb-en-client-extensions-AGUE"></a>

A extensão `AutoGeneratedUuidExtension` gera um UUID (Identificador Único Universal) exclusivo para um atributo quando um novo registro é gravado no banco de dados. Usa o método Java JDK [UUID.randomUUID()](https://docs.oracle.com/javase/8/docs/api/java/util/UUID.html#randomUUID--) e se aplica aos atributos do tipo `java.lang.String`. Essa extensão não é carregada por padrão.

#### Configuração
<a name="ddb-en-client-extensions-AGUE-conf"></a>

O atributo `uniqueId` é o alvo do comportamento da extensão no trecho a seguir.

```
    @AutoGeneratedUuidExtension
    public String getUniqueId() {...}
    public void setUniqueId(String uniqueId) {...}
```

A abordagem equivalente do esquema de tabela estática é mostrada no trecho a seguir.

```
     .addAttribute(String.class, a -> a.name("uniqueId")
                                        .getter(Customer::getUniqueId)
                                        .setter(Customer::setUniqueId)
                                        // Applying the 'autoGeneratedUuid' tag to the attribute.
                                        .tags(AutoGeneratedUuidExtension.AttributeTags.autoGeneratedUuidAttribute())
```

Se quiser que a extensão preencha o UUID somente para métodos `putItem` e não para métodos `updateItem`, adicione a anotação de [comportamento de atualização](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/UpdateBehavior.html) conforme mostrado no trecho a seguir.

```
    @AutoGeneratedUuidExtension
    @DynamoDbUpdateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS)
    public String getUniqueId() {...}
    public void setUniqueId(String uniqueId) {...}
```

Se você usar a abordagem de esquema de tabela estática, use o código equivalente a seguir.

```
     .addAttribute(String.class, a -> a.name("uniqueId")
                                        .getter(Customer::getUniqueId)
                                        .setter(Customer::setUniqueId)
                                        // Applying the 'autoGeneratedUuid' tag to the attribute.
                                        .tags(AutoGeneratedUuidExtension.AttributeTags.autoGeneratedUuidAttribute(),
                                              StaticAttributeTags.updateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS))
```

# Exemplo de extensão personalizada
<a name="ddb-en-client-extensions-custom"></a>

É possível criar extensões personalizadas implementando a interface `DynamoDbEnhancedClientExtension`. A classe de extensão personalizada a seguir mostra um método `beforeWrite()` que usa uma expressão de atualização para definir um atributo `registrationDate` caso o item no banco de dados ainda não tenha um.

```
public final class CustomExtension implements DynamoDbEnhancedClientExtension {

    // 1. In a custom extension, use an UpdateExpression to define what action to take before
    //    an item is updated.
    @Override
    public WriteModification beforeWrite(DynamoDbExtensionContext.BeforeWrite context) {
        if ( context.operationContext().tableName().equals("Customer")
                && context.operationName().equals(OperationName.UPDATE_ITEM)) {
            return WriteModification.builder()
                    .updateExpression(createUpdateExpression())
                    .build();
        }
        return WriteModification.builder().build();  // Return an "empty" WriteModification instance if the extension should not be applied.
                                                     // In this case, if the code is not updating an item on the Customer table.
    }

    private static UpdateExpression createUpdateExpression() {

        // 2. Use a SetAction, a subclass of UpdateAction, to provide the values in the update.
        SetAction setAction =
                SetAction.builder()
                        .path("registrationDate")
                        .value("if_not_exists(registrationDate, :regValue)")
                        .putExpressionValue(":regValue", AttributeValue.fromS(Instant.now().toString()))
                        .build();
        // 3. Build the UpdateExpression with one or more UpdateAction.
        return UpdateExpression.builder()
                .addAction(setAction)
                .build();
    }
}
```

# Usar a API do cliente avançado do DynamoDB de forma assíncrona
<a name="ddb-en-client-async"></a>

Se seu aplicativo exigir chamadas assíncronas e sem bloqueio para o DynamoDB, você poderá usar o [DynamoDbEnhancedAsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedAsyncClient.html). Ele é semelhante à implementação síncrona, mas com as seguintes diferenças principais:

1. Ao criar o `DynamoDbEnhancedAsyncClient`, é necessário usar a versão assíncrona do cliente padrão, `DynamoDbAsyncClient`, conforme mostrado no trecho a seguir.

   ```
    DynamoDbEnhancedAsyncClient enhancedClient = 
        DynamoDbEnhancedAsyncClient.builder()
                                   .dynamoDbClient(dynamoDbAsyncClient)
                                   .build();
   ```

1. Os métodos que retornam um único objeto de dados retornam um `CompletableFuture` do resultado em vez de somente o resultado. Entretanto, seu aplicativo pode fazer outro trabalho sem precisar bloquear o resultado. O trecho a seguir mostra o método `getItem()` assíncrono. 

   ```
   CompletableFuture<Customer> result = customerDynamoDbTable.getItem(customer);
   // Perform other work here.
   return result.join();   // Now block and wait for the result.
   ```

1. Métodos que retornam listas paginadas de resultados retornam um [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/SdkPublisher.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/SdkPublisher.html) em vez de um [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/pagination/sync/SdkIterable.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/pagination/sync/SdkIterable.html) que o `DynamoDbEnhanceClient` síncrono retorna para os mesmos métodos. Seu aplicativo pode então inscrever um manipulador nesse publicador para lidar com os resultados de forma assíncrona, sem precisar bloquear.

   ```
   PagePublisher<Customer> results = customerDynamoDbTable.query(r -> r.queryConditional(keyEqualTo(k -> k.partitionValue("Smith"))));
   results.subscribe(myCustomerResultsProcessor);
   // Perform other work and let the processor handle the results asynchronously.
   ```

   Para obter um exemplo mais completo de como trabalhar com a `SdkPublisher API`, consulte [o exemplo](ddb-en-client-use-multirecord.md#ddb-en-client-use-multirecord-scan-async) na seção que discute o método `scan()` assíncrono deste guia.

# Anotações de classes de dados
<a name="ddb-en-client-anno-index"></a>

A tabela a seguir lista as anotações que podem ser usadas em classes de dados e fornece links para informações e exemplos neste guia. A tabela é classificada em ordem alfabética crescente pelo nome da anotação.


**Anotações de classe de dados usadas neste guia**  

| Nome da anotação | A anotação se aplica a1 | O que ela faz | Onde ela é mostrada neste guia | 
| --- | --- | --- | --- | 
| DynamoDbAtomicCounter | atributo2 | Incrementa um atributo numérico marcado sempre que um registro é gravado no banco de dados. | [Introdução e discussão.](ddb-en-client-extensions.md#ddb-en-client-extensions-ACE) | 
| DynamoDbAttribute | atributo | Define ou renomeia uma propriedade de bean que é mapeada para um atributo de tabela do DynamoDB. |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbAutoGeneratedTimestampAttribute | atributo | Atualiza um atributo marcado com um carimbo de data/hora atual toda vez que o item é gravado com sucesso no banco de dados | [Introdução e discussão](ddb-en-client-extensions.md#ddb-en-client-extensions-AGTE). | 
| DynamoDbAutoGeneratedUuid | atributo | Gere um UUID (Identificador Único Universal) exclusivo para um atributo quando um novo registro é gravado no banco de dados. | [Introdução e discussão.](ddb-en-client-extensions.md#ddb-en-client-extensions-AGUE) | 
| DynamoDbBean | classe | Marca uma classe de dados como mapeável para um esquema de tabela. | Primeiro uso na [classe Customer](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust) na seção Conceitos básicos. Vários usos aparecem no guia. | 
| DynamoDbConvertedBy | atributo | Associa um AttributeConverter personalizado ao atributo anotado. | [Discussão inicial e exemplo.](ddb-en-client-adv-features-conversion.md#ddb-en-client-adv-features-conversion-single) | 
| DynamoDbFlatten | atributo | Nivela todos os atributos de uma classe de dados separada do DynamoDB e os adiciona como atributos de nível superior ao registro que é lido e gravado no banco de dados.  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbIgnore | atributo |  Faz com que o atributo permaneça não mapeado.  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbIgnoreNulls | atributo | Impede o salvamento de atributos nulos de objetos aninhados do DynamoDb. | [Discussão e exemplos.](ddb-en-client-adv-features-ignore-null.md) | 
| DynamoDbImmutable | classe |  Marca uma classe de dados imutável como mapeável para um esquema de tabela.  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbPartitionKey | atributo |  Marca um atributo como a chave de partição primária (chave de hash) da tabela do DynamoDb.  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbPreserveEmptyObject | atributo |  Especifica que, se nenhum dado estiver presente para o objeto mapeado para o atributo anotado, o objeto deverá ser inicializado com todos os campos nulos.  | [Discussão e exemplos.](ddb-en-client-adv-features-empty.md) | 
| DynamoDbSecondaryPartitionKey | atributo |  Marca um atributo como uma chave de partição para um índice secundário global.  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbSecondarySortKey | atributo |  Marca um atributo como uma chave de classificação opcional para um índice secundário global ou local.  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbSortKey | atributo |  Marca um atributo como a chave de classificação primária opcional (chave de intervalo).  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/pt_br/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbUpdateBehavior | atributo |  Especifica o comportamento quando esse atributo é atualizado como parte de uma operação de atualização, como UpdateItem.  | [Introdução e exemplo.](ddb-en-client-adv-features-upd-behavior.md) | 
| DynamoDbVersionAttribute | atributo | Incrementa o número da versão de um item. | [Introdução e discussão.](ddb-en-client-extensions.md#ddb-en-client-extensions-VRE) | 

1Você pode aplicar anotações no nível de atributo ao getter ou ao setter, mas não a ambos. Este guia mostra anotações sobre getters.

2O termo `property` normalmente é usado para um valor encapsulado em uma classe de dados JavaBean. No entanto, no lugar desse termo, este guia usa o termo `attribute`, para ser consistente com a terminologia usada pelo DynamoDB.