

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

# Uso de extensiones para personalizar operaciones de DynamoDB Enhanced Client
<a name="ddb-en-client-extensions"></a>

La API del cliente mejorado de DynamoDB admite extensiones de complementos que proporcionan funciones que van más allá de las operaciones de mapeo. Las extensiones utilizan dos métodos de enlace para modificar datos durante las operaciones de lectura y escritura:
+ `beforeWrite()`: modifica una operación de escritura antes de que se produzca
+ `afterRead()`: modifica los resultados de una operación de lectura después de que se produzca

Algunas operaciones (como las actualizaciones de elementos) realizan una escritura y luego una lectura, por lo que se llama a ambos métodos de enlace.

## Cómo se cargan las extensiones
<a name="ddb-en-client-extensions-loading"></a>

Las extensiones se cargan en el orden que especifique en el compilador del cliente mejorado. El orden de carga puede ser importante porque una extensión puede actuar sobre valores que han sido transformados por una extensión anterior.

De forma predeterminada, el cliente mejorado carga dos extensiones:
+ `[VersionedRecordExtension](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/extensions/VersionedRecordExtension.html)`: proporciona bloqueo positivo
+ `[AtomicCounterExtension](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/extensions/AtomicCounterExtension.html)`: incrementa automáticamente los atributos del contador

Puede anular el comportamiento predeterminado con el compilador del cliente mejorado y cargar cualquier extensión. También puede no especificar ninguna si no desea utilizar las extensiones predeterminadas.

**importante**  
Si carga sus propias extensiones, el cliente mejorado no carga ninguna extensión predeterminada. Si desea el comportamiento proporcionado por alguna de las extensiones predeterminadas, deberá añadirla explícitamente a la lista de extensiones.

El siguiente ejemplo muestra cómo cargar una extensión personalizada llamada `verifyChecksumExtension` después de la `VersionedRecordExtension`. La `AtomicCounterExtension` no se carga en este ejemplo.

```
DynamoDbEnhancedClientExtension versionedRecordExtension = VersionedRecordExtension.builder().build();

DynamoDbEnhancedClient enhancedClient = 
    DynamoDbEnhancedClient.builder()
                          .dynamoDbClient(dynamoDbClient)
                          .extensions(versionedRecordExtension, verifyChecksumExtension)
                          .build();
```

## Detalles y configuración de extensión disponibles
<a name="ddb-en-client-extensions-details"></a>

En las secciones siguientes se proporciona información detallada sobre cada extensión disponible en el SDK.

### Implemente bloqueo positivo con `VersionedRecordExtension`
<a name="ddb-en-client-extensions-VRE"></a>

La extensión `VersionedRecordExtension` proporciona bloqueo positivo incrementando y rastreando el número de versión de los elemento a medida que estos se escriben en la base de datos. Se añade una condición a cada escritura que hará que esta falle si el número de versión del elemento persistido real no coincide con el valor que la aplicación leyó por última vez.

#### Configuración
<a name="ddb-en-client-extensions-VRE-conf"></a>

Para especificar qué atributo usar para rastrear el número de versión del elemento, etiquete un atributo numérico en el esquema de la tabla.

El siguiente fragmento especifica que el atributo `version` debe contener el número de versión del artículo.

```
    @DynamoDbVersionAttribute
    public Integer getVersion() {...};
    public void setVersion(Integer version) {...};
```

El enfoque equivalente del esquema de tabla estático se muestra en el siguiente fragmento.

```
    .addAttribute(Integer.class, a -> a.name("version")
                                       .getter(Customer::getVersion)
                                       .setter(Customer::setVersion)
                                        // Apply the 'version' tag to the attribute.
                                       .tags(VersionedRecordExtension.AttributeTags.versionAttribute())
```

#### Funcionamiento
<a name="ddb-en-client-extensions-VRE-how-it-works"></a>

El bloqueo positivo con la `VersionedRecordExtension` tiene el siguiente efecto en estos métodos `DynamoDbEnhancedClient` y `DynamoDbTable`.

**`putItem`**  
A los nuevos elementos se les asigna un valor de versión inicial de 0. Esto se puede configurar con `@DynamoDbVersionAttribute(startAt = X)`.

**`updateItem`**  
Si recupera un elemento, actualiza una o varias de sus propiedades e intenta guardar los cambios, la operación solamente se lleva a cabo si el número de versión del lado del cliente coincide con el número de versión del lado del servidor.  
Si la operación es correcta, el número de versión aumenta automáticamente en 1. Esto se puede configurar con `@DynamoDbVersionAttribute(incrementBy = X)`.

**`deleteItem`**  
La anotación `DynamoDbVersionAttribute` no tiene ningún efecto. Debe añadir una expresión de condición manualmente al eliminar un elemento.  
En el siguiente ejemplo, se agrega una expresión condicional para garantizar que el elemento eliminado es el elemento que se ha leído. En el siguiente ejemplo, `recordVersion` es el atributo del bean anotado con `@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`: este método tiene el mismo comportamiento que `putItem`.
+ `addUpdateItem`: este método tiene el mismo comportamiento que `updateItem`.
+ `addDeleteItem`: este método tiene el mismo comportamiento que `deleteItem`.

**`batchWriteItem`**  
+ `addPutItem`: este método tiene el mismo comportamiento que `putItem`.
+ `addDeleteItem`: este método tiene el mismo comportamiento que `deleteItem`.

