

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á.

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