

# Programación de DynamoDB con AWS SDK for Java 2.x
<a name="ProgrammingWithJava"></a>

Esta guía de programación ofrece orientación a los programadores que desean utilizar Amazon DynamoDB con Java. La guía abarca diferentes conceptos, como las capas de abstracción, la administración de la configuración, la gestión de errores, el control de las políticas de reintentos y la administración de keep-alive.

**Topics**
+ [Acerca del AWS SDK for Java 2.x](#AboutProgrammingWithJavaSDK)
+ [Introducción](#GetStartedProgrammingWithJavaSDK)
+ [Documentación del SDK para Java 2.x](#ProgrammingWithJavaUseDoc)
+ [Interfaces admitidas](#JavaInterfaces)
+ [Ejemplos de código adicionales](#AdditionalCodeEx)
+ [Programación síncrona y asíncrona](#SyncAsyncProgramming)
+ [Clientes de HTTP](#HttpClients)
+ [Config](#ConfigHttpClient)
+ [Gestión de errores](#JavaErrorHandling)
+ [ID de solicitud de AWS](#JavaRequestID)
+ [Registro](#JavaLogging)
+ [Paginación](#JavaPagination)
+ [Anotaciones de clases de datos](#JavaDataClassAnnotation)

## Acerca del AWS SDK for Java 2.x
<a name="AboutProgrammingWithJavaSDK"></a>

Puede acceder a DynamoDB desde Java utilizando el AWS SDK para Java oficial. El SDK para Java tiene dos versiones: 1.x y 2.x. El 12 de enero de 2024 se [anunció](https://aws.amazon.com/blogs/developer/announcing-end-of-support-for-aws-sdk-for-java-v1-x-on-december-31-2025/) el fin de soporte de la versión 1.x. El 31 de julio de 2024 entrará en modo de mantenimiento y está previsto que se deje de dar soporte el 31 de diciembre de 2025. Para nuevos desarrollos, recomendamos encarecidamente el uso de la versión 2.x, que se lanzó por primera vez en 2018. Esta guía hace referencia exclusivamente a la versión 2.x y se centra únicamente en las partes del SDK relevantes para DynamoDB.

Para obtener más información sobre el mantenimiento y el soporte de los AWS SDK, consulte [AWS SDK and Tools maintenance policy](https://docs.aws.amazon.com/sdkref/latest/guide/maint-policy.html) y [AWS SDKs and Tools version support matrix](https://docs.aws.amazon.com/sdkref/latest/guide/version-support-matrix.html) en la *Guía de referencia de los SDK y las herramientas de AWS*.

AWS SDK for Java 2.x constituye un cambio importante con respecto a la base de código de la versión 1.x. El SDK para Java 2.x admite las características modernas de Java, como la E/S sin bloqueo introducida en Java 8. El SDK para Java 2.x también admite implementaciones de clientes HTTP conectables para ofrecer más flexibilidad y opciones de configuración para las conexiones de red.

Un cambio notable entre el SDK para Java 1.x y el SDK para Java 2.x es el uso de un nuevo nombre del paquete. El SDK para Java 1.x usa el nombre del paquete `com.amazonaws`, mientras que el SDK para Java 2.x usa `software.amazon.awssdk`. Del mismo modo, los artefactos de Maven para el SDK para Java 1.x utilizan el `groupId` de `com.amazonaws`, mientras que los artefactos del SDK para Java 2.x utilizan el `groupId` de `software.amazon.awssdk`.

**importante**  
El AWS SDK para Java 1.x tiene un paquete de DynamoDB denominado `com.amazonaws.dynamodbv2`. El “v2” en el nombre del paquete no indica que sea para Java 2 (J2SE). Indica que el paquete admite la [segunda versión](CurrentAPI.md) de la API de bajo nivel de DynamoDB en lugar de la [versión original](Appendix.APIv20111205.md) de la API de bajo nivel.

### Compatibilidad con las versiones de Java
<a name="SupportedJavaVersions"></a>

El AWS SDK for Java 2.x proporciona compatibilidad total con las [versiones de Java](https://github.com/aws/aws-sdk-java-v2?tab=readme-ov-file#maintenance-and-support-for-java-versions) basadas en soporte a largo plazo (LTS).

## Introducción a la AWS SDK for Java 2.x
<a name="GetStartedProgrammingWithJavaSDK"></a>

En el siguiente tutorial se muestra cómo utilizar [Apache Maven](https://maven.apache.org/) para definir las dependencias del SDK para Java 2.x. En este tutorial también se explica cómo escribir el código que se conecta a DynamoDB para enumerar las tablas de DynamoDB disponibles. El tutorial de esta guía se basa en el tutorial [Get started with the AWS SDK for Java 2.x](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html) de la *Guía para desarrolladores de AWS SDK for Java 2.x*. Hemos modificado este tutorial para realizar llamadas a DynamoDB en lugar de a Amazon S3.

**Topics**
+ [Paso 1: Prepararse para el tutorial](#GetStartedJavaSetup)
+ [Paso 2: Crear el proyecto](#GetStartedJavaProjectSetup)
+ [Paso 3: Escribir el código](#GetStartedJavaCode)
+ [Paso 4: Compilar y ejecutar la aplicación](#GetStartedRunJava)

### Paso 1: Prepararse para el tutorial
<a name="GetStartedJavaSetup"></a>

Antes de empezar este tutorial, necesitará:
+ Permiso para acceder a DynamoDB.
+ Un entorno de desarrollo de Java que esté configurado con acceso de inicio de sesión único a los Servicios de AWS para utilizar el Portal de acceso a AWS.

Para prepararse para este tutorial, siga las instrucciones de [Setup overview](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html#setup-overview) de la *Guía para desarrolladores de AWS SDK for Java 2.x*. Cuando [haya configurado su entorno de desarrollo con acceso de inicio de sesión único](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html#setup-credentials) para el SDK de Java y tenga una [sesión activa en el portal de acceso a AWS](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html#setup-login-sso), continúe con el [paso 2](#GetStartedJavaProjectSetup) de este tutorial.

### Paso 2: Crear el proyecto
<a name="GetStartedJavaProjectSetup"></a>

Para crear el proyecto de este tutorial, ejecute un comando de Maven que le pida información sobre cómo configurar el proyecto. Una vez introducidas y confirmadas todas las entradas, Maven finaliza la construcción del proyecto al crear un archivo `pom.xml` y archivos stub de Java.

1. Abra una ventana de terminal o línea de comandos y acceda a un directorio de su elección, como su carpeta `Desktop` o `Home`.

1. Introduzca el siguiente comando en el terminal y pulse **Intro**.

   ```
   mvn archetype:generate \
      -DarchetypeGroupId=software.amazon.awssdk \
      -DarchetypeArtifactId=archetype-app-quickstart \
      -DarchetypeVersion=2.22.0
   ```

1. Cuando se le solicite, introduzca el valor que aparece en la segunda columna.    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/amazondynamodb/latest/developerguide/ProgrammingWithJava.html)

1. Después de introducir el último valor, Maven muestra una lista de las opciones que ha elegido. Para confirmarlas, introduzca **Y**. O bien, introduzca **N** y, a continuación, vuelva a introducir las opciones.

Maven crea una carpeta del proyecto llamada `getstarted` basándose en el valor de `artifactId` que haya introducido. En la carpeta `getstarted`, busque un archivo llamado `README.md` que pueda revisar, un archivo `pom.xml` y un directorio `src`.

Maven crea el árbol de directorios siguiente.

```
getstarted
 ├── README.md
 ├── pom.xml
 └── src
     ├── main
     │   ├── java
     │   │   └── org
     │   │       └── example
     │   │           ├── App.java
     │   │           ├── DependencyFactory.java
     │   │           └── Handler.java
     │   └── resources
     │       └── simplelogger.properties
     └── test
         └── java
             └── org
                 └── example
                     └── HandlerTest.java
 
 10 directories, 7 files
```

A continuación se muestra el contenido del archivo de proyecto `pom.xml`.

#### `pom.xml`
<a name="ProjectSetupCollapse2"></a>

La sección `dependencyManagement` contiene una dependencia a AWS SDK for Java 2.x y la sección `dependencies` a Amazon DynamoDB. La especificación de estas dependencias obliga a Maven a incluir los archivos `.jar` correspondientes en la ruta de clases de Java. De forma predeterminada, el SDK de AWS no incluye todas las clases para todos los Servicios de AWS. En el caso de DynamoDB, si utiliza la interfaz de bajo nivel, debería tener una dependencia en el artefacto `dynamodb`. O bien, si utiliza la interfaz de alto nivel, en el artefacto `dynamodb-enhanced`. Si no incluye las dependencias pertinentes, su código no puede compilarse. El proyecto usa Java 1.8 debido al valor `1.8` de las `maven.compiler.target` propiedades `maven.compiler.source`.

```
<?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 
     <groupId>org.example</groupId>
     <artifactId>getstarted</artifactId>
     <version>1.0-SNAPSHOT</version>
     <packaging>jar</packaging>
     <properties>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <maven.compiler.source>1.8</maven.compiler.source>
         <maven.compiler.target>1.8</maven.compiler.target>
         <maven.shade.plugin.version>3.2.1</maven.shade.plugin.version>
         <maven.compiler.plugin.version>3.6.1</maven.compiler.plugin.version>
         <exec-maven-plugin.version>1.6.0</exec-maven-plugin.version>
         <aws.java.sdk.version>2.22.0</aws.java.sdk.version> <-------- SDK version picked up from archetype version.
         <slf4j.version>1.7.28</slf4j.version>
         <junit5.version>5.8.1</junit5.version>
     </properties>
 
     <dependencyManagement>
         <dependencies>
             <dependency>
                 <groupId>software.amazon.awssdk</groupId>
                 <artifactId>bom</artifactId>
                 <version>${aws.java.sdk.version}</version>
                 <type>pom</type>
                 <scope>import</scope>
             </dependency>
         </dependencies>
     </dependencyManagement>
 
     <dependencies>
         <dependency>
             <groupId>software.amazon.awssdk</groupId>
             <artifactId>dynamodb</artifactId>  <-------- DynamoDB dependency
             <exclusions>
                 <exclusion>
                     <groupId>software.amazon.awssdk</groupId>
                     <artifactId>netty-nio-client</artifactId>
                 </exclusion>
                 <exclusion>
                     <groupId>software.amazon.awssdk</groupId>
                     <artifactId>apache-client</artifactId>
                 </exclusion>
             </exclusions>
         </dependency>
 
         <dependency>
             <groupId>software.amazon.awssdk</groupId>
             <artifactId>sso</artifactId> <-------- Required for identity center authentication.
         </dependency>
 
         <dependency>
             <groupId>software.amazon.awssdk</groupId>
             <artifactId>ssooidc</artifactId> <-------- Required for identity center authentication.
         </dependency>
 
         <dependency>
             <groupId>software.amazon.awssdk</groupId>
             <artifactId>apache-client</artifactId> <-------- HTTP client specified.
             <exclusions>
                 <exclusion>
                     <groupId>commons-logging</groupId>
                     <artifactId>commons-logging</artifactId>
                 </exclusion>
             </exclusions>
         </dependency>
 
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
             <version>${slf4j.version}</version>
         </dependency>
 
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-simple</artifactId>
             <version>${slf4j.version}</version>
         </dependency>
 
         <!-- Needed to adapt Apache Commons Logging used by Apache HTTP Client to Slf4j to avoid
         ClassNotFoundException: org.apache.commons.logging.impl.LogFactoryImpl during runtime -->
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>jcl-over-slf4j</artifactId>
             <version>${slf4j.version}</version>
         </dependency>
 
         <!-- Test Dependencies -->
         <dependency>
             <groupId>org.junit.jupiter</groupId>
             <artifactId>junit-jupiter</artifactId>
             <version>${junit5.version}</version>
             <scope>test</scope>
         </dependency>
     </dependencies>
 
     <build>
         <plugins>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
                 <version>${maven.compiler.plugin.version}</version>
             </plugin>
         </plugins>
     </build>
 
 </project>
```

### Paso 3: Escribir el código
<a name="GetStartedJavaCode"></a>

En el código siguiente se muestra la clase `App` que crea Maven. El método `main` es el punto de entrada a la aplicación, que crea una instancia de la `Handler` clase y, a continuación, llama a su método `sendRequest`.

#### Clase `App`
<a name="projectsetup-collapse2"></a>

```
package org.example;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class App {
     private static final Logger logger = LoggerFactory.getLogger(App.class);
 
     public static void main(String... args) {
         logger.info("Application starts");
 
         Handler handler = new Handler();
         handler.sendRequest();
 
         logger.info("Application ends");
     }
 }
```

La clase `DependencyFactory` que crea Maven contiene el método de fábrica `dynamoDbClient` que crea y devuelve una instancia de [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html). La instancia `DynamoDbClient` usa una instancia del cliente HTTP basado en Apache. Esto se debe a que especificó `apache-client` cuando Maven preguntó qué cliente HTTP usar.

En el siguiente código se muestra la clase `DependencyFactory`.

#### Clase DependencyFactory
<a name="code-collapse2"></a>

```
package org.example;
 
 import software.amazon.awssdk.http.apache.ApacheHttpClient;
 import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
 
 /**
  * The module containing all dependencies required by the {@link Handler}.
  */
 public class DependencyFactory {
 
     private DependencyFactory() {}
 
     /**
      * @return an instance of DynamoDbClient
      */
     public static DynamoDbClient dynamoDbClient() {
         return DynamoDbClient.builder()
                        .httpClientBuilder(ApacheHttpClient.builder())
                        .build();
     }
 }
```

La clase `Handler` contiene la lógica principal del programa. Cuando se crea una instancia de `Handler` en la clase `App`, `DependencyFactory` proporciona el cliente de servicios `DynamoDbClient`. El código utiliza la instancia de `DynamoDbClient` para llamar a DynamoDB.

Maven genera la siguiente clase `Handler` con un comentario `TODO`. El siguiente paso del tutorial reemplaza el comentario *`TODO`* por código.

#### Clase `Handler`, generada por Maven
<a name="code-collapsible3"></a>

```
package org.example;
 
 import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
 
 
 public class Handler {
     private final DynamoDbClient dynamoDbClient;
 
     public Handler() {
         dynamoDbClient = DependencyFactory.dynamoDbClient();
     }
 
     public void sendRequest() {
         // TODO: invoking the API calls using dynamoDbClient.
     }
 }
```

Para completar la lógica, reemplace todo el contenido de la clase `Handler` por el código siguiente. El método `sendRequest` se rellena y se añaden las importaciones necesarias.

#### Clase `Handler`, implementada
<a name="code-collapse4"></a>

El siguiente código usa la instancia [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html) para recuperar una lista de tablas existentes. Si existen tablas para una cuenta y una Región de AWS determinadas, el código usa la instancia de `Logger` para registrar los nombres de esas tablas.

```
package org.example;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
 import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse;
 
 
 public class Handler {
     private final DynamoDbClient dynamoDbClient;
 
     public Handler() {
         dynamoDbClient = DependencyFactory.dynamoDbClient();
     }
 
     public void sendRequest() {
         Logger logger = LoggerFactory.getLogger(Handler.class);
 
         logger.info("calling the DynamoDB API to get a list of existing tables");
         ListTablesResponse response = dynamoDbClient.listTables();
 
         if (!response.hasTableNames()) {
             logger.info("No existing tables found for the configured account & region");
         } else {
             response.tableNames().forEach(tableName -> logger.info("Table: " + tableName));
         }
     }
 }
```

### Paso 4: Compilar y ejecutar la aplicación
<a name="GetStartedRunJava"></a>

Una vez creado el proyecto y que contenga la clase `Handler` completa, compile y ejecute la aplicación.

1. Asegúrese de que tenga una sesión de AWS IAM Identity Center activa. Para ello, ejecute el comando de la AWS Command Line Interface (AWS CLI) `aws sts get-caller-identity` y compruebe la respuesta. Si no tiene una sesión activa, consulte [Sign in using the AWS CLI](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html#setup-login-sso) para obtener instrucciones.

1. Abra una ventana de terminal o símbolo del sistema y desplácese hasta el directorio del proyecto `getstarted`.

1. Para compilar su proyecto, ejecute el comando siguiente:

   ```
   mvn clean package
   ```

1. Para ejecutar la aplicación, ejecute el comando siguiente:

   ```
   mvn exec:java -Dexec.mainClass="org.example.App"
   ```

Después de ver el archivo, elimine el objeto y, a continuación, elimine el bucket.

#### Success
<a name="GetStartedSuccessJava"></a>

Si su proyecto de Maven se creó y ejecutó sin errores, ¡enhorabuena\$1 Ha creado correctamente su primera aplicación de Java con el SDK para Java 2.x.

#### Eliminación
<a name="GetStartedCleanupJava"></a>

Para limpiar los recursos que ha creado en este tutorial, elimine la carpeta del proyecto `getstarted`.

## Revisión de la documentación de AWS SDK for Java 2.x
<a name="ProgrammingWithJavaUseDoc"></a>

La [Guía para desarrolladores de AWS SDK for Java 2.x](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/home.html) cubre todos los aspectos del SDK en todos los Servicios de AWS. Le aconsejamos que revise primero los siguientes temas:
+ [Migrate from version 1.x to 2.x](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/migration.html): incluye una explicación detallada de las diferencias entre la versión 1.x y la 2.x. Este tema también incluye instrucciones sobre cómo utilizar ambas versiones principales en paralelo.
+ [DynamoDB guide for Java 2.x SDK](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/examples-dynamodb.html): muestra cómo realizar operaciones básicas de DynamoDB, tales como crear una tabla, manipular elementos y recuperar elementos. En estos ejemplos se utiliza la interfaz de bajo nivel. Java tiene varias interfaces, tal como se explica en la siguiente sección: [Interfaces admitidas](#JavaInterfaces).

**sugerencia**  
Tras revisar estos temas, agregue a sus favoritos la [Referencia de la API de AWS SDK for Java 2.x](https://sdk.amazonaws.com/java/api/latest/). Abarca todos los Servicios de AWS y le recomendamos que la utilice como referencia principal de la API.

## Interfaces admitidas
<a name="JavaInterfaces"></a>

AWS SDK for Java 2.x admite las siguientes interfaces en función del nivel de abstracción que desee.

**Topics**
+ [Interfaz de bajo nivel](#LowLevelInterface)
+ [Interfaz de alto nivel](#HighLevelInterface)
+ [Interfaz de documentos](#DocumentInterface)
+ [Comparación de interfaces con un ejemplo de `Query`](#CompareJavaInterfacesQueryEx)

### Interfaz de bajo nivel
<a name="LowLevelInterface"></a>

La interfaz de bajo nivel proporciona una asignación uno a uno con la API del servicio subyacente. Todas las API de DynamoDB están disponibles a través de esta interfaz. Esto significa que la interfaz de bajo nivel puede proporcionar una funcionalidad completa, pero suele ser más detallada y su uso es más complejo. Por ejemplo, debe usar las funciones `.s()` para incluir cadenas y las funciones `.n()` para incluir números. En el siguiente ejemplo de [PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html) se inserta un elemento mediante la interfaz de bajo nivel.

```
import org.slf4j.*;
import software.amazon.awssdk.http.crt.AwsCrtHttpClient;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.*;

import java.util.Map;

public class PutItem {

    // Create a DynamoDB client with the default settings connected to the DynamoDB
    // endpoint in the default region based on the default credentials provider chain.
    private static final DynamoDbClient DYNAMODB_CLIENT = DynamoDbClient.create();
    private static final Logger LOGGER = LoggerFactory.getLogger(PutItem.class);

    private void putItem() {
        PutItemResponse response = DYNAMODB_CLIENT.putItem(PutItemRequest.builder()
                .item(Map.of(
                        "pk", AttributeValue.builder().s("123").build(),
                        "sk", AttributeValue.builder().s("cart#123").build(),
                        "item_data", AttributeValue.builder().s("YourItemData").build(),
                        "inventory", AttributeValue.builder().n("500").build()
                        // ... more attributes ...
                ))
                .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)
                .tableName("YourTableName")
                .build());
        LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)");
    }
}
```

### Interfaz de alto nivel
<a name="HighLevelInterface"></a>

La interfaz de alto nivel de AWS SDK for Java 2.x se denomina cliente mejorado de DynamoDB. Esta interfaz proporciona una experiencia de creación de código más idiomática.

El cliente mejorado ofrece una forma de asignar entre las clases de datos del lado del cliente y las tablas de DynamoDB diseñadas para almacenar esos datos. Puede definir las relaciones entre las tablas y sus correspondientes clases de modelo en el código. Luego, puede confiar en el SDK para administrar la manipulación de los tipos de datos. Para obtener más información sobre el cliente mejorado, consulte [DynamoDB enhanced client API](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/dynamodb-enhanced-client.html) en la *Guía parea desarrolladores de AWS SDK for Java 2.x*.

El siguiente ejemplo de [PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html) utiliza la interfaz de alto nivel. En este ejemplo, el `DynamoDbBean` denominado `YourItem` crea un `TableSchema` que permite su uso directo como entrada para la llamada `putItem()`.

```
import org.slf4j.*;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.*;
import software.amazon.awssdk.enhanced.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity;

public class DynamoDbEnhancedClientPutItem {
    private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build();
    private static final DynamoDbTable<YourItem> DYNAMODB_TABLE = ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.fromBean(YourItem.class));
    private static final Logger LOGGER = LoggerFactory.getLogger(PutItem.class);

    private void putItem() {
        PutItemEnhancedResponse<YourItem> response = DYNAMODB_TABLE.putItemWithResponse(PutItemEnhancedRequest.builder(YourItem.class)
                .item(new YourItem("123", "cart#123", "YourItemData", 500))
                .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)
                .build());
        LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)");
    }

    @DynamoDbBean
    public static class YourItem {

        public YourItem() {}

        public YourItem(String pk, String sk, String itemData, int inventory) {
            this.pk = pk;
            this.sk = sk;
            this.itemData = itemData;
            this.inventory = inventory;
        }

        private String pk;
        private String sk;
        private String itemData;

        private int inventory;

        @DynamoDbPartitionKey
        public void setPk(String pk) {
            this.pk = pk;
        }

        public String getPk() {
            return pk;
        }

        @DynamoDbSortKey
        public void setSk(String sk) {
            this.sk = sk;
        }

        public String getSk() {
            return sk;
        }

        public void setItemData(String itemData) {
            this.itemData = itemData;
        }

        public String getItemData() {
            return itemData;
        }

        public void setInventory(int inventory) {
            this.inventory = inventory;
        }

        public int getInventory() {
            return inventory;
        }
    }
}
```

AWS SDK para Java 1.x tiene su propia interfaz de alto nivel, a la que suele hacer referencia su `DynamoDBMapper` de clase principal. El AWS SDK for Java 2.x se publica en un paquete separado (y un artefacto de Maven) denominado `software.amazon.awssdk.enhanced.dynamodb`. El SDK para Java 2.x a menudo se denomina por su `DynamoDbEnhancedClient` de clase principal.

#### Interfaz de alto nivel que utiliza clases de datos inmutables
<a name="HighLevelInterfaceImmutableDataClasses"></a>

La característica de asignación de la API de cliente mejorado de DynamoDB también funciona con clases de datos inmutables. Una clase inmutable solo tiene getters y requiere una clase constructora que el SDK utiliza para crear instancias de la clase. La inmutabilidad en Java es un estilo común que los desarrolladores pueden usar para crear clases sin efectos secundarios. El comportamiento de estas clases es más predecible en aplicaciones complejas de varios subprocesos. En lugar de usar la anotación `@DynamoDbBean` como se muestra en el [High-level interface example](#highleveleg), las clases inmutables usan la anotación `@DynamoDbImmutable`, que toma la clase de generador como entrada.

En el siguiente ejemplo, se utiliza la clase de generador `DynamoDbEnhancedClientImmutablePutItem` como entrada para crear un esquema de tabla. A continuación, el ejemplo proporciona el esquema como entrada para la llamada a la API [PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html).

```
import org.slf4j.*;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity;

public class DynamoDbEnhancedClientImmutablePutItem {
    private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build();
    private static final DynamoDbTable<YourImmutableItem> DYNAMODB_TABLE = ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.fromImmutableClass(YourImmutableItem.class));
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedClientImmutablePutItem.class);

    private void putItem() {
        PutItemEnhancedResponse<YourImmutableItem> response = DYNAMODB_TABLE.putItemWithResponse(PutItemEnhancedRequest.builder(YourImmutableItem.class)
                .item(YourImmutableItem.builder()
                                        .pk("123")
                                        .sk("cart#123")
                                        .itemData("YourItemData")
                                        .inventory(500)
                                        .build())
                .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)
                .build());
        LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)");
    }
}
```

En el siguiente ejemplo, se muestra la clase de datos inmutable.

```
@DynamoDbImmutable(builder = YourImmutableItem.YourImmutableItemBuilder.class)
class YourImmutableItem {
    private final String pk;
    private final String sk;
    private final String itemData;
    private final int inventory;
    public YourImmutableItem(YourImmutableItemBuilder builder) {
        this.pk = builder.pk;
        this.sk = builder.sk;
        this.itemData = builder.itemData;
        this.inventory = builder.inventory;
    }

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

    @DynamoDbPartitionKey
    public String getPk() {
        return pk;
    }

    @DynamoDbSortKey
    public String getSk() {
        return sk;
    }

    public String getItemData() {
        return itemData;
    }

    public int getInventory() {
        return inventory;
    }

    static final class YourImmutableItemBuilder {
        private String pk;
        private String sk;
        private String itemData;
        private int inventory;

        private YourImmutableItemBuilder() {}

        public YourImmutableItemBuilder pk(String pk) { this.pk = pk; return this; }
        public YourImmutableItemBuilder sk(String sk) { this.sk = sk; return this; }
        public YourImmutableItemBuilder itemData(String itemData) { this.itemData = itemData; return this; }
        public YourImmutableItemBuilder inventory(int inventory) { this.inventory = inventory; return this; }

        public YourImmutableItem build() { return new YourImmutableItem(this); }
    }
}
```

#### Interfaz de alto nivel que utiliza clases de datos inmutables y bibliotecas de generación reutilizables de terceros
<a name="ImmutableDataClassesThirdPartyBoilerplateGenLib"></a>

Las clases de datos inmutables (que se muestran en el ejemplo anterior) requieren algo de código reutilizable. Por ejemplo, la lógica de getter y setter en las clases de datos, además de las clases `Builder`. Las bibliotecas de terceros, como [Project Lombok](https://projectlombok.org/), pueden ayudarlo a generar ese tipo de código reutilizable. Reducir la mayor parte del código reutilizable ayuda a limitar la cantidad de código necesaria para trabajar con clases de datos inmutables y con el SDK de AWS. Además, esto mejora la productividad y la legibilidad del código. Para obtener más información, consulte [Use third-party libraries, such as Lombok](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-use-immut.html#ddb-en-client-use-immut-lombok) en la *Guía para desarrolladores de AWS SDK for Java 2.x*.

En el siguiente ejemplo se muestra cómo Project Lombok simplifica el código necesario para utilizar la API de cliente mejorada de DynamoDB.

```
import org.slf4j.*;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity;

public class DynamoDbEnhancedClientImmutableLombokPutItem {

    private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build();
    private static final DynamoDbTable<YourImmutableLombokItem> DYNAMODB_TABLE = ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.fromImmutableClass(YourImmutableLombokItem.class));
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedClientImmutableLombokPutItem.class);

    private void putItem() {
        PutItemEnhancedResponse<YourImmutableLombokItem> response = DYNAMODB_TABLE.putItemWithResponse(PutItemEnhancedRequest.builder(YourImmutableLombokItem.class)
                .item(YourImmutableLombokItem.builder()
                        .pk("123")
                        .sk("cart#123")
                        .itemData("YourItemData")
                        .inventory(500)
                        .build())
                .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)
                .build());
        LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)");
    }
}
```

En el siguiente ejemplo se muestra el objeto de datos inmutables de la clase de datos inmutables.

```
import lombok.*;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.*;

@Builder
@DynamoDbImmutable(builder = YourImmutableLombokItem.YourImmutableLombokItemBuilder.class)
@Value
public class YourImmutableLombokItem {

    @Getter(onMethod_=@DynamoDbPartitionKey)
    String pk;
    @Getter(onMethod_=@DynamoDbSortKey)
    String sk;
    String itemData;
    int inventory;
}
```

La clase `YourImmutableLombokItem` usa las siguientes anotaciones proporcionadas por Project Lombok y el AWS SDK:
+ [@Builder](https://projectlombok.org/features/Builder): produce API de creador complejas para las clases de datos que proporciona Project Lombok.
+ [@DynamoDbImmutable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/annotations/DynamoDbImmutable.html): identifica la clase `DynamoDbImmutable` como una anotación de entidad asignable de DynamoDB proporcionada por el AWS SDK.
+ [@Value](https://projectlombok.org/features/Value): la variante inmutable de `@Data`. Todos los campos se convierten en privados y definitivos de forma predeterminada y los setters no se generan. El proyecto Lombok proporciona esta anotación.

### Interfaz de documentos
<a name="DocumentInterface"></a>

La interfaz de documentos de AWS SDK for Java 2.x evita la necesidad de especificar descriptores de tipos de datos. Los tipos de datos quedan implícitos en la propia semántica de los datos. Esta interfaz de documentos es similar a la interfaz de documentos de AWS SDK para Java 1.x, pero se ha rediseñado.

El siguiente [Document interface example](#DocInterfaceEg) muestra la llamada `PutItem` expresada mediante la interfaz de documentos. En el ejemplo también se utiliza EnhancedDocument. Para ejecutar comandos en una tabla de DynamoDB mediante la API de documentos mejorada, primero debe asociar la tabla al esquema de la tabla de documentos para crear un objeto de recurso `DynamoDBTable`. El generador de un esquema de tabla de documentos requiere la clave de índice principal y proveedores de convertidores de atributos.

Puede utilizar `AttributeConverterProvider.defaultProvider()` para convertir los atributos de los documentos de los tipos predeterminados. Puede cambiar el comportamiento predeterminado general con una implementación de `AttributeConverterProvider` personalizada. También puede cambiar el conversor de un solo atributo. En la [Guía de referencia de SDK y herramientas de AWS](https://docs.aws.amazon.com/sdkref/latest/guide/version-support-matrix.html) se proporcionan más detalles y ejemplos sobre cómo utilizar los convertidores personalizados. Su uso principal es para los atributos de las clases de dominio que no tienen un conversor predeterminado disponible. Con un conversor personalizado, puede proporcionar al SDK la información necesaria para escribir o leer en DynamoDB.

```
import org.slf4j.*;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument;
import software.amazon.awssdk.enhanced.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.model.ReturnConsumedCapacity;

public class DynamoDbEnhancedDocumentClientPutItem {
    private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build();
    private static final DynamoDbTable<EnhancedDocument> DYNAMODB_TABLE =
            ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.documentSchemaBuilder()
                            .addIndexPartitionKey(TableMetadata.primaryIndexName(),"pk", AttributeValueType.S)
                            .addIndexSortKey(TableMetadata.primaryIndexName(), "sk", AttributeValueType.S)
                            .attributeConverterProviders(AttributeConverterProvider.defaultProvider())
                            .build());

    private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedDocumentClientPutItem.class);

    private void putItem() {
        PutItemEnhancedResponse<EnhancedDocument> response = DYNAMODB_TABLE.putItemWithResponse(
                        PutItemEnhancedRequest.builder(EnhancedDocument.class)
                                .item(
                                    EnhancedDocument.builder()
                                            .attributeConverterProviders(AttributeConverterProvider.defaultProvider())
                                            .putString("pk", "123")
                                            .putString("sk", "cart#123")
                                            .putString("item_data", "YourItemData")
                                            .putNumber("inventory", 500)
                                            .build())
                                .returnConsumedCapacity(ReturnConsumedCapacity.TOTAL)
                                .build());
        LOGGER.info("PutItem call consumed [" + response.consumedCapacity().capacityUnits() + "] Write Capacity Unites (WCU)");
    }

}
```

Para convertir documentos JSON a los tipos de datos nativos de Amazon DynamoDB y viceversa, puede utilizar los siguientes métodos de la utilidad:
+ [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html#fromJson(java.lang.String)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html#fromJson(java.lang.String)): crea una nueva instancia de EnhancedDocument a partir de una cadena JSON.
+ [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html#toJson()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html#toJson()): crea una representación de la cadena JSON del documento que puede utilizar en su aplicación como cualquier otro objeto JSON.

### Comparación de interfaces con un ejemplo de `Query`
<a name="CompareJavaInterfacesQueryEx"></a>

En esta sección, se muestra la misma llamada a [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html) expresada mediante las distintas interfaces. Para ajustar los resultados de estas consultas, tenga en cuenta lo siguiente:
+ DynamoDB se centra en un valor de clave de partición concreto, por lo que debe especificar la clave de partición por completo.
+ Para que la consulta se dirija únicamente a los artículos del carrito, la clave de clasificación tiene una expresión de condición clave que utiliza `begins_with`.
+ Utilizamos `limit()` para que la consulta devuelva un máximo de 100 elementos.
+ Establecemos el valor `scanIndexForward` en false. Los resultados se muestran en orden de bytes en UTF-8, lo que normalmente significa que el artículo del carrito con el número más bajo se devuelve primero. Al establecer `scanIndexForward` en false, invertimos el orden y se devuelve primero el elemento del carrito con el número más alto.
+ Aplicamos un filtro para eliminar cualquier resultado que no coincida con los criterios. Los datos que se filtran consumen capacidad de lectura independientemente de si el elemento coincide con el filtro.

**Example `Query` mediante la interfaz de bajo nivel**  
En el siguiente ejemplo, se consulta una tabla denominada `YourTableName` mediante una `keyConditionExpression`. Esto limita la consulta a un valor de clave de partición específico y a un valor de clave de clasificación que comience por un valor de prefijo específico. Estas condiciones clave limitan la cantidad de datos leídos de DynamoDB. Por último, la consulta aplica un filtro a los datos obtenidos de DynamoDB mediante una `filterExpression`.  

```
import org.slf4j.*;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.*;

import java.util.Map;

public class Query {

    // Create a DynamoDB client with the default settings connected to the DynamoDB 
    // endpoint in the default region based on the default credentials provider chain.
    private static final DynamoDbClient DYNAMODB_CLIENT = DynamoDbClient.builder().build();
    private static final Logger LOGGER = LoggerFactory.getLogger(Query.class);

    private static void query() {
        QueryResponse response = DYNAMODB_CLIENT.query(QueryRequest.builder()
                .expressionAttributeNames(Map.of("#name", "name"))
                .expressionAttributeValues(Map.of(
                    ":pk_val", AttributeValue.fromS("id#1"),
                    ":sk_val", AttributeValue.fromS("cart#"),
                    ":name_val", AttributeValue.fromS("SomeName")))
                .filterExpression("#name = :name_val")
                .keyConditionExpression("pk = :pk_val AND begins_with(sk, :sk_val)")
                .limit(100)
                .scanIndexForward(false)
                .tableName("YourTableName")
                .build());

        LOGGER.info("nr of items: " + response.count());
        LOGGER.info("First item pk: " + response.items().get(0).get("pk"));
        LOGGER.info("First item sk: " + response.items().get(0).get("sk"));
    }
}
```

**Example `Query` mediante la interfaz de documentos**  
En el siguiente ejemplo, se consulta una tabla denominada `YourTableName` mediante la interfaz de documentos.  

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.document.EnhancedDocument;
import software.amazon.awssdk.enhanced.dynamodb.model.*;

import java.util.Map;

public class DynamoDbEnhancedDocumentClientQuery {

    // Create a DynamoDB client with the default settings connected to the DynamoDB 
    // endpoint in the default region based on the default credentials provider chain.
    private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build();
    private static final DynamoDbTable<EnhancedDocument> DYNAMODB_TABLE =
            ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.documentSchemaBuilder()
                    .addIndexPartitionKey(TableMetadata.primaryIndexName(),"pk", AttributeValueType.S)
                    .addIndexSortKey(TableMetadata.primaryIndexName(), "sk", AttributeValueType.S)
                    .attributeConverterProviders(AttributeConverterProvider.defaultProvider())
                    .build());
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedDocumentClientQuery.class);

    private void query() {
        PageIterable<EnhancedDocument> response = DYNAMODB_TABLE.query(QueryEnhancedRequest.builder()
                .filterExpression(Expression.builder()
                        .expression("#name = :name_val")
                        .expressionNames(Map.of("#name", "name"))
                        .expressionValues(Map.of(":name_val", AttributeValue.fromS("SomeName")))
                        .build())
                .limit(100)
                .queryConditional(QueryConditional.sortBeginsWith(Key.builder()
                        .partitionValue("id#1")
                        .sortValue("cart#")
                        .build()))
                .scanIndexForward(false)
                .build());

        LOGGER.info("nr of items: " + response.items().stream().count());
        LOGGER.info("First item pk: " + response.items().iterator().next().getString("pk"));
        LOGGER.info("First item sk: " + response.items().iterator().next().getString("sk"));

    }
}
```

**Example `Query` mediante la interfaz de alto nivel**  
En el siguiente ejemplo, se consulta una tabla denominada `YourTableName` mediante la API de cliente mejorada de DynamoDB.  

```
import org.slf4j.*;
import software.amazon.awssdk.enhanced.dynamodb.*;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.*;
import software.amazon.awssdk.enhanced.dynamodb.model.*;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;

import java.util.Map;

public class DynamoDbEnhancedClientQuery {

    private static final DynamoDbEnhancedClient ENHANCED_DYNAMODB_CLIENT = DynamoDbEnhancedClient.builder().build();
    private static final DynamoDbTable<YourItem> DYNAMODB_TABLE = ENHANCED_DYNAMODB_CLIENT.table("YourTableName", TableSchema.fromBean(DynamoDbEnhancedClientQuery.YourItem.class));
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamoDbEnhancedClientQuery.class);

    private void query() {
        PageIterable<YourItem> response = DYNAMODB_TABLE.query(QueryEnhancedRequest.builder()
                .filterExpression(Expression.builder()
                        .expression("#name = :name_val")
                        .expressionNames(Map.of("#name", "name"))
                        .expressionValues(Map.of(":name_val", AttributeValue.fromS("SomeName")))
                        .build())
                .limit(100)
                .queryConditional(QueryConditional.sortBeginsWith(Key.builder()
                        .partitionValue("id#1")
                        .sortValue("cart#")
                        .build()))
                .scanIndexForward(false)
                .build());

        LOGGER.info("nr of items: " + response.items().stream().count());
        LOGGER.info("First item pk: " + response.items().iterator().next().getPk());
        LOGGER.info("First item sk: " + response.items().iterator().next().getSk());
    }

    @DynamoDbBean
    public static class YourItem {

        public YourItem() {}

        public YourItem(String pk, String sk, String name) {
            this.pk = pk;
            this.sk = sk;
            this.name = name;
        }

        private String pk;
        private String sk;
        private String name;

        @DynamoDbPartitionKey
        public void setPk(String pk) {
            this.pk = pk;
        }

        public String getPk() {
            return pk;
        }

        @DynamoDbSortKey
        public void setSk(String sk) {
            this.sk = sk;
        }

        public String getSk() {
            return sk;
        }

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

        public String getName() {
            return name;
        }
    }
}
```
**Interfaz de alto nivel que utiliza clases de datos inmutables**  
Al realizar una `Query` con las clases de datos inmutables de alto nivel, el código es el mismo que en el ejemplo de la interfaz de alto nivel, excepto en lo que respecta a la construcción de la clase de entidad `YourItem` o `YourImmutableItem`. Consulte el ejemplo [PutItem](#HighLevelImmutableDataClassEg) para obtener más información.
**Interfaz de alto nivel que utiliza clases de datos inmutables y bibliotecas de generación reutilizables de terceros**  
Al realizar una `Query` con las clases de datos inmutables de alto nivel, el código es el mismo que en el ejemplo de la interfaz de alto nivel, excepto en lo que respecta a la construcción de la clase de entidad `YourItem` o `YourImmutableLombokItem`. Consulte el ejemplo [PutItem](#HighLevelImmutableDataClassEg) para obtener más información.

## Ejemplos de código adicionales
<a name="AdditionalCodeEx"></a>

Para ver ejemplos adicionales sobre cómo utilizar DynamoDB con el SDK para Java 2.x, consulte los siguientes repositorios de ejemplos de código:
+ [Ejemplos de código oficiales de una sola acción de AWS](https://docs.aws.amazon.com/code-library/latest/ug/java_2_dynamodb_code_examples.html)
+ [Ejemplos de código de una sola acción actualizados por la comunidad](https://github.com/aws-samples/aws-dynamodb-examples/tree/master/examples/SDK/java)
+ [Ejemplos de código oficiales orientados a escenarios de AWS](https://github.com/aws-samples/aws-dynamodb-examples/tree/master/examples/SDK/java)

## Programación síncrona y asíncrona
<a name="SyncAsyncProgramming"></a>

El AWS SDK for Java 2.x proporciona clientes *síncronos* y *asíncronos* para Servicios de AWS, como DynamoDB.

Las clases `DynamoDbClient` y `DynamoDbEnhancedClient` proporcionan métodos síncronos que bloquean la ejecución de los subprocesos hasta que el cliente recibe una respuesta del servicio. Este cliente es la forma más sencilla de interactuar con DynamoDB si no necesita operaciones asíncronas.

Las clases `DynamoDbAsyncClient` y `DynamoDbEnhancedAsyncClient` proporcionan métodos asíncronos que se ejecutan inmediatamente y devuelven el control al subproceso que realiza la llamada sin esperar una respuesta. El cliente sin bloqueo tiene la ventaja de que ofrece una alta simultaneidad en unos pocos subprocesos, lo que permite gestionar de forma eficiente las solicitudes de E/S con pocos recursos de computación. Esto mejora el rendimiento y la capacidad de respuesta.

El AWS SDK for Java 2.x utiliza la compatibilidad nativa con la E/S sin bloqueo. El AWS SDK para Java 1.x tenía que simular la E/S sin bloqueo.

Los métodos síncronos terminan de ejecutarse antes de que haya una respuesta disponible, de manera que necesita una forma de obtener la respuesta cuando esté lista. Los métodos asíncronos en el AWS SDK para Java devuelven un objeto [https://docs.oracle.com/javase/8/docs/api/index.html?java/util/concurrent/CompletableFuture.html](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/concurrent/CompletableFuture.html) que contiene los resultados de la operación asíncrona en el futuro. Al llamar a `get()` o `join()` en estos objetos `CompletableFuture`, el código se bloquea hasta que el resultado esté disponible. Si los llama al mismo tiempo que realiza la solicitud, el comportamiento es similar al de una llamada síncrona simple.

Para obtener más información sobre la programación asíncrona, consulte [Use asynchronous programming](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/asynchronous.html) en la *Guía para desarrolladores de AWS SDK for Java 2.x*.

## Clientes de HTTP
<a name="HttpClients"></a>

Para dar soporte a cada cliente, existe un cliente HTTP que se encarga de la comunicación con los Servicios de AWS. Puede conectar clientes HTTP alternativos y elegir uno que tenga las características que mejor se adapten a su aplicación. Algunos son más ligeros y otros tienen más opciones de configuración.

Algunos clientes HTTP solo admiten el uso sincrónico, mientras que otros solo admiten el uso asíncrono. Para ver un diagrama de flujo que le ayude a seleccionar el cliente HTTP óptimo para su carga de trabajo, consulte [HTTP client recommendations](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration.html#http-clients-recommend) en la *Guía para desarrolladores de AWS SDK for Java 2.x*.

La siguiente lista presenta algunos de los posibles clientes HTTP:

**Topics**
+ [Cliente HTTP basado en Apache](#ApacheHttpClient)
+ [Cliente HTTP basado en `URLConnection`](#URLConnHttpClient)
+ [Cliente HTTP basado en Netty](#NettyHttpClient)
+ [Cliente HTTP basado en CRT de AWS](#AWSCRTHttpClient)

### Cliente HTTP basado en Apache
<a name="ApacheHttpClient"></a>

La clase [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/apache/ApacheHttpClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/apache/ApacheHttpClient.html) admite clientes de servicios síncronos. Es el cliente HTTP predeterminado para el uso síncrono. Para obtener información sobre la configuración de la clase `ApacheHttpClient`, consulte [Configure the Apache-based HTTP client](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-apache.html) en la *Guía para desarrolladores de AWS SDK for Java 2.x*.

### Cliente HTTP basado en `URLConnection`
<a name="URLConnHttpClient"></a>

La clase [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/urlconnection/UrlConnectionHttpClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/urlconnection/UrlConnectionHttpClient.html) es otra opción para los clientes síncronos. Este cliente se carga más rápido que el cliente HTTP basado en Apache, pero tiene menos características. Para obtener información sobre la configuración de la clase `UrlConnectionHttpClient`, consulte [Configure the URLConnection-based HTTP client](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-url.html) en la *Guía para desarrolladores de AWS SDK for Java 2.x*.

### Cliente HTTP basado en Netty
<a name="NettyHttpClient"></a>

La clase `NettyNioAsyncHttpClient` admite clientes asíncronos. Es la opción predeterminada para el uso asíncrono. Para obtener información sobre la configuración de la clase `NettyNioAsyncHttpClient`, consulte [Configure the Netty-based HTTP client](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-netty.html) en la *Guía para desarrolladores de AWS SDK for Java 2.x*.

### Cliente HTTP basado en CRT de AWS
<a name="AWSCRTHttpClient"></a>

Las nuevas clases `AwsCrtHttpClient` y `AwsCrtAsyncHttpClient` de las bibliotecas AWS Common Runtime (CRT) son otras opciones que admiten clientes síncronos y asíncronos. En comparación con otros clientes HTTP, AWS CRT ofrece:
+ Tiempo de inicio del SDK más rápido
+ Ocupación de menos espacio de memoria
+ Tiempo de latencia reducido
+ Administración del estado de conexión
+ equilibrio de carga de DNS

Para obtener información sobre la configuración de las clases `AwsCrtHttpClient` y `AwsCrtAsyncHttpClient`, consulte [Configure the AWS CRT-based HTTP clients](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-crt.html) en la *Guía para desarrolladores de AWS SDK for Java 2.x*.

El cliente HTTP basado en AWS CRT no es el predeterminado porque, de lo contrario, dejaría de ser compatible con versiones anteriores de las aplicaciones. Sin embargo, para DynamoDB, se recomienda utilizar el cliente HTTP basado en AWS CRT para los usos síncrono y asíncrono.

Para leer una introducción sobre el cliente HTTP basado en AWS CRT, consulte [Announcing availability of the AWS CRT HTTP Client in the AWS SDK for Java 2.x](https://aws.amazon.com/blogs/developer/announcing-availability-of-the-aws-crt-http-client-in-the-aws-sdk-for-java-2-x/) en el *AWS Developer Tools Blog*.

## Configuración de un cliente HTTP
<a name="ConfigHttpClient"></a>

Al configurar un cliente, puede proporcionar varias opciones de configuración, entre las que se incluyen las siguientes:
+ Configurar los tiempos de espera para distintos aspectos de las llamadas a la API.
+ Habilitar TCP Keep-Alive.
+ Controlar la política de reintentos cuando se producen errores.
+ Especificar los atributos de ejecución que pueden modificar las instancias [Execution interceptor](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/interceptors.html). Los interceptores de ejecución pueden escribir código que intercepte la ejecución de las solicitudes y respuestas de la API. Esto le permite realizar tareas, como publicar métricas y modificar las solicitudes de forma instantánea.
+ Agregar o manipular encabezados HTTP.
+ Permitir el seguimiento de las [métricas de rendimiento del cliente](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/metrics.html). Esta característica le permite recopilar métricas sobre los clientes de servicio de su aplicación y analizar los resultados en Amazon CloudWatch.
+ Especificar un servicio ejecutor alternativo que se utilizará para programar tareas, como los reintentos asíncronos y las tareas de tiempo de espera.

La configuración se controla proporcionando un objeto [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.html) a la clase `Builder` del cliente de servicio. Verá cómo se hace en algunos ejemplos de código de las siguientes secciones.

La `ClientOverrideConfiguration` proporciona opciones de configuración estándar. Los diferentes clientes HTTP conectables también ofrecen posibilidades de configuración específicas de la implementación.

**Topics**
+ [Configuración de tiempo de espera](#TimeoutConfig)
+ [RetryMode](#RetryMode)
+ [DefaultsMode](#DefaultsMode)
+ [Configuración de Keep-Alive](#KeepAliveConfig)

### Configuración de tiempo de espera
<a name="TimeoutConfig"></a>

Puede ajustar la configuración del cliente para controlar varios tiempos de espera relacionados con las llamadas al servicio. DynamoDB proporciona latencias más bajas en comparación con otros Servicios de AWS. Por lo tanto, es posible que desee ajustar estas propiedades para reducir los valores de tiempo de espera, de modo que pueda responder rápido a los errores si se produce un problema de red.

Puede personalizar el comportamiento relacionado con la latencia mediante `ClientOverrideConfiguration` en el cliente DynamoDB o cambiar las opciones de configuración detalladas en la implementación del cliente HTTP subyacente.

Puede configurar las siguientes propiedades impactantes usando `ClientOverrideConfiguration`:
+ `apiCallAttemptTimeout`: tiempo que se debe esperar a que se complete un solo intento de una solicitud HTTP antes de abandonar y agotar el tiempo de espera.
+ `apiCallTimeout`: tiempo del que dispone el cliente para ejecutar por completo una llamada a la API. Esto incluye la ejecución del controlador de solicitudes, que consta de todas las solicitudes HTTP, incluidos los reintentos.

El AWS SDK for Java 2.x proporciona [valores predeterminados](https://github.com/aws/aws-sdk-java-v2/blob/a0c8a0af1fa572b16b5bd78f310594d642324156/http-client-spi/src/main/java/software/amazon/awssdk/http/SdkHttpConfigurationOption.java#L134) para algunas opciones de tiempo de espera, como el tiempo de espera de la conexión y los tiempos de espera del socket. El SDK no proporciona valores predeterminados para los tiempos de espera de las llamadas a la API ni para los tiempos de espera de los intentos de llamadas individuales a la API. Si estos tiempos de espera no están configurados en `ClientOverrideConfiguration`, el SDK utiliza el valor de tiempo de espera del socket en lugar del tiempo de espera general de las llamadas a la API. El tiempo de espera del socket tiene un valor predeterminado de 30 segundos.

### RetryMode
<a name="RetryMode"></a>

Otra configuración relacionada con la configuración del tiempo de espera que debe tener en cuenta es el objeto de configuración `RetryMode`. Este objeto de configuración contiene un conjunto de comportamientos de reintento.

El SDK para Java 2.x admite los siguientes modos de reintento:
+ `legacy`: modo de reintento predeterminado si no lo cambia de forma explícita. Este modo de reintento es específico del SDK de Java. Se caracteriza por hasta tres reintentos, o más para los servicios como DynamoDB, que tiene hasta ocho reintentos.
+ `standard`: se denomina “estándar” porque es más consistente con otros AWS SDK. Este modo espera un período de tiempo aleatorio que va desde 0 ms hasta 1000 ms para el primer intento. Si es necesario volver a intentarlo, este modo selecciona otro tiempo aleatorio de 0 ms a 1000 ms y lo multiplica por dos. Si es necesario volver a intentarlo, realiza la misma selección aleatoria multiplicada por cuatro, y así sucesivamente. Cada espera tiene un límite de 20 segundos. Este modo realiza reintentos en caso de que se detecten más condiciones de fallo que el modo `legacy`. En el caso de DynamoDB, realiza hasta un máximo de tres intentos, a menos que se anule con [numRetries](#numRetries).
+ `adaptive`: se basa en el modo `standard` y limita de forma dinámica la tasa de solicitudes de AWS para maximizar la tasa de éxito. Esto puede producirse a expensas de la latencia de las solicitudes. No recomendamos utilizar el modo de reintento adaptativo cuando la latencia predecible es importante.

En el tema [Retry behavior](https://docs.aws.amazon.com/sdkref/latest/guide/feature-retry-behavior.html) de la *Guía de referencia de los SDK y las herramientas de AWS* encontrará una definición amplia de estos modos de reintento.

#### Políticas de reintentos
<a name="RetryPolicies"></a>

Todas las configuraciones de `RetryMode` tienen una [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/RetryPolicy.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/RetryPolicy.html), que se crea en función de una o más configuraciones de [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/RetryCondition.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/RetryCondition.html). La condición [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/TokenBucketRetryCondition.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/TokenBucketRetryCondition.html) es especialmente importante para el comportamiento de reintento de la implementación del cliente del SDK de DynamoDB. Esta condición limita el número de reintentos que realiza el SDK mediante un algoritmo de bucket de tokens. Según el modo de reintento seleccionado, las excepciones de limitación pueden restar o no tokens al `TokenBucket`.

Cuando un cliente detecta un error que se puede volver a intentar, como una excepción de limitación o un error temporal del servidor, el SDK vuelve a intentar la solicitud automáticamente. Puede controlar el número de veces y la rapidez con que se realizan estos reintentos.

Al configurar un cliente, puede proporcionar una `RetryPolicy` que admita los siguientes parámetros:
+ `numRetries`: número máximo de reintentos que deben realizarse antes de que una solicitud se considere fallida. El valor predeterminado es ocho, independientemente del modo de reintento que utilice.
**aviso**  
Asegúrese de cambiar este valor predeterminado después de estudiarlo atentamente.
+ `backoffStrategy`: la [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/backoff/BackoffStrategy.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/backoff/BackoffStrategy.html) que se debe aplicar a los reintentos, siendo [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/backoff/FullJitterBackoffStrategy.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/backoff/FullJitterBackoffStrategy.html) la estrategia predeterminada. Esta estrategia genera un retardo exponencial entre los reintentos adicionales en función del número o los reintentos actuales, el retardo base y el tiempo máximo de retroceso. A continuación, añade fluctuación para proporcionar un poco de aleatoriedad. El retardo base utilizado en el retardo exponencial es de 25 ms, independientemente del modo de reintento.
+ `retryCondition`: la [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/RetryCondition.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/RetryCondition.html) determina si se debe volver a intentar una solicitud o no. De forma predeterminada, volverá a intentar un conjunto específico de códigos de estado HTTP y excepciones que considere que se pueden volver a intentar. En la mayoría de las situaciones, la configuración predeterminada debería ser suficiente.

El código siguiente proporciona una política de reintentos alternativa. Además, especifica un total de cinco reintentos (seis solicitudes en total). El primer reintento debe producirse tras un retardo de aproximadamente 100 ms, y cada reintento adicional duplica ese tiempo exponencialmente, hasta un retardo máximo de un segundo.

```
DynamoDbClient client = DynamoDbClient.builder()
    .overrideConfiguration(ClientOverrideConfiguration.builder()
        .retryPolicy(RetryPolicy.builder()
            .backoffStrategy(FullJitterBackoffStrategy.builder()
                .baseDelay(Duration.ofMillis(100))
                .maxBackoffTime(Duration.ofSeconds(1))
                .build())
            .numRetries(5)
            .build())
        .build())
    .build();
```

### DefaultsMode
<a name="DefaultsMode"></a>

Las propiedades de tiempo de espera que no administran `ClientOverrideConfiguration` y `RetryMode` suelen configurarse implícitamente especificando un `DefaultsMode`.

En AWS SDK for Java 2.x (versión 2.17.102 o posteriores) se introdujo la compatibilidad con `DefaultsMode`. Esta característica proporciona un conjunto de valores predeterminados para los ajustes configurables más comunes, como los ajustes de comunicación HTTP, el comportamiento de los reintentos, los ajustes del punto de conexión regionales del servicio y, potencialmente, cualquier configuración relacionada con el SDK. Al utilizar esta característica, se pueden obtener nuevos valores predeterminados de configuración adaptados a los escenarios de uso habituales.

Los modos predeterminados están estandarizados en todos los SDK de AWS. El SDK para Java 2.x admite los siguientes modos de reintento predeterminados:
+ `legacy`: proporciona una configuración predeterminada que varía según el AWS SDK y que existía antes de la creación de `DefaultsMode`.
+ `standard`: proporciona una configuración predeterminada no optimizada para la mayoría de los escenarios.
+ `in-region`: se basa en el modo estándar e incluye una configuración adaptada a las aplicaciones que llaman a los Servicios de AWS desde la misma Región de AWS.
+ `cross-region`: se basa en el modo estándar e incluye una configuración con tiempos de espera elevados para las aplicaciones que llaman a los Servicios de AWS desde una región distinta.
+ `mobile`: se basa en el modo estándar e incluye configuraciones con tiempos de espera elevados diseñados para aplicaciones móviles con latencias más altas.
+ `auto`: se basa en el modo estándar e incluye funciones experimentales. El SDK intenta descubrir el tiempo de ejecución para determinar automáticamente la configuración adecuada. La detección automática se basa en la heurística y no es precisa al 100 %. Si no se puede determinar el tiempo de ejecución, se utiliza el modo estándar. La detección automática puede consultar los [Metadatos de instancia y datos de usuario](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html), lo que puede introducir latencia. Si la startup es fundamental para tu aplicación, te recomendamos que elijas un `DefaultsMode` explícito en su lugar.

Puede configurar el modo predeterminado de las siguientes maneras:
+ Directamente en un cliente mediante `AwsClientBuilder.Builder#defaultsMode(DefaultsMode)`
+ En un perfil de configuración a través de la propiedad del archivo de perfil `defaults_mode`.
+ Globalmente a través de la propiedad del sistema `aws.defaultsMode`.
+ Globalmente a través de la variable de entorno `AWS_DEFAULTS_MODE`.

**nota**  
Para cualquier modo que no sea el `legacy`, los valores predeterminados pueden cambiar a medida que evolucionen las prácticas recomendadas. Por lo tanto, recomendamos que se realicen pruebas al actualizar el SDK si se utiliza un modo distinto a `legacy`.

Los [valores predeterminados de configuración inteligente](https://docs.aws.amazon.com/sdkref/latest/guide/feature-smart-config-defaults.html) de la *Guía de referencia de los SDK y las herramientas de AWS* proporcionan una lista de las propiedades de configuración y sus valores predeterminados en los distintos modos predeterminados.

El valor de modo predeterminado se elige según las características de la aplicación y el Servicio de AWS con el que interactúa la aplicación.

Estos valores se configuran teniendo en cuenta una amplia selección de Servicios de AWS. Para una implementación típica de DynamoDB en la que tanto las tablas como la aplicación de DynamoDB se implementan en una región, el modo predeterminado `in-region` es el más relevante de los modos predeterminados `standard`.

**Example Configuración del cliente del SDK de DynamoDB adaptada para llamadas de baja latencia**  
En el siguiente ejemplo, los tiempos de espera se adaptan a valores más bajos para una llamada a DynamoDB de baja latencia prevista.  

```
DynamoDbAsyncClient asyncClient = DynamoDbAsyncClient.builder()
    .defaultsMode(DefaultsMode.IN_REGION)
    .httpClientBuilder(AwsCrtAsyncHttpClient.builder())
    .overrideConfiguration(ClientOverrideConfiguration.builder()
        .apiCallTimeout(Duration.ofSeconds(3))
        .apiCallAttemptTimeout(Duration.ofMillis(500))
        .build())
    .build();
```
La implementación del cliente HTTP individual puede proporcionar un control aún más detallado sobre el tiempo de espera y el comportamiento de uso de la conexión. Por ejemplo, en el caso del cliente basado en AWS CRT, puede activar `ConnectionHealthConfiguration`, que permite que el cliente monitoree activamente el estado de las conexiones utilizadas. Para obtener más información, consulte [Configuración avanzada de clientes HTTP basados en CRT de AWS](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-crt.html#configuring-the-crt-based-http-client) en la *Guía para desarrolladores de AWS SDK for Java 2.x*.

### Configuración de Keep-Alive
<a name="KeepAliveConfig"></a>

Si se activa keep-alive, se pueden reducir las latencias al reutilizar las conexiones. Hay dos tipos diferentes de keep-alive: HTTP Keep-Alive y TCP Keep-Alive.
+ HTTP Keep-Alive intenta mantener la conexión HTTPS entre el cliente y el servidor para que las solicitudes posteriores puedan aprovechar dicha conexión. De este modo, se omite la autenticación HTTPS tan pesada en las solicitudes sucesivas. HTTP Keep-Alive está activado de forma predeterminada en todos los clientes.
+ TCP Keep-Alive solicita al sistema operativo subyacente que envíe paquetes pequeños a través de la conexión por socket para garantizar que el socket se mantenga activo y detectar inmediatamente cualquier caída. De este modo, se garantiza que una solicitud posterior no pierda tiempo intentando utilizar un socket caído. TCP Keep-Alive está desactivado de forma predeterminada en todos los clientes. En los siguientes ejemplos de código se muestra cómo activarlo en cada cliente HTTP. Cuando está activado para todos los clientes HTTP no basados en CRT, el mecanismo Keep-Alive real depende del sistema operativo. Por lo tanto, debe configurar valores adicionales de TCP Keep-Alive, como el tiempo de espera y el número de paquetes, a través del sistema operativo. Puede hacerlo con `sysctl` en Linux o macOS, o con los valores del registro en Windows.

**Example para activar TCP Keep-Alive en un cliente HTTP basado en Apache**  

```
DynamoDbClient client = DynamoDbClient.builder()
    .httpClientBuilder(ApacheHttpClient.builder().tcpKeepAlive(true))
    .build();
```

**Cliente HTTP basado en `URLConnection`**  
Ningún cliente síncrono que use el cliente HTTP basado en `URLConnection` [https://docs.oracle.com/javase/8/docs/api/java/net/HttpURLConnection.html](https://docs.oracle.com/javase/8/docs/api/java/net/HttpURLConnection.html) dispone de ningún [mecanismo](https://docs.oracle.com/javase/8/docs/api/java/net/doc-files/net-properties.html) para activar keep-alive.

**Example para activar TCP Keep-Alive en un cliente HTTP basado en Netty**  

```
DynamoDbAsyncClient client = DynamoDbAsyncClient.builder()
    .httpClientBuilder(NettyNioAsyncHttpClient.builder().tcpKeepAlive(true))
    .build();
```

**Example para activar TCP Keep-Alive en un cliente HTTP basado en AWS CRT**  
Con el cliente HTTP basado en AWS CRT, puede activar TCP Keep-Alive y controlar la duración.  

```
DynamoDbClient client = DynamoDbClient.builder()
    .httpClientBuilder(AwsCrtHttpClient.builder()
    .tcpKeepAliveConfiguration(TcpKeepAliveConfiguration.builder()
        .keepAliveInterval(Duration.ofSeconds(50))
        .keepAliveTimeout(Duration.ofSeconds(5))
        .build()))
    .build();
```
Al utilizar el cliente asíncrono de DynamoDB, puede activar TCP Keep-Alive, tal y como se muestra en el código siguiente.  

```
DynamoDbAsyncClient client = DynamoDbAsyncClient.builder()
    .httpClientBuilder(AwsCrtAsyncHttpClient.builder()
    .tcpKeepAliveConfiguration(TcpKeepAliveConfiguration.builder()
        .keepAliveInterval(Duration.ofSeconds(50))
        .keepAliveTimeout(Duration.ofSeconds(5))
        .build()))
    .build();
```

## Gestión de errores
<a name="JavaErrorHandling"></a>

En lo que respecta a la gestión de excepciones, AWS SDK for Java 2.x utiliza excepciones en tiempo de ejecución (no comprobadas).

La excepción básica, que cubre todas las excepciones del SDK, es [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkServiceException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkServiceException.html), que se extiende desde la `RuntimeException` no comprobada de Java. Si detecta esto, detectará todas las excepciones del SDK.

`SdkServiceException` tiene una subclase llamada [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/exception/AwsServiceException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/exception/AwsServiceException.html). Esta subclase indica cualquier problema de comunicación con el Servicio de AWS. Tiene una subclase llamada [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/DynamoDbException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/DynamoDbException.html), que indica que hay un problema en la comunicación con DynamoDB. Si detecta esto, detectará todas las excepciones relacionadas con DynamoDB, pero no las demás excepciones del SDK.

En `DynamoDbException` encontrará [tipos de excepción](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/DynamoDbException.html) más específicos. Algunos de estos tipos de excepción se aplican a las operaciones del plano de control, como [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/TableAlreadyExistsException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/TableAlreadyExistsException.html). Otros se aplican a las operaciones del plano de datos. El siguiente es un ejemplo de una excepción común en el plano de datos:
+ [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ConditionalCheckFailedException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ConditionalCheckFailedException.html): ha especificado una condición en la solicitud que se ha evaluado como false. Por ejemplo, es posible que haya intentado realizar una actualización condicional de un elemento, pero que el valor real del atributo no coincidiese con el valor previsto en la condición. Una solicitud que falle de esta manera no se volverá a intentar.

Las otras situaciones no tienen una excepción específica definida. Por ejemplo, cuando se limitan las solicitudes, es posible que se lance la `ProvisionedThroughputExceededException` específica, mientras que en otros casos se lanzará la `DynamoDbException` más genérica. En cualquier caso, puede determinar si la excepción se debe a una limitación comprobando si `isThrottlingException()` devuelve `true`.

Según las necesidades de la aplicación, puede detectar todas las instancias `AwsServiceException` o las instancias `DynamoDbException`. Sin embargo, a menudo se necesita un comportamiento diferente en distintas situaciones. La lógica para hacer frente a un fallo en la comprobación de condiciones es diferente a la que se utiliza para gestionar la limitación. Defina las rutas excepcionales con las que quiere trabajar y asegúrese de probar las rutas alternativas. De este modo, se asegurará de que es capaz de enfrentarse a todas las situaciones relevantes.

Consulte [Control de errores con DynamoDB](Programming.Errors.md) para ver una lista de los errores más comunes que puede encontrar. Consulte también [Common Errors](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/CommonErrors.html) en la *Referencia de la API de Amazon DynamoDB*. En la referencia de la API, también se proporcionan los errores exactos posibles para cada operación de la API; por ejemplo, para la operación [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html). Para obtener información sobre la gestión de excepciones, consulte [Exception handling for the AWS SDK for Java 2.x](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/handling-exceptions.html) en la *Guía para desarrolladores de AWS SDK for Java 2.x*.

## ID de solicitud de AWS
<a name="JavaRequestID"></a>

Cada solicitud incluye un ID de solicitud que puede resultar útil si trabaja con AWS Support para diagnosticar un problema. Cada excepción derivada de `SdkServiceException` tiene un método [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkServiceException.html#requestId()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkServiceException.html#requestId()) disponible para recuperar el ID de solicitud.

## Registro
<a name="JavaLogging"></a>

El uso del registro proporcionado por el SDK puede resultar útil tanto para detectar cualquier mensaje importante de las bibliotecas de cliente como para realizar una depuración más exhaustiva. Los registradores son jerárquicos. El SDK utiliza `software.amazon.awssdk` como registrador raíz. Puede configurar el nivel con `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`, `ALL` o `OFF`. El nivel configurado se aplica a ese registrador a la jerarquía de registradores que tiene debajo.

Para el registro, AWS SDK for Java 2.x utiliza Simple Logging Façade for Java (SLF4J). Actúa como una capa de abstracción alrededor de otros registradores y puede usarla para conectar el registrador que prefiera. Para obtener instrucciones sobre cómo conectar los registradores, consulte el [SLF4J user manual](https://www.slf4j.org/manual.html).

Cada registrador tiene un comportamiento particular. El registrador Log4j 2.x crea un `ConsoleAppender` de forma predeterminada que agrega los eventos de registro a `System.out` y es, de forma predeterminada, el nivel de registro de `ERROR`.

El registrador SimpleLogger incluido en el SLF4J genera `System.err` y va al nivel de registro `INFO` de forma predeterminada.

Recomendamos que establezca el nivel en `WARN` para `software.amazon.awssdk` para todas las implementaciones de producción para detectar cualquier mensaje importante de las bibliotecas de cliente del SDK y, al mismo tiempo, limitar la cantidad de resultados.

Si SLF4J no encuentra un registrador compatible en la ruta de clases (no hay ningún enlace con SLF4J), no [implementa ninguna operación](https://www.slf4j.org/codes.html#noProviders) de forma predeterminada. Esta implementación hace que se registren mensajes `System.err` que expliquen que SLF4J no ha podido encontrar una implementación de registrador en la ruta de clases. Para evitar esta situación, hay que agregar una implementación de registrador. Para ello, puede añadir una dependencia en el `pom.xml` de Apache Maven en los artefactos, como `org.slf4j.slf4j-simple` o `org.apache.logging.log4j.log4j-slf4j2-imp`.

Para obtener información sobre cómo configurar el registro en el SDK, incluido cómo agregar dependencias de registro a la configuración de la aplicación, consulte [Logging with the SDK for Java 2.x](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/logging-slf4j.html) en la *Guía para desarrolladores de AWS SDK para Java*.

La siguiente configuración del archivo `Log4j2.xml` muestra cómo ajustar el comportamiento de registro si se utiliza el registrador Apache Log4j 2. Esta configuración establece el nivel del registrador raíz en `WARN`. Todos los registradores de la jerarquía, incluido el registrador `software.amazon.awssdk`, heredan este nivel de registro.

De forma predeterminada, el resultado es `System.out`. En el siguiente ejemplo, seguimos anulando el appender Log4j de salida predeterminado para aplicar un `PatternLayout` de Log4j personalizado.

**Ejemplo de un archivo de configuración de `Log4j2.xml`**  
Esta configuración registra los mensajes en los niveles `ERROR` y `WARN` en la consola para todas las jerarquías de registradores.

```
<Configuration status="WARN">
  <Appenders>
    <Console name="ConsoleAppender" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c:%L - %m%n" />
    </Console>
  </Appenders>

  <Loggers>
    <Root level="WARN">
      <AppenderRef ref="ConsoleAppender"/>
    </Root>
  </Loggers>
</Configuration>
```

### Registro de ID de solicitud de AWS
<a name="JavaReqIDLogging"></a>

Si algo sale mal, puede consultar los ID de solicitud dentro de las excepciones. En cambio, si quiere los ID de solicitud para las solicitudes que no generan excepciones, puede usar el registro.

El registrador `software.amazon.awssdk.request` genera los ID de solicitud en el nivel `DEBUG`. El siguiente ejemplo amplía el [configuration example](#Log4j2ConfigEg) anterior para mantener el nivel del registrador raíz en`ERROR`, el `software.amazon.awssdk` en el nivel `WARN` y el `software.amazon.awssdk.request` en el nivel `DEBUG`. Establecer estos niveles ayuda a detectar los ID de solicitud y otros detalles relacionados con la solicitud, como el punto de conexión y el código de estado.

```
<Configuration status="WARN">
  <Appenders>
    <Console name="ConsoleAppender" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss} [%t] %-5p %c:%L - %m%n" />
    </Console>
  </Appenders>

  <Loggers>
    <Root level="ERROR">
      <AppenderRef ref="ConsoleAppender"/>
    </Root>
    <Logger name="software.amazon.awssdk" level="WARN" />
    <Logger name="software.amazon.awssdk.request" level="DEBUG" />
  </Loggers>
</Configuration>
```

Este es un ejemplo del resultado del registro:

```
2022-09-23 16:02:08 [main] DEBUG software.amazon.awssdk.request:85 - Sending Request: DefaultSdkHttpFullRequest(httpMethod=POST, protocol=https, host=dynamodb.us-east-1.amazonaws.com, encodedPath=/, headers=[amz-sdk-invocation-id, Content-Length, Content-Type, User-Agent, X-Amz-Target], queryParameters=[])
 2022-09-23 16:02:08 [main] DEBUG software.amazon.awssdk.request:85 - Received successful response: 200, Request ID: QS9DUMME2NHEDH8TGT9N5V53OJVV4KQNSO5AEMVJF66Q9ASUAAJG, Extended Request ID: not available
```

## Paginación
<a name="JavaPagination"></a>

Algunas solicitudes, como [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html) y [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html), limitan el tamaño de los datos que se devuelven en una sola solicitud y requieren que realice solicitudes repetidas para abrir las páginas siguientes.

Puede controlar el número máximo de elementos que se debe leer en cada página con el parámetro `Limit`. Por ejemplo, puede usar el parámetro `Limit` para recuperar solo los últimos 10 elementos. Este límite especifica el número de elementos que debe leerse de la tabla antes de aplicar un filtro. Si desea exactamente 10 elementos después de aplicar el filtro, no hay forma de especificarlo. Solo puede controlar el recuento antes de filtrar y comprobarlo desde el cliente cuando haya obtenido los 10 elementos. Las respuestas tienen siempre un tamaño máximo de 1 MB, sin importar el límite.

Es posible que se incluya una `LastEvaluatedKey` en la respuesta de la API. Esto indica que la respuesta ha finalizado porque se ha alcanzado un límite de recuento o un límite de tamaño. Esta clave es la última clave evaluada para esa respuesta. Al interactuar directamente con la API, puede recuperar esta `LastEvaluatedKey` y pasarla a una llamada de seguimiento como `ExclusiveStartKey` para que lea el siguiente fragmento desde ese punto de partida. Si se devuelve ninguna `LastEvaluatedKey`, eso significa que no hay más elementos que coincidan con la llamada a la API `Query` o `Scan`.

En el siguiente ejemplo, se utiliza la interfaz de bajo nivel para limitar los elementos a 100 en función del parámetro `keyConditionExpression`.

```
QueryRequest.Builder queryRequestBuilder = QueryRequest.builder()
        .expressionAttributeValues(Map.of(
                ":pk_val", AttributeValue.fromS("123"),
                ":sk_val", AttributeValue.fromN("1000")))
        .keyConditionExpression("pk = :pk_val AND sk > :sk_val")
        .limit(100)
        .tableName(TABLE_NAME);

while (true) {
    QueryResponse queryResponse = DYNAMODB_CLIENT.query(queryRequestBuilder.build());

    queryResponse.items().forEach(item -> {
        LOGGER.info("item PK: [" + item.get("pk") + "] and SK: [" + item.get("sk") + "]");
    });

    if (!queryResponse.hasLastEvaluatedKey()) {
        break;
    }
    queryRequestBuilder.exclusiveStartKey(queryResponse.lastEvaluatedKey());
}
```

AWS SDK for Java 2.x puede simplificar esta interacción con DynamoDB al proporcionar métodos de paginación automática que efectúan varias llamadas al servicio para obtener las siguientes páginas de resultados automáticamente. Esto simplifica el código, pero elimina parte del control sobre el uso de los recursos que se mantendría al leer las páginas manualmente.

Al utilizar los métodos `Iterable` disponibles en el cliente de DynamoDB, como [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html#queryPaginator(software.amazon.awssdk.services.dynamodb.model.QueryRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html#queryPaginator(software.amazon.awssdk.services.dynamodb.model.QueryRequest)) y [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html#scanPaginator(software.amazon.awssdk.services.dynamodb.model.ScanRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html#scanPaginator(software.amazon.awssdk.services.dynamodb.model.ScanRequest)), el SDK se encarga de la paginación. El tipo de retorno de estos métodos es un retorno iterable personalizado que se puede utilizar para iterar en todas las páginas. El SDK gestiona internamente las llamadas al servicio en su nombre. La API de Java Stream permite gestionar el resultado de `QueryPaginator`, tal como se muestra en el siguiente ejemplo.

```
QueryPublisher queryPublisher =
    DYNAMODB_CLIENT.queryPaginator(QueryRequest.builder()
        .expressionAttributeValues(Map.of(
            ":pk_val", AttributeValue.fromS("123"),
            ":sk_val", AttributeValue.fromN("1000")))
        .keyConditionExpression("pk = :pk_val AND sk > :sk_val")
        .limit(100)
        .tableName("YourTableName")
        .build());

queryPublisher.items().subscribe(item ->
    System.out.println(item.get("itemData"))).join();
```

## Anotaciones de clases de datos
<a name="JavaDataClassAnnotation"></a>

El SDK de Java proporciona varias anotaciones que puede incluir en los atributos de la clase de datos. Estas anotaciones influyen en la forma en que el SDK interactúa con los atributos. Al agregar la anotación, puede hacer que un atributo se comporte como un contador atómico implícito, mantener un valor de marca de tiempo generado automáticamente o realizar un seguimiento del número de versión de un elemento. Para obtener más información, consulte [Data class annotations](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html).