**nota**  
Las tablas globales de DynamoDB usan una [conciliación de “el último escritor gana”](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/V2globaltables_HowItWorks.html#V2globaltables_HowItWorks.consistency-modes) entre actualizaciones simultáneas. DynamoDB hace todo lo posible para determinar quién realizó la última escritura. Si utiliza tablas globales, esta política de “el último escritor gana” significa que las estrategias de bloqueo pueden no funcionar como se esperaba, ya que todas las réplicas acabarán convergiendo en función de la última escritura determinada por DynamoDB. 

#### Cómo deshabilitar
<a name="ddb-en-client-extensions-VRE-how-to-disable"></a>

Para deshabilitar el bloqueo positivo, no utilice la anotación `@DynamoDbVersionAttribute`.

### Implemente contadores con la `AtomicCounterExtension`
<a name="ddb-en-client-extensions-ACE"></a>

La extensión `AtomicCounterExtension` incrementa un atributo numérico etiquetado cada vez que se escribe un registro en la base de datos. Se pueden especificar los valores de inicio y de incremento. Si no se especifica ningún valor, el valor inicial se establece en 0 y el valor del atributo se incrementa en 1.

#### Configuración
<a name="ddb-en-client-extensions-ACE-conf"></a>

Para especificar qué atributo es un contador, etiquete un atributo de tipo `Long` en el esquema de la tabla.

En el siguiente fragmento se muestra el uso de los valores de inicio e incremento predeterminados para el atributo `counter`.

```
    @DynamoDbAtomicCounter
    public Long getCounter() {...};
    public void setCounter(Long counter) {...};
```

El enfoque del esquema de tabla estático se muestra en el siguiente fragmento. La extensión del contador atómico utiliza un valor inicial de 10 e incrementa el valor en 5 cada vez que se escribe el registro.

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

### Agregue marcas de tiempo con la `AutoGeneratedTimestampRecordExtension`.
<a name="ddb-en-client-extensions-AGTE"></a>

La extensión `AutoGeneratedTimestampRecordExtension` actualiza automáticamente los atributos de tipo `[Instant](https://docs.oracle.com/javase/8/docs/api/java/time/Instant.html)` con una marca de tiempo actual cada vez que el elemento se escribe correctamente en la base de datos. Esta extensión no se carga de forma predeterminada.

#### Configuración
<a name="ddb-en-client-extensions-AGTE-conf"></a>

Para especificar qué atributo actualizar con la marca de tiempo actual, etiquete el atributo `Instant` en el esquema de la tabla.

El atributo `lastUpdate` es el objetivo del comportamiento de la extensión en el siguiente fragmento. Tenga en cuenta el requisito de que el atributo debe ser de tipo `Instant`.

```
    @DynamoDbAutoGeneratedTimestampAttribute
    public Instant getLastUpdate() {...}
    public void setLastUpdate(Instant lastUpdate) {...}
```

El enfoque equivalente del esquema de tabla estático se muestra en el siguiente fragmento.

```
     .addAttribute(Instant.class, a -> a.name("lastUpdate")
                                        .getter(Customer::getLastUpdate)
                                        .setter(Customer::setLastUpdate)
                                        // Applying the 'autoGeneratedTimestamp' tag to the attribute.
                                        .tags(AutoGeneratedTimestampRecordExtension.AttributeTags.autoGeneratedTimestampAttribute())
```

### Genere un UUID con AutoGeneratedUuidExtension
<a name="ddb-en-client-extensions-AGUE"></a>

La extensión `AutoGeneratedUuidExtension` genera un UUID (identificador único universal) único para un atributo cuando se escribe un registro nuevo en la base de datos. Utiliza el método [UUID.randomUUID()](https://docs.oracle.com/javase/8/docs/api/java/util/UUID.html#randomUUID--) de Java JDK y lo aplica a los atributos de tipo `java.lang.String`. Esta extensión no se carga de forma predeterminada.

#### Configuración
<a name="ddb-en-client-extensions-AGUE-conf"></a>

El atributo `uniqueId` es el objetivo del comportamiento de la extensión en el siguiente fragmento.

```
    @AutoGeneratedUuidExtension
    public String getUniqueId() {...}
    public void setUniqueId(String uniqueId) {...}
```

El enfoque equivalente del esquema de tabla estático se muestra en el siguiente fragmento.

```
     .addAttribute(String.class, a -> a.name("uniqueId")
                                        .getter(Customer::getUniqueId)
                                        .setter(Customer::setUniqueId)
                                        // Applying the 'autoGeneratedUuid' tag to the attribute.
                                        .tags(AutoGeneratedUuidExtension.AttributeTags.autoGeneratedUuidAttribute())
```

Si quiere que la extensión rellene el UUID solo para los métodos `putItem` y no para los métodos `updateItem`, añade la anotación [comportamiento de actualización](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/UpdateBehavior.html) como se muestra en el siguiente fragmento.

```
    @AutoGeneratedUuidExtension
    @DynamoDbUpdateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS)
    public String getUniqueId() {...}
    public void setUniqueId(String uniqueId) {...}
```

Si utiliza el método de esquema de tabla estática, use el siguiente código equivalente.

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

# Ejemplo de extensión personalizada
<a name="ddb-en-client-extensions-custom"></a>

Puede crear extensiones personalizadas implementando la interfaz `DynamoDbEnhancedClientExtension`. La siguiente clase de extensión personalizada muestra un método `beforeWrite()` que usa una expresión de actualización para establecer un atributo `registrationDate` si el elemento de la base de datos aún no tiene uno.

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