

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.

# Llamada a Servicios de AWS desde AWS SDK for Java 2.x
<a name="work-with-services"></a>

Esta sección ofrece breves tutoriales y orientación sobre cómo trabajar con algunos Servicios de AWS. Para ver un conjunto completo de ejemplos, consulte la [sección Ejemplos de código](java_code_examples.md).

**Topics**
+ [CloudWatch](examples-cloudwatch.md)
+ [Servicios de bases de datos de AWS](examples-databases.md)
+ [DynamoDB](examples-dynamodb.md)
+ [Amazon EC2](examples-ec2.md)
+ [IAM](examples-iam.md)
+ [Kinesis](examples-kinesis.md)
+ [Lambda](examples-lambda.md)
+ [Amazon S3](examples-s3.md)
+ [Amazon SNS](examples-simple-notification-service.md)
+ [Amazon SQS](examples-sqs.md)
+ [Amazon Transcribe](examples-transcribe.md)

# Trabaja con CloudWatch
<a name="examples-cloudwatch"></a>

En esta sección se proporcionan ejemplos de programación de [Amazon CloudWatch](https://docs.aws.amazon.com//AmazonCloudWatch/latest/monitoring/WhatIsCloudWatch.html) mediante la versión AWS SDK para Java 2.x.

 Amazon CloudWatch monitorea tus recursos Amazon Web Services (AWS) y las aplicaciones en las que ejecutas AWS en tiempo real. Puede utilizarlas CloudWatch para recopilar y realizar un seguimiento de las métricas, que son variables que puede medir para sus recursos y aplicaciones. CloudWatch las alarmas envían notificaciones o modifican automáticamente los recursos que está supervisando en función de las reglas que usted defina.

Los siguientes ejemplos incluyen únicamente el código necesario para demostrar cada técnica. El [código de ejemplo completo está disponible en GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2). Desde allí, puede descargar un único archivo de código fuente o clonar el repositorio localmente para obtener todos los ejemplos para compilarlos y ejecutarlos.

**Topics**
+ [Obtenga métricas de CloudWatch](examples-cloudwatch-get-metrics.md)
+ [Publica datos de métricas personalizadas en CloudWatch](examples-cloudwatch-publish-custom-metrics.md)
+ [Trabaja con CloudWatch alarmas](examples-cloudwatch-create-alarms.md)
+ [Usa Amazon CloudWatch Events](examples-cloudwatch-send-events.md)

# Obtenga métricas de CloudWatch
<a name="examples-cloudwatch-get-metrics"></a>

## Mostrar métricas
<a name="listing-metrics"></a>

Para enumerar CloudWatch las métricas, crea un `listMetrics` método [ListMetricsRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/model/ListMetricsRequest.html)y CloudWatchClient llama al. Puede utilizar el objeto `ListMetricsRequest` para filtrar las métricas devueltas por espacio de nombres, nombre de métrica o dimensiones.

**nota**  
Puedes encontrar una lista de métricas y dimensiones publicadas por AWS servicios en la [referencia de Amazon CloudWatch métricas y dimensiones](https://docs.aws.amazon.com//AmazonCloudWatch/latest/monitoring/aws-services-cloudwatch-metrics.html) de la Guía del Amazon CloudWatch usuario.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatch.CloudWatchClient;
import software.amazon.awssdk.services.cloudwatch.model.CloudWatchException;
import software.amazon.awssdk.services.cloudwatch.model.ListMetricsRequest;
import software.amazon.awssdk.services.cloudwatch.model.ListMetricsResponse;
import software.amazon.awssdk.services.cloudwatch.model.Metric;
```

 **Código** 

```
    public static void listMets( CloudWatchClient cw, String namespace) {

        boolean done = false;
        String nextToken = null;

        try {
            while(!done) {

                ListMetricsResponse response;

                if (nextToken == null) {
                   ListMetricsRequest request = ListMetricsRequest.builder()
                        .namespace(namespace)
                        .build();

                 response = cw.listMetrics(request);
                } else {
                  ListMetricsRequest request = ListMetricsRequest.builder()
                        .namespace(namespace)
                        .nextToken(nextToken)
                        .build();

                response = cw.listMetrics(request);
            }

            for (Metric metric : response.metrics()) {
                System.out.printf(
                        "Retrieved metric %s", metric.metricName());
                System.out.println();
            }

            if(response.nextToken() == null) {
                done = true;
            } else {
                nextToken = response.nextToken();
            }
        }

        } catch (CloudWatchException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Las métricas se devuelven en forma de [ListMetricsResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/model/ListMetricsResponse.html)llamando a su `getMetrics` método.

Los resultados puede que estén *paginados*. Para recuperar el siguiente lote de resultados, llame a `nextToken` en el objeto de respuesta y use el valor de token para crear un nuevo objeto de solicitud. Llame entonces al método `listMetrics` de nuevo con la nueva solicitud.

Consulta el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/cloudwatch/src/main/java/com/example/cloudwatch/ListMetrics.java) en GitHub.

## Más información
<a name="more-information"></a>
+  [ListMetrics](https://docs.aws.amazon.com//AmazonCloudWatch/latest/APIReference/API_ListMetrics.html)en la referencia Amazon CloudWatch de la API

# Publica datos de métricas personalizadas en CloudWatch
<a name="examples-cloudwatch-publish-custom-metrics"></a>

Varios AWS servicios publican [sus propias métricas](https://docs.aws.amazon.com//AmazonCloudWatch/latest/monitoring/aws-services-cloudwatch-metrics.html) en espacios de nombres que comienzan por «`AWS`». También puedes publicar datos de métricas personalizados con tu propio espacio de nombres (siempre y cuando no comience por «»). `AWS`

## Publicar datos de métricas personalizadas
<a name="cwid1"></a>

Para publicar tus propios datos de métricas, llama al método CloudWatchClient's con un`putMetricData`. [PutMetricDataRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/model/PutMetricDataRequest.html) `PutMetricDataRequest`Debe incluir el espacio de nombres personalizado que se utilizará para los datos e información sobre el propio punto de datos de un [MetricDatum](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/model/MetricDatum.html)objeto.

**nota**  
No puede especificar un espacio de nombres que comience por "`AWS`". Los espacios de nombres que comienzan por «`AWS`» están reservados para su uso por parte de los productos. Amazon Web Services 

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatch.CloudWatchClient;
import software.amazon.awssdk.services.cloudwatch.model.Dimension;
import software.amazon.awssdk.services.cloudwatch.model.MetricDatum;
import software.amazon.awssdk.services.cloudwatch.model.StandardUnit;
import software.amazon.awssdk.services.cloudwatch.model.PutMetricDataRequest;
import software.amazon.awssdk.services.cloudwatch.model.CloudWatchException;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
```

 **Código** 

```
    public static void putMetData(CloudWatchClient cw, Double dataPoint ) {

        try {
            Dimension dimension = Dimension.builder()
                    .name("UNIQUE_PAGES")
                    .value("URLS")
                    .build();

            // Set an Instant object
            String time = ZonedDateTime.now( ZoneOffset.UTC ).format( DateTimeFormatter.ISO_INSTANT );
            Instant instant = Instant.parse(time);

            MetricDatum datum = MetricDatum.builder()
                .metricName("PAGES_VISITED")
                .unit(StandardUnit.NONE)
                .value(dataPoint)
                .timestamp(instant)
                .dimensions(dimension).build();

            PutMetricDataRequest request = PutMetricDataRequest.builder()
                .namespace("SITE/TRAFFIC")
                .metricData(datum).build();

            cw.putMetricData(request);

        } catch (CloudWatchException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        System.out.printf("Successfully put data point %f", dataPoint);
     }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/cloudwatch/src/main/java/com/example/cloudwatch/PutMetricData.java) en. GitHub

## Más información
<a name="more-information"></a>
+  [Utilice Amazon CloudWatch las métricas](https://docs.aws.amazon.com//AmazonCloudWatch/latest/monitoring/working_with_metrics.html) en la guía Amazon CloudWatch del usuario.
+  [AWS Los espacios de nombres](https://docs.aws.amazon.com//AmazonCloudWatch/latest/monitoring/aws-services-cloudwatch-metrics.html) en la guía del Amazon CloudWatch usuario.
+  [PutMetricData](https://docs.aws.amazon.com//AmazonCloudWatch/latest/APIReference/API_PutMetricData.html)en la referencia de la Amazon CloudWatch API.

# Trabaja con CloudWatch alarmas
<a name="examples-cloudwatch-create-alarms"></a>

## Crear una alarma
<a name="create-an-alarm"></a>

Para crear una alarma basada en una CloudWatch métrica, llama al CloudWatchClient `putMetricAlarm` método s y [PutMetricAlarmRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/model/PutMetricAlarmRequest.html)rellena con las condiciones de la alarma.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatch.CloudWatchClient;
import software.amazon.awssdk.services.cloudwatch.model.Dimension;
import software.amazon.awssdk.services.cloudwatch.model.PutMetricAlarmRequest;
import software.amazon.awssdk.services.cloudwatch.model.ComparisonOperator;
import software.amazon.awssdk.services.cloudwatch.model.Statistic;
import software.amazon.awssdk.services.cloudwatch.model.StandardUnit;
import software.amazon.awssdk.services.cloudwatch.model.CloudWatchException;
```

 **Código** 

```
    public static void putMetricAlarm(CloudWatchClient cw, String alarmName, String instanceId) {

        try {
            Dimension dimension = Dimension.builder()
                .name("InstanceId")
                .value(instanceId).build();

            PutMetricAlarmRequest request = PutMetricAlarmRequest.builder()
                .alarmName(alarmName)
                .comparisonOperator(
                        ComparisonOperator.GREATER_THAN_THRESHOLD)
                .evaluationPeriods(1)
                .metricName("CPUUtilization")
                .namespace("AWS/EC2")
                .period(60)
                .statistic(Statistic.AVERAGE)
                .threshold(70.0)
                .actionsEnabled(false)
                .alarmDescription(
                        "Alarm when server CPU utilization exceeds 70%")
                .unit(StandardUnit.SECONDS)
                .dimensions(dimension)
                .build();

            cw.putMetricAlarm(request);
            System.out.printf(
                    "Successfully created alarm with name %s", alarmName);

        } catch (CloudWatchException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulta el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/cloudwatch/src/main/java/com/example/cloudwatch/PutMetricAlarm.java) en GitHub.

## Mostrar alarmas
<a name="list-alarms"></a>

Para ver una lista de las CloudWatch alarmas que ha creado, llame al CloudWatchClient `describeAlarms` método s con un [DescribeAlarmsRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/model/DescribeAlarmsRequest.html)que pueda usar para configurar las opciones del resultado.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatch.CloudWatchClient;
import software.amazon.awssdk.services.cloudwatch.model.CloudWatchException;
import software.amazon.awssdk.services.cloudwatch.model.DescribeAlarmsRequest;
import software.amazon.awssdk.services.cloudwatch.model.DescribeAlarmsResponse;
import software.amazon.awssdk.services.cloudwatch.model.MetricAlarm;
```

 **Código** 

```
    public static void desCWAlarms( CloudWatchClient cw) {

        try {

            boolean done = false;
            String newToken = null;

            while(!done) {
                DescribeAlarmsResponse response;

                if (newToken == null) {
                    DescribeAlarmsRequest request = DescribeAlarmsRequest.builder().build();
                    response = cw.describeAlarms(request);
                } else {
                    DescribeAlarmsRequest request = DescribeAlarmsRequest.builder()
                        .nextToken(newToken)
                        .build();
                    response = cw.describeAlarms(request);
                }

                for(MetricAlarm alarm : response.metricAlarms()) {
                    System.out.printf("\n Retrieved alarm %s", alarm.alarmName());
                }

                if(response.nextToken() == null) {
                    done = true;
                } else {
                    newToken = response.nextToken();
                }
            }

        } catch (CloudWatchException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        System.out.printf("Done");
    }
```

La lista de alarmas se puede obtener llamando `MetricAlarms` [DescribeAlarmsResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/model/DescribeAlarmsResponse.html)al devuelto por`describeAlarms`.

Los resultados puede que estén *paginados*. Para recuperar el siguiente lote de resultados, llame a `nextToken` en el objeto de respuesta y use el valor de token para crear un nuevo objeto de solicitud. Llame entonces al método `describeAlarms` de nuevo con la nueva solicitud.

**nota**  
También puede recuperar las alarmas de una métrica específica mediante CloudWatchClient el `describeAlarmsForMetric` método. Su uso es similar a `describeAlarms`.

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/cloudwatch/src/main/java/com/example/cloudwatch/DescribeAlarms.java) en GitHub.

## Eliminación de alarmas
<a name="delete-alarms"></a>

Para eliminar CloudWatch las alarmas, llame al CloudWatchClient `deleteAlarms` método [DeleteAlarmsRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/model/DeleteAlarmsRequest.html)que contenga uno o más nombres de las alarmas que desee eliminar.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.cloudwatch.CloudWatchClient;
import software.amazon.awssdk.services.cloudwatch.model.CloudWatchException;
import software.amazon.awssdk.services.cloudwatch.model.DeleteAlarmsRequest;
```

 **Código** 

```
    public static void deleteCWAlarm(CloudWatchClient cw, String alarmName) {

        try {
            DeleteAlarmsRequest request = DeleteAlarmsRequest.builder()
                    .alarmNames(alarmName)
                    .build();

            cw.deleteAlarms(request);
            System.out.printf("Successfully deleted alarm %s", alarmName);

        } catch (CloudWatchException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulta el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/cloudwatch/src/main/java/com/example/cloudwatch/DeleteAlarm.java) en GitHub.

## Más información
<a name="more-information"></a>
+  [Uso de Amazon CloudWatch alarmas](https://docs.aws.amazon.com//AmazonCloudWatch/latest/monitoring/AlarmThatSendsEmail.html) en la Guía Amazon CloudWatch del usuario
+  [PutMetricAlarm](https://docs.aws.amazon.com//AmazonCloudWatch/latest/APIReference/API_PutMetricAlarm.html)en la referencia Amazon CloudWatch de la API
+  [DescribeAlarms](https://docs.aws.amazon.com//AmazonCloudWatch/latest/APIReference/API_DescribeAlarms.html)en la referencia Amazon CloudWatch de la API
+  [DeleteAlarms](https://docs.aws.amazon.com//AmazonCloudWatch/latest/APIReference/API_DeleteAlarms.html)en la referencia Amazon CloudWatch de la API

# Usa Amazon CloudWatch Events
<a name="examples-cloudwatch-send-events"></a>

 CloudWatch Los eventos ofrecen un flujo casi en tiempo real de los eventos del sistema que describen los cambios en AWS los recursos en las Amazon EC2 instancias, Lambda las funciones, los Kinesis flujos, Amazon ECS las tareas, las máquinas de Step Functions estado, Amazon SNS los temas, Amazon SQS las colas o los objetivos integrados. Mediante reglas sencillas, puede asignar los eventos y dirigirlos a una o más secuencias o funciones de destino.

Amazon EventBridge es la [evolución](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-cwe-now-eb.html) de los CloudWatch eventos. Ambos servicios utilizan la misma API, por lo que puedes seguir utilizando el [cliente de CloudWatch Events](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatch/CloudWatchClient.html) que proporciona el SDK o migrar al SDK del [EventBridge cliente](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/eventbridge/EventBridgeClient.html) de Java para disfrutar de la funcionalidad de CloudWatch Events. CloudWatch [La documentación de la guía del usuario](https://docs.aws.amazon.com/eventbridge/latest/userguide/index.html) de Events y la [referencia sobre la API](https://docs.aws.amazon.com/eventbridge/latest/APIReference/index.html) ya están disponibles en los sitios de EventBridge documentación.

## Agregar eventos
<a name="add-events"></a>

Para añadir CloudWatch eventos personalizados, llama al `CloudWatchEventsClient’s` `putEvents` método con un [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatchevents/model/PutEventsRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatchevents/model/PutEventsRequest.html)objeto que contenga uno o más [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatchevents/model/PutEventsRequestEntry.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatchevents/model/PutEventsRequestEntry.html)objetos que proporcionen detalles sobre cada evento. Puede especificar varios parámetros para la entrada como el origen y el tipo del evento, los recursos asociados con el evento, etc.

**nota**  
Puede especificar un máximo de 10 eventos para cada llamada a `putEvents`.

 **Importaciones** 

```
import software.amazon.awssdk.services.cloudwatch.model.CloudWatchException;
import software.amazon.awssdk.services.cloudwatchevents.CloudWatchEventsClient;
import software.amazon.awssdk.services.cloudwatchevents.model.PutEventsRequest;
import software.amazon.awssdk.services.cloudwatchevents.model.PutEventsRequestEntry;
```

 **Código** 

```
    public static void putCWEvents(CloudWatchEventsClient cwe, String resourceArn ) {

        try {

            final String EVENT_DETAILS =
                "{ \"key1\": \"value1\", \"key2\": \"value2\" }";

            PutEventsRequestEntry requestEntry = PutEventsRequestEntry.builder()
                    .detail(EVENT_DETAILS)
                    .detailType("sampleSubmitted")
                    .resources(resourceArn)
                    .source("aws-sdk-java-cloudwatch-example")
                    .build();

            PutEventsRequest request = PutEventsRequest.builder()
                    .entries(requestEntry)
                    .build();

            cwe.putEvents(request);
            System.out.println("Successfully put CloudWatch event");

        } catch (CloudWatchException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/cloudwatch/src/main/java/com/example/cloudwatch/PutEvents.java) en GitHub.

## Cómo añadir reglas
<a name="add-rules"></a>

Para crear o actualizar una regla, llame al `CloudWatchEventsClient’s` `putRule` método con una [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatchevents/model/PutRuleRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatchevents/model/PutRuleRequest.html)con el nombre de la regla y parámetros opcionales, como el [patrón de eventos](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns.html), el IAM rol que se va a asociar a la regla y una [expresión de programación](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-create-rule-schedule.html) que describa la frecuencia con la que se ejecuta la regla.

 **Importaciones** 

```
import software.amazon.awssdk.services.cloudwatch.model.CloudWatchException;
import software.amazon.awssdk.services.cloudwatchevents.CloudWatchEventsClient;
import software.amazon.awssdk.services.cloudwatchevents.model.PutRuleRequest;
import software.amazon.awssdk.services.cloudwatchevents.model.PutRuleResponse;
import software.amazon.awssdk.services.cloudwatchevents.model.RuleState;
```

 **Código** 

```
    public static void putCWRule(CloudWatchEventsClient cwe, String ruleName, String roleArn) {

        try {
            PutRuleRequest request = PutRuleRequest.builder()
                .name(ruleName)
                .roleArn(roleArn)
                .scheduleExpression("rate(5 minutes)")
                .state(RuleState.ENABLED)
                .build();

            PutRuleResponse response = cwe.putRule(request);
            System.out.printf(
                    "Successfully created CloudWatch events rule %s with arn %s",
                    roleArn, response.ruleArn());
        } catch (
            CloudWatchException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/cloudwatch/src/main/java/com/example/cloudwatch/PutRule.java) en GitHub.

## Agregar destinos
<a name="add-targets"></a>

Los destinos son los recursos que se invocan cuando se activa una regla. Los objetivos de ejemplo incluyen Amazon EC2 instancias, Lambda funciones, Kinesis flujos, Amazon ECS tareas, máquinas de Step Functions estado y objetivos integrados.

Para añadir un destino a una regla, llame al método `putTargets` de `CloudWatchEventsClient’s` con un objeto [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatchevents/model/PutTargetsRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/cloudwatchevents/model/PutTargetsRequest.html) que contenga la regla que se va a actualizar y la lista de destinos que se van a añadir a la regla.

 **Importaciones** 

```
import software.amazon.awssdk.services.cloudwatch.model.CloudWatchException;
import software.amazon.awssdk.services.cloudwatchevents.CloudWatchEventsClient;
import software.amazon.awssdk.services.cloudwatchevents.model.PutTargetsRequest;
import software.amazon.awssdk.services.cloudwatchevents.model.PutTargetsResponse;
import software.amazon.awssdk.services.cloudwatchevents.model.Target;
```

 **Código** 

```
    public static void putCWTargets(CloudWatchEventsClient cwe, String ruleName, String functionArn, String targetId ) {

        try {
            Target target = Target.builder()
                .arn(functionArn)
                .id(targetId)
                .build();

            PutTargetsRequest request = PutTargetsRequest.builder()
                .targets(target)
                .rule(ruleName)
                .build();

            PutTargetsResponse response = cwe.putTargets(request);
            System.out.printf(
                "Successfully created CloudWatch events target for rule %s",
                ruleName);
        } catch (CloudWatchException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/cloudwatch/src/main/java/com/example/cloudwatch/PutTargets.java) en GitHub.

## Más información
<a name="more-information"></a>
+  [Añadir eventos PutEvents en la](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-putevents.html) Guía del EventBridge usuario de Amazon
+  [Programe expresiones para reglas](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-create-rule-schedule.html#eb-create-scheduled-rule-schedule) en la Guía del EventBridge usuario de Amazon
+  [Tipos de eventos para CloudWatch Events](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-service-event.html) la Guía del EventBridge usuario de Amazon
+  [Patrones de eventos](https://docs.aws.amazon.com/eventbridge/latest/userguide/eb-event-patterns.html) en la guía del EventBridge usuario de Amazon
+  [PutEvents](https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_PutEvents.html)en la referencia de la EventBridge API de Amazon
+  [PutTargets](https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_PutTargets.html)en la referencia de la EventBridge API de Amazon
+  [PutRule](https://docs.aws.amazon.com/eventbridge/latest/APIReference/API_PutRule.html)en la referencia de la EventBridge API de Amazon

# Servicios de base de datos de AWS y AWS SDK for Java 2.x
<a name="examples-databases"></a>

AWS ofrece varios tipos de bases de datos: relacionales, clave-valor, en memoria, de documentos y [otras](https://aws.amazon.com/products/databases/). La compatibilidad con el SDK para Java 2.x varía según la naturaleza del servicio de base de datos en AWS.

Algunos servicios de bases de datos, como [Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/Welcome.html), tienen API de servicios web para administrar el recurso de AWS (base de datos), así como API de servicios web para interactuar con los datos. En el SDK para Java 2.x, estos tipos de servicios tienen clientes de servicio dedicados, por ejemplo [DynamoDBClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html).

Otros servicios de bases de datos tienen API de servicios web que interactúan con el recurso, como la API [Amazon DocumentDB](https://docs.aws.amazon.com/documentdb/latest/developerguide/api-reference.html) (para la administración de clústeres, instancias y recursos), pero no tienen una API de servicio web para trabajar con los datos. El SDK para Java 2.x tiene la interfaz [DocDBClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/docdb/DocDbClient.html) correspondiente para trabajar con el recurso. Sin embargo, necesita otra API de Java, como [MongoDB para Java](https://www.mongodb.com/developer/languages/java/) para trabajar con los datos.

Utilice los ejemplos siguientes para aprender cómo utilizar los clientes de servicio SDK para Java 2.x con los distintos tipos de bases de datos.

## Ejemplos de Amazon DynamoDB
<a name="examples-db-dynamodb"></a>


| Trabajo con los datos | Trabajo con la base de datos | 
| --- |--- |
| SDK service client: [DynamoDbClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html) | SDK service client: [DynamoDbClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html) | 
| Example: [Aplicación REST React/Spring con DynamoDB](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/usecases/creating_dynamodb_web_app) | Examples: [CreateTable, ListTables, DeleteTable](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb) | 
| Examples: [Varios ejemplos de DynamoDB](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb) |  | 
|  | 
| --- |
| SDK service client: [DynamoDbEnhancedClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html) |  | 
| Example: [Aplicación REST React/Spring con DynamoDB](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/usecases/creating_dynamodb_web_app) |  | 
| Examples: [Varios ejemplos de DynamoDB](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb) (names starting with 'Enhanced") |  | 

Consulte [otros ejemplos de DynamoDB](examples-dynamodb.md) en la sección de ejemplos de código guiados de esta guía.

## Ejemplos de Amazon RDS
<a name="examples-db-rds"></a>


|  Trabajo con los datos  |  Trabajo con la base de datos  | 
| --- | --- | 
| API ajena al SDK: JDBC, tipo SQL específico de la base de datos; el código administra las conexiones de la base de datos o un grupo de conexiones. | Cliente de servicio del SDK: [RdsClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/rds/RdsClient.html) | 
| Ejemplo: [aplicación REST de React/Spring con MySQL](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/usecases/Creating_rds_item_tracker) | Ejemplos: [varios ejemplos de RdsClient](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/example_code/rds/src/main/java/com/example/rds) | 

## Ejemplos de Amazon Redshift
<a name="examples-db-redshift"></a>


|  Trabajo con los datos  |  Trabajo con la base de datos  | 
| --- | --- | 
| Cliente de servicio SDK: [RedshiftDataClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/redshiftdata/RedshiftDataClient.html) | Cliente de servicio SDK: [RedshiftClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/redshift/RedshiftClient.html) | 
| Ejemplos: [varios ejemplos de RedshiftDataClient](https://github.com/awsdocs/aws-doc-sdk-examples/blob/c682a07a1e6abce793e3c32ef3b9661fa723d0ff/javav2/example_code/redshift/src/main/java/com/example/scenario/RedshiftScenario.java) | Ejemplos: [varios ejemplos de RedShiftClient](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/example_code/redshift/src/main/java/com/example/redshift) | 
| Ejemplo: [aplicación REST de React/Spring con RedshiftDataClient](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/usecases/CreatingSpringRedshiftRest) |  | 

## Ejemplos de Amazon Aurora sin servidor v2
<a name="examples-db-aurora-sv1"></a>


|  Trabajo con los datos  |  Trabajo con la base de datos  | 
| --- | --- | 
| Cliente de servicio SDK: [RdsDataClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/rdsdata/RdsDataClient.html) | Cliente de servicio del SDK: [RdsClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/rds/RdsClient.html) | 
| Ejemplo: [aplicación REST de React/Spring con RdsDataClient](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/usecases/Creating_Spring_RDS_Rest) | Ejemplos: [varios ejemplos de RdsClient](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2/example_code/rds/src/main/java/com/example/rds) | 

## Ejemplos de Amazon DocumentDB
<a name="examples-db-docdb"></a>


|  Trabajo con los datos  |  Trabajo con la base de datos  | 
| --- | --- | 
| API que no pertenece al SDK: biblioteca Java específica de MongoDB (por ejemplo, [MongoDB para Java](https://www.mongodb.com/developer/languages/java/)); su código administra las conexiones de bases de datos o un grupo de conexiones. | Cliente de servicio SDK: [DocDBClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/docdb/DocDbClient.html) | 
| Ejemplos: [Guía del desarrollador de DocumentDB (Mongo)](https://docs.aws.amazon.com/documentdb/latest/developerguide/connect_programmatically.html#connect_programmatically-tls_enabled) (seleccione la pestaña «Java») |  | 

# Trabaja con DynamoDB
<a name="examples-dynamodb"></a>

[DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Introduction.html) es un servicio de bases de datos NoSQL totalmente administrado que proporciona un rendimiento rápido y predecible, así como una perfecta escalabilidad. En esta sección se muestra cómo trabajar con DynamoDB mediante la versión AWS SDK para Java 2.x.

## Selección de un cliente de DynamoDB
<a name="ddb-clients-overview"></a>

El SDK ofrece dos métodos principales para trabajar con DynamoDB:

Cliente de bajo nivel (`DynamoDbClient`)  
Proporciona acceso directo a las operaciones de DynamoDB con control total sobre las solicitudes y las respuestas. Utilice este cliente cuando necesite un control detallado o trabaje con esquemas dinámicos.

Cliente mejorado (`DynamoDbEnhancedClient`)  
Ofrece programación orientada a objetos con asignación automática entre objetos de Java y elementos de DynamoDB. También proporciona funciones orientadas a documentos para trabajar con datos tipo JSON que no siguen un esquema fijo. Utilice este cliente cuando trabaje con modelos de datos bien definidos o datos de tipo documento.

## Configuración de clientes de DynamoDB
<a name="ddb-configuration-setup"></a>

Antes de trabajar con DynamoDB, configure el cliente para obtener un rendimiento y una fiabilidad óptimos.

### Descripción del comportamiento de reintento de DynamoDB
<a name="ddb-retry-behavior"></a>

Los clientes de DynamoDB utilizan un número máximo de reintentos predeterminado de 8, que es mayor que el de otros clientes de Servicio de AWS . Este mayor número de reintentos ayuda a administrar la naturaleza distribuida y las limitaciones temporales de capacidad de DynamoDB. Para obtener más información sobre estrategias de reintento, consulte [Configure el comportamiento de reintento en el AWS SDK for Java 2.x](retry-strategy.md).

### Optimización del rendimiento con puntos de conexión basados en cuentas
<a name="ddb-account-based-endpoints-v2"></a>

DynamoDB [AWS ofrece puntos de enlace basados en cuentas](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Programming.SDKOverview.html#Programming.SDKs.endpoints) que mejoran el rendimiento mediante el uso de AWS su ID de cuenta para agilizar el enrutamiento de solicitudes. 

Para utilizar esta característica, debe utilizar la versión 2.28.4 o superior de AWS SDK for Java 2.x. Encontrará la versión más reciente en el [Repositorio central de Maven](https://central.sonatype.com/artifact/software.amazon.awssdk/bom/versions). Las versiones del SDK compatibles utilizan automáticamente los nuevos puntos de conexión.

Para excluir el enrutamiento basado en cuentas, elija una de estas opciones:
+ Configure un cliente de servicio de DynamoDB con `AccountIdEndpointMode` establecido en `DISABLED`.
+ Establezca una variable de entorno.
+ Establezca una propiedad del sistema JVM.
+ Actualice la configuración del archivo de configuración compartido. AWS 

El siguiente ejemplo muestra cómo deshabilitar el enrutamiento basado en cuentas mediante la configuración de un cliente de servicio de DynamoDB:

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

Para obtener más información sobre las demás opciones de configuración, consulte los [puntos finales basados en cuentas](https://docs.aws.amazon.com/sdkref/latest/guide/feature-account-endpoints.html) en la Guía de referencia de herramientas AWS SDKs y herramientas.

## Qué se aborda en este tema
<a name="ddb-topics-overview"></a>

En las secciones siguientes se muestra cómo se trabaja con DynamoDB:
+ [Trabajar con tablas en DynamoDB](examples-dynamodb-tables.md): crear, describir, actualizar y eliminar tablas
+ [Trabaja con elementos en DynamoDB](examples-dynamodb-items.md): añadir, recuperar y actualizar elementos individuales
+ [Asigne objetos Java a elementos de DynamoDB con AWS SDK for Java 2.x](dynamodb-enhanced-client.md): utilizar la asignación de objetos y datos orientados a documentos con el Cliente mejorado

Para ver más ejemplos de código de DynamoDB, consulte Ejemplos de código de [DynamoDB en la biblioteca de ejemplos](java_dynamodb_code_examples.md) de código. AWS 

# Trabajar con tablas en DynamoDB
<a name="examples-dynamodb-tables"></a>

Las tablas son los contenedores de todos los elementos de una base de datos de DynamoDB. Para poder añadir o eliminar datos de DynamoDB, debe crear una tabla.

Para cada tabla, debe definir:
+ Un *nombre* de tabla que sea único para su cuenta y región.
+ Una *clave principal* para la que cada valor debe ser único; no puede haber dos elementos de la tabla que tengan el mismo valor de clave principal.

  Una clave principal puede ser *simple*, formada por una sola clave de partición (HASH) o *compuesta*, formada por una clave de partición y una clave de ordenación (RANGE).

  Cada valor de clave tiene un *tipo de datos* asociado, enumerado por la clase [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ScalarAttributeType.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ScalarAttributeType.html). El valor de clave puede ser binario (B), numérico (N) o una cadena (S). Para obtener más información, consulte [Reglas de nomenclatura y tipos de datos](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html) en la Guía para desarrolladores de Amazon DynamoDB.
+  El *desempeño aprovisionado* son valores que definen el número de unidades de capacidad de lectura/escritura reservadas para la tabla.
**nota**  
 [Los precios de Amazon DynamoDB](https://aws.amazon.com/dynamodb/pricing/) se basan en los valores de desempeño aprovisionado que puede definir en sus tablas para que solo se reserve la capacidad que piensa que va a necesitar para la tabla.

  El desempeño aprovisionado para una tabla se puede modificar en cualquier momento, por lo que puede ajustar la capacidad cuando cambien sus necesidades.

## Creación de una tabla
<a name="dynamodb-create-table"></a>

Use el método `createTable` de `DynamoDbClient’s` para crear una nueva tabla de DynamoDB. Debe crear los atributos de la tabla y un esquema de tabla, que se pueden usar para identificar la clave principal de la tabla. También debe proporcionar los valores iniciales de desempeño aprovisionado y el nombre de una tabla.

**nota**  
Si ya existe una tabla con el nombre elegido, se producirá una `[DynamoDbException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/DynamoDbException.html)`.

### Crear una tabla con una clave principal simple
<a name="dynamodb-create-table-simple"></a>

Este código crea una tabla con un atributo que es la clave principal simple de la tabla. El ejemplo utiliza los objetos `[AttributeDefinition](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/AttributeDefinition.html)` y `[KeySchemaElement](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/KeySchemaElement.html)` y para la [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/CreateTableRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/CreateTableRequest.html).

 **Importaciones** 

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

 **Código de** 

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

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

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

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

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

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

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/CreateTable.java) en GitHub.

### Crear una tabla con una clave primaria compuesta
<a name="dynamodb-create-table-composite"></a>

En el siguiente ejemplo se crea una tabla con dos atributos. Ambos atributos se utilizan para la clave primaria compuesta.

 **Importaciones** 

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

 **Código de** 

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

       String tableId = "";

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

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/CreateTableCompositeKey.java) en GitHub.

## Lista de tablas
<a name="dynamodb-list-tables"></a>

Puede mostrar las tablas de una región determinada llamando al método `listTables` de `DynamoDbClient’s`.

**nota**  
Si la tabla designada no existe para su cuenta y región, se produce una [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html).

 **Importaciones** 

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

 **Código de** 

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

        boolean moreTables = true;
        String lastName = null;

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

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

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

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

De forma predeterminada, se devuelve un máximo de 100 tablas para cada llamada: utilice `lastEvaluatedTableName` en el objeto [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ListTablesResponse.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ListTablesResponse.html) devuelto para obtener la última tabla que se evaluó. Puede utilizar este valor para iniciar la enumeración después del último valor devuelto de la enumeración anterior.

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/ListTables.java) en GitHub.

## Describir una tabla (obtener información de ella)
<a name="dynamodb-describe-table"></a>

Utilice el método `describeTable` de `DynamoDbClient’s` para obtener información sobre una tabla.

**nota**  
Si la tabla designada no existe para su cuenta y región, se produce una [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html).

 **Importaciones** 

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

 **Código de** 

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

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

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

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

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

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

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

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/DescribeTable.java) en GitHub.

## Modificar (actualizar) una tabla
<a name="dynamodb-update-table"></a>

Puede modificar los valores de rendimiento aprovisionado de la tabla en cualquier momento llamando al método `updateTable` de `DynamoDbClient’s`.

**nota**  
Si la tabla designada no existe para su cuenta y región, se produce una [ResourceNotFoundException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html).

 **Importaciones** 

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

 **Código de** 

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

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

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

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

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

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

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/UpdateTable.java) en GitHub.

## Eliminación de una tabla
<a name="dynamodb-delete-table"></a>

Para eliminar una tabla, llame al método `deleteTable` de `DynamoDbClient’s` y proporcione el nombre de la tabla.

**nota**  
Si la tabla designada no existe para su cuenta y región, se produce una [ResourceNotFoundException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html).

 **Importaciones** 

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

 **Código de** 

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

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

        try {
            ddb.deleteTable(request);

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

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/DeleteTable.java) en GitHub.

## Más información
<a name="more-information"></a>
+  [Prácticas recomendadas para trabajar con tablas](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GuidelinesForTables.html) en la Guía para desarrolladores de Amazon DynamoDB
+  [Trabajar con tablas en DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/WorkingWithTables.html) en la Guía para desarrolladores de Amazon DynamoDB

# Trabaja con elementos en DynamoDB
<a name="examples-dynamodb-items"></a>

*En DynamoDB, un elemento es una colección de *atributos*, cada uno de los cuales tiene un *nombre* y un valor.* Los valores de los atributos pueden ser escalares, conjuntos o tipos de documentos. Para obtener más información, consulte [Reglas de nomenclatura y tipos de datos](https://docs.aws.amazon.com//amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html) en la Guía para desarrolladores de Amazon DynamoDB .

## Recuperar (obtener) un elemento de una tabla
<a name="dynamodb-get-item"></a>

Llama al `getItem` método DynamoDbClient's y pásale un [GetItemRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/GetItemRequest.html)objeto con el nombre de la tabla y el valor de la clave principal del elemento que desees. Devuelve un [GetItemResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/GetItemResponse.html)objeto con todos los atributos de ese elemento. Puede especificar una o varias [expresiones de proyección](https://docs.aws.amazon.com//amazondynamodb/latest/developerguide/Expressions.ProjectionExpressions.html) en `GetItemRequest` para recuperar atributos específicos.

Puede utilizar el `item()` método del `GetItemResponse` objeto devuelto para recuperar un [mapa](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/Map.html) de los pares clave (cadena [AttributeValue](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/AttributeValue.html)) y valor () asociados al elemento.

 **Importaciones** 

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

 **Código** 

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

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

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

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

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

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

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

Consulta el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/bc964a243276990f05c180618ea8b34777c68f0e/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/GetItem.java) en GitHub.

## Recuperar (obtener) un elemento de una tabla usando el cliente asíncrono
<a name="id1ddb"></a>

Invoque el `getItem` método del DynamoDbAsyncClient y pásele un [GetItemRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/GetItemRequest.html)objeto con el nombre de la tabla y el valor de la clave principal del elemento que desee.

Puede devolver una instancia de [recopilación](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/Collection.html) con todos los atributos de ese elemento (consulte el siguiente ejemplo).

 **Importaciones** 

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

 **Código** 

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

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

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

        try {

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

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

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

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

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/bc964a243276990f05c180618ea8b34777c68f0e/javav2/example_code/dynamodbasync/src/main/java/com/example/dynamodbasync/DynamoDBAsyncGetItem.java) en. GitHub

## Agregar un nuevo elemento a una tabla
<a name="dynamodb-add-item"></a>

Cree un [mapa](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/Map.html) de pares de clave-valor que represente los atributos del elemento. Estos deben incluir valores para los campos de la clave principal de la tabla. Si el elemento identificado por la clave principal ya existe, la solicitud *actualiza* sus campos.

**nota**  
Si la tabla con el nombre indicado no existe para tu cuenta y región, [ResourceNotFoundException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html)aparecerá una.

 **Importaciones** 

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

 **Código** 

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

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

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

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

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

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

Consulta el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/PutItem.java) en GitHub.

## Actualizar un elemento existente en una tabla
<a name="dynamodb-update-item"></a>

Puede actualizar un atributo de un elemento que ya existe en una tabla mediante el método `updateItem` de DynamoDbClient, proporcionando el nombre de la tabla, el valor de clave principal y un mapa de los campos que se van a actualizar.

**nota**  
Si la tabla con el nombre asignado no existe para tu cuenta y región, o si el elemento identificado con la clave principal que has introducido no existe, [ResourceNotFoundException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html)aparecerá un.

 **Importaciones** 

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

 **Código** 

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

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

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

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

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

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

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

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

Consulta el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/UpdateItem.java) en GitHub.

## Eliminar un elemento existente en una tabla
<a name="dynamodb-delete-item"></a>

Puede eliminar un elemento que existe en una tabla utilizando el `deleteItem` método DynamoDbClient's y proporcionando un nombre de tabla, así como el valor de la clave principal.

**nota**  
Si la tabla con el nombre asignado no existe para tu cuenta y región, o si el elemento identificado con la clave principal que has introducido no existe, [ResourceNotFoundException](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ResourceNotFoundException.html)se generará una.

 **Importaciones** 

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

 **Código** 

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

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

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

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

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

Consulta el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f4eaf2b2971805cfb2b87a8e5ab408f83169432e/javav2/example_code/dynamodb/src/main/java/com/example/dynamodb/DeleteItem.java) en GitHub.

## Más información
<a name="more-information"></a>
+  [Prácticas recomendadas para el uso de elementos](https://docs.aws.amazon.com//amazondynamodb/latest/developerguide/best-practices.html) en la Guía para desarrolladores de Amazon DynamoDB 
+  [Cómo trabajar con los elementos DynamoDB](https://docs.aws.amazon.com//amazondynamodb/latest/developerguide/WorkingWithItems.html) de la guía para Amazon DynamoDB desarrolladores

# Asigne objetos Java a elementos de DynamoDB con AWS SDK for Java 2.x
<a name="dynamodb-enhanced-client"></a>

La [API de cliente mejorado de DynamoDB](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/package-summary.html) es una biblioteca de alto nivel, sucesora de la clase `DynamoDBMapper` del SDK para Java v1.x. Ofrece una forma sencilla de asignar clases del cliente a tablas de DynamoDB. Defina las relaciones entre tablas y sus correspondientes clases de datos en el código. Después de definir estas relaciones, puede realizar intuitivamente varias operaciones de creación, lectura, actualización o eliminación (CRUD) en tablas o elementos en DynamoDB.

La API de cliente mejorado de DynamoDB también incluye [la API de documentos mejorada](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/package-summary.html) para trabajar con elementos de tipo documento que no siguen un esquema definido. 

**Topics**
+ [Empiece a utilizar la API de cliente mejorado de DynamoDB](ddb-en-client-getting-started.md)
+ [Aprenda los conceptos básicos de la API de cliente mejorado de DynamoDB](ddb-en-client-use.md)
+ [Utilizar características de asignación avanzadas](ddb-en-client-adv-features.md)
+ [Trabaje con documentos JSON con la API de documentos mejorada para DynamoDB](ddb-en-client-doc-api.md)
+ [Uso de extensiones para personalizar operaciones de DynamoDB Enhanced Client](ddb-en-client-extensions.md)
+ [Utilizar la API de cliente mejorado de DynamoDB de forma asíncrona](ddb-en-client-async.md)
+ [Anotaciones de clases de datos](ddb-en-client-anno-index.md)

# Empiece a utilizar la API de cliente mejorado de DynamoDB
<a name="ddb-en-client-getting-started"></a>

El siguiente tutorial presenta los aspectos básicos necesarios para trabajar con la API de cliente mejorado de DynamoDB.

## agregar dependencias de API
<a name="ddb-en-client-gs-dep"></a>

Para empezar a trabajar con la API de cliente mejorado de DynamoDB en su proyecto, añada una dependencia al artefacto de Maven `dynamodb-enhanced`. Esto se muestra en el ejemplo siguiente. 

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

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

Realice una búsqueda en el repositorio central de Maven para encontrar la [última versión](https://central.sonatype.com/artifact/software.amazon.awssdk/bom) y *<VERSION>* sustitúyala por este valor.

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

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

Realice una búsqueda en el repositorio central de Maven para encontrar la [última versión](https://central.sonatype.com/artifact/software.amazon.awssdk/bom) y sustitúyala por este *<VERSION>* valor.

------

# Generación de un `TableSchema` partir de una clase de datos
<a name="ddb-en-client-gs-tableschema"></a>

Un `[TableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/TableSchema.html)` permite al cliente mejorado asignar valores de atributos de DynamoDB hacia y desde las clases del cliente. En este tutorial, conocerá los `TableSchema` derivados de una clase de datos estáticos y generadas a partir de código mediante un generador.

## Utilice una clase de datos anotada
<a name="ddb-en-client-gs-tableschema-anno-bean"></a>

El SDK para Java 2.x incluye un [conjunto de anotaciones](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/annotations/package-summary.html) que puede utilizar con una clase de datos para generar rápidamente un `TableSchema` para asignar sus clases a tablas.

Comience por crear una clase de datos que se ajuste a la [JavaBean especificación](https://download.oracle.com/otn-pub/jcp/7224-javabeans-1.01-fr-spec-oth-JSpec/beans.101.pdf). La especificación requiere que una clase tenga un constructor público sin argumentos, además de getters y setters para cada atributo de la clase. Incluya una anotación a nivel de clase para indicar que la clase de datos es una `DynamoDbBean`. Además, como mínimo, incluya una anotación `DynamoDbPartitionKey` en el getter o setter para el atributo de clave principal. 

Puede aplicar [anotaciones de nivel de atributo](ddb-en-client-anno-index.md) a getters o setters, pero no a ambos.

**nota**  
El término `property` se usa normalmente para un valor encapsulado en un. JavaBean Sin embargo, en esta guía se utiliza el término `attribute` en su lugar para mantener la coherencia con la terminología utilizada por DynamoDB.

La clase siguiente `Customer` muestra anotaciones que vinculan la definición de clase a una tabla de DynamoDB.

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

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

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

import java.time.Instant;

@DynamoDbBean
public class Customer {

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

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

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

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

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

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

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

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

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

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

Una vez creada una clase de datos anotada, utilícela para crear el `TableSchema`, como se muestra en el siguiente fragmento.

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

Un `TableSchema` se diseña para ser estático e inmutable. Por lo general, puede instanciarlo en el momento de cargar la clase.

El método de fábrica estático `TableSchema.fromBean()` introspecciona el objeto bean para generar la asignación de atributos (propiedades) de la clase de datos a y desde los atributos de DynamoDB.

Para ver un ejemplo de cómo trabajar con un modelo de datos compuesto por varias clases de datos, consulte la clase `Person` en la sección [Uso de atributos que son beans, mapas, listas y conjuntos](ddb-en-client-adv-features-nested.md).

## Uso de un constructor
<a name="ddb-en-client-gs-tableschema-builder"></a>

Puede saltarse el coste de la introspección de objetos bean si define el esquema de la tabla en código. Si codificas el esquema, no es necesario que tu clase siga los estándares de JavaBean nomenclatura ni que esté anotada. El siguiente ejemplo utiliza un constructor y es equivalente al ejemplo de clase `Customer` que utiliza anotaciones.

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

# Crear un cliente mejorado y una `DynamoDbTable`
<a name="ddb-en-client-getting-started-dynamodbTable"></a>

## Crear un cliente mejorado
<a name="ddb-en-client-getting-started-dynamodbTable-eclient"></a>

La [DynamoDbEnhancedClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html)clase o su contraparte asíncrona [DynamoDbEnhancedAsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedAsyncClient.html), es el punto de partida para trabajar con la API de cliente mejorada de DynamoDB.

El cliente mejorado requiere un `[DynamoDbClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html)` estándar para hacer el trabajo. La API ofrece dos formas de crear una instancia de `DynamoDbEnhancedClient`. La primera opción, que se muestra en el siguiente fragmento, crea un `DynamoDbClient` estándar con los ajustes predeterminados tomados de la configuración.

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

Si desea configurar el cliente estándar subyacente, puede suministrarlo al método constructor del cliente mejorado como se muestra en el siguiente fragmento.

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

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

## Crear una instancia de `DynamoDbTable`
<a name="ddb-en-client-getting-started-dynamodbTable-table"></a>

Piense en una [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html) como la representación del cliente de una tabla de DynamoDB que utiliza la funcionalidad de mapeo proporcionada por un `TableSchema`. La clase `DynamoDbTable` proporciona métodos para las operaciones CRUD que permiten interactuar con una sola tabla de DynamoDB.

`DynamoDbTable<T>` es una clase genérica que toma un único argumento de tipo, ya sea una clase personalizada o un `EnhancedDocument` cuando se trabaja con elementos de tipo documento. Este tipo de argumento establece la relación entre la clase que utiliza y la tabla única de DynamoDB.

Siga el método de fábrica `table()` del `DynamoDbEnhancedClient` para crear una instancia `DynamoDbTable`, como se muestra en el siguiente fragmento.

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

Las instancias de `DynamoDbTable` son aptas para ser únicas porque son inmutables y se pueden usar en toda la aplicación.

El código ahora tiene una representación en memoria de una tabla de DynamoDB que puede funcionar con instancias `Customer`. La tabla de DynamoDB real puede existir o no. Si la tabla nombrada `Customer` ya existe, puede empezar a realizar operaciones de CRUD en ella. Si no existe, utilice la instancia de `DynamoDbTable` para crear la tabla, tal y como se explica en la siguiente sección.

# Cree una tabla de DynamoDB si es necesario
<a name="ddb-en-client-gs-ddbtable"></a>

Después de crear una instancia `DynamoDbTable`, úsela para crear una tabla de DynamoDB por *única vez*.

## Crear código de ejemplo de tabla
<a name="ddb-en-client-gs-ddbtable-createex"></a>

En el siguiente ejemplo, se crea una tabla de DynamoDB basada en la clase de datos `Customer`. 

En este ejemplo se crea una tabla DynamoDB con el nombre `Customer` —idéntico al nombre de la clase— pero el nombre de la tabla puede ser otro. Sea cual sea el nombre que dé a la tabla, debe usar este nombre en otras aplicaciones para trabajar con la tabla. Proporcione este nombre al método `table()` cada vez que cree otro objeto `DynamoDbTable` para trabajar con la tabla de DynamoDB subyacente.

El parámetro lambda de Java, `builder`, que se pasa al método `createTable`, permite [personalizar la tabla](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/CreateTableEnhancedRequest.Builder.html). En este ejemplo, se configura el [rendimiento aprovisionado](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.html#HowItWorks.ProvisionedThroughput.Manual). Si desea utilizar la configuración predeterminada al crear una tabla, omita el generador, tal y como se muestra en el siguiente fragmento.

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

Cuando se utiliza la configuración predeterminada, no se establecen los valores del rendimiento aprovisionado. En su lugar, el modo de facturación de la tabla se establece como [bajo demanda](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.html#HowItWorks.OnDemand).

En el ejemplo también se utiliza una `[DynamoDbWaiter](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/waiters/DynamoDbWaiter.html)` antes de intentar imprimir el nombre de la tabla recibido en la respuesta. Crear una tabla lleva algún tiempo. Por lo tanto, utilizar un 'waiter' significa que no tiene que escribir lógica que consulte el servicio de DynamoDB para verificar si la tabla existe antes de su uso.

### Importaciones
<a name="ddb-en-client-gs-ddbtable-imports"></a>

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

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

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

**nota**  
Los nombres de los atributos de una tabla de DynamoDB comienzan con una letra minúscula cuando la tabla se genera a partir de una clase de datos. Si desea que el nombre del atributo de la tabla comience con una letra mayúscula, utilice la [anotación `@DynamoDbAttribute(NAME)`](ddb-en-client-adv-features-inex-attr.md) y proporcione el nombre que desee como parámetro.

# Realizar operaciones
<a name="ddb-en-client-gs-use"></a>

Una vez creada la tabla, utilice la instancia `DynamoDbTable` para las operaciones en la tabla de DynamoDB. 

En el siguiente ejemplo, se pasa un singleton `DynamoDbTable<Customer>` como parámetro junto con una [instancia de clase de datos `Customer`](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust) para añadir un nuevo elemento a la tabla.

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

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

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

Antes de enviar el objeto `customer` al servicio DynamoDB, registre el resultado del método del objeto `toString()` para compararlo con lo que envía el cliente mejorado.

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

El registro a nivel de cable muestra la carga útil de la solicitud generada. El cliente mejorado generó la representación de bajo nivel a partir de la clase de datos. El atributo `regDate`, que es un tipo `Instant` en Java, se representa como una cadena de DynamoDB.

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

# Trabajar con una tabla existente
<a name="ddb-en-client-gs-existingtable"></a>

En la sección anterior se muestra cómo crear una tabla de DynamoDB a partir de una clase de datos de Java. Si ya tiene una tabla existente y desea utilizar las características del cliente mejorado, puede crear una clase de datos de Java para que funcione con la tabla. Debe examinar la tabla de DynamoDB y añadir las anotaciones necesarias a la clase de datos. 

Antes de trabajar con una tabla existente, llame al método `DynamoDbEnhanced.table()`. Esto se hizo en el ejemplo anterior con la instrucción siguiente.

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

Una vez devuelta la instancia `DynamoDbTable`, puede empezar a trabajar de inmediato con la tabla subyacente. No es necesario volver a crear la tabla llamando al método `DynamoDbTable.createTable()`.

El siguiente ejemplo lo demuestra mediante la recuperación inmediata de una instancia `Customer` de la tabla de DynamoDB.

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

**importante**  
El nombre de la tabla utilizado en el método `table()` debe coincidir con el nombre de la tabla de DynamoDB existente.

# Aprenda los conceptos básicos de la API de cliente mejorado de DynamoDB
<a name="ddb-en-client-use"></a>

Este tema trata las características básicas de la API de cliente mejorado de DynamoDB y la compara con la [API de cliente de DynamoDB estándar](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/package-summary.html).

Si es la primera vez que utiliza la API de cliente mejorado de DynamoDB, le recomendamos que consulte el [tutorial introductorio](ddb-en-client-getting-started.md) para familiarizarse con las clases fundamentales.

## Elementos de DynamoDB en Java
<a name="ddb-en-client-use-usecase"></a>

Las tablas de DynamoDB almacenan elementos. Según el caso práctico, los elementos del lado de Java pueden adoptar la forma de datos estructurados de forma estática o estructuras creadas dinámicamente. 

Si su caso requiere elementos con un conjunto coherente de atributos, utilice [clases anotadas](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean) o un [constructor](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-builder) para generar los tipos estáticos adecuados de `TableSchema`. 

Alternativamente, si necesita almacenar elementos que consten de estructuras variables, cree un `DocumentTableSchema`. `DocumentTableSchema` forma parte de la [API de documento mejorada](ddb-en-client-doc-api.md) y solo requiere una clave principal de tipo estático y funciona con instancias `EnhancedDocument` para contener los elementos de datos. La API de documentos mejorada se trata en otro [tema](ddb-en-client-doc-api.md).

## Tipos de atributos para clases de modelos de datos
<a name="ddb-en-client-use-types"></a>

Si bien DynamoDB admite [un número menor de tipos de atributos](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes) en comparación con el sistema de tipos enriquecidos de Java, la API de cliente mejorado de DynamoDB proporciona mecanismos para convertir los miembros de una clase de Java a y desde tipos de atributos de DynamoDB.

Los tipos de atributos (propiedades) de las clases de datos de Java deben ser tipos de objetos, no primitivos. Por ejemplo, utilice siempre tipos de datos de objetos `Long` y `Integer`, no primitivos `long` y `int`.

[De forma predeterminada, la API de cliente mejorada de DynamoDB admite convertidores de atributos para una gran cantidad de tipos, [como](https://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html) Integer [[BigDecimal](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/BigDecimalAttributeConverter.html),](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html) String e Instant.](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/InstantAsStringAttributeConverter.html) La lista aparece en las [clases de implementación conocidas de la AttributeConverter interfaz](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverter.html). La lista incluye muchos tipos y colecciones, como mapas, listas y conjuntos.

Para almacenar los datos de un tipo de atributo que no se admite de forma predeterminada o que no se ajusta a la JavaBean convención, puede escribir una `AttributeConverter` implementación personalizada para realizar la conversión. Consulte la sección sobre conversión de atributos para ver un [ejemplo](ddb-en-client-adv-features-conversion.md#ddb-en-client-adv-features-conversion-example).

Para almacenar los datos de un tipo de atributo cuya clase cumpla con la especificación de beans de Java (o una [clase de datos inmutable](ddb-en-client-use-immut.md)), puede adoptar dos enfoques. 
+ Si tiene acceso al archivo fuente, puede anotar la clase con `@DynamoDbBean` (o `@DynamoDbImmutable`). La sección que analiza los atributos anidados muestra [ejemplos](ddb-en-client-adv-features-nested.md#ddb-en-client-adv-features-nested-map-anno) del uso de clases anotadas.
+ Si no tiene acceso al archivo de origen de la clase de JavaBean datos del atributo (o no quiere hacer anotaciones en el archivo de origen de una clase a la que sí tiene acceso), puede utilizar el enfoque de creación. Esto crea un esquema de tabla sin definir las claves. A continuación, puede anidar este esquema de tabla dentro de otro esquema de tabla para realizar el mapeo. La sección de atributos anidados contiene un [ejemplo](ddb-en-client-adv-features-nested.md#ddb-en-client-adv-features-nested-map-builder) que muestra el uso de esquemas anidados.

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

Al utilizar el método `putItem`, el cliente mejorado no incluye los atributos con valores nulos de un objeto de datos mapeado en la solicitud a DynamoDB.

El comportamiento predeterminado del SDK para las solicitudes `updateItem` elimina atributos del elemento de DynamoDB que están configurados como nulos en el objeto que se envía en el método `updateItem`. Si desea actualizar algunos valores de atributos y mantener los demás sin cambios, tiene dos opciones.
+ Recupere el elemento (usando `getItem`) antes de realizar cambios en los valores. Con este método, el SDK envía todos los valores antiguos y actualizados a DynamoDB.
+ Utilice `[IgnoreNullsMode](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/IgnoreNullsMode.html).SCALAR_ONLY` o `IgnoreNullsMode.MAPS_ONLY` cuando cree la solicitud para actualizar el elemento. Ambos modos ignoran las propiedades con valores nulos del objeto que representan atributos escalares en DynamoDB. El tema [Actualización de elementos que contienen tipos complejos](ddb-en-client-adv-features-nested.md#ddb-en-client-adv-features-nested-updates) de esta guía contiene más información sobre los valores `IgnoreNullsMode` y sobre cómo trabajar con tipos complejos.

El siguiente ejemplo demuestra `ignoreNullsMode()` para el método `updateItem()`.

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

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

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

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

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

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


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

## Métodos básicos del cliente mejorado de DynamoDB
<a name="ddb-en-client-use-basic-ops"></a>

Los métodos básicos del cliente mejorado se corresponden con las operaciones del servicio de DynamoDB que les dan nombre. Los siguientes ejemplos muestran la variación más simple de cada método. Puede personalizar cada método pasando un objeto de solicitud mejorado. Los objetos de solicitud mejorados ofrecen la mayoría de las características disponibles en el cliente estándar de DynamoDB. Estas acciones se documentan por completo en la Referencia de la API de AWS SDK for Java 2.x .

El ejemplo usa la [Clase `Customer`](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust) mostrada anteriormente.

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

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

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

// PutItem
customerTable.putItem(customer);

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

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

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

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

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

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

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

## Comparar el cliente mejorado con el cliente estándar de DynamoDB
<a name="ddb-en-client-use-compare"></a>

Los dos APIs clientes de DynamoDB [(](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/package-summary.html)estándar [y](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/package-summary.html) mejorado) le permiten trabajar con tablas de DynamoDB para realizar operaciones CRUD (crear, leer, actualizar y eliminar) a nivel de datos. La diferencia entre los clientes reside en la forma en que se logra. APIs Con el cliente estándar, se trabaja directamente con atributos de datos de bajo nivel. La API de cliente mejorada utiliza clases de Java conocidas y se asigna a la API de bajo nivel en segundo plano.

Si bien ambos clientes APIs admiten operaciones a nivel de datos, el cliente estándar de DynamoDB también admite operaciones a nivel de recursos. Las operaciones a nivel de recursos gestionan la base de datos, como la creación de copias de seguridad, la creación de listas y la actualización de tablas. La API de cliente mejorada admite un número selecto de operaciones a nivel de recursos, como la creación, descripción y eliminación de tablas.

Para ilustrar los diferentes enfoques utilizados por los dos clientes APIs, los siguientes ejemplos de código muestran la creación de la misma `ProductCatalog` tabla con el cliente estándar y el cliente mejorado.

### Comparación: crear una tabla mediante el cliente estándar de DynamoDB
<a name="ddb-en-client-use-compare-cs1"></a>

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

### Comparación: crear una tabla mediante el cliente mejorado de DynamoDB
<a name="ddb-en-client-use-compare-cs2"></a>

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

El cliente mejorado utiliza la siguiente clase de datos anotados. El cliente mejorado de DynamoDB asigna los tipos de datos de Java a los tipos de datos de DynamoDB para obtener un código menos detallado que sea más fácil de seguir. `ProductCatalog` es un ejemplo del uso de una clase inmutable con el cliente mejorado de DynamoDB. El uso de clases inmutables para las clases de datos mapeados [se analiza más adelante en este tema](ddb-en-client-use-immut.md).

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

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

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

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

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


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

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

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


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

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

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

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

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

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

Los dos ejemplos de código siguientes de una escritura por lotes ilustran la imprecisión y la falta de seguridad de tipos cuando se utiliza el cliente estándar frente al cliente mejorado.

### Comparación: escritura por lotes mediante el cliente estándar de DynamoDB
<a name="ddb-en-client-use-compare-cs4"></a>

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

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

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

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

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

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

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

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

### Comparación: escritura por lotes mediante el cliente mejorado de DynamoDB
<a name="ddb-en-client-use-compare-cs5"></a>

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

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

# Trabajar con clases de datos inmutables
<a name="ddb-en-client-use-immut"></a>

La característica de asignación de la API de cliente mejorado de DynamoDB 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. En lugar de usar la anotación `@DynamoDbBean` como se muestra en la [clase Customer](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust), las clases inmutables usan la anotación `@DynamoDbImmutable`, que toma un parámetro que indica la clase de generador que se va a usar.

La siguiente clase es una versión inmutable de `Customer`.

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

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

import java.time.Instant;

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

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

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

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

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

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

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

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

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

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

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

Debe cumplir los siguientes requisitos al anotar una clase de datos con `@DynamoDbImmutable`.

1. Todo método que no sea una sustitución de `Object.class` y que no haya sido anotado con `@DynamoDbIgnore` debe ser un método de obtención de un atributo de la tabla de DynamoDB.

1. Cada getter debe tener un setter correspondiente que distinga entre mayúsculas y minúsculas en la clase de constructor.

1. Solo debe cumplirse una de las siguientes condiciones de construcción.
   + La clase de constructor debe tener un constructor público predeterminado.
   + La clase de datos debe tener un nombre de método estático público `builder()` que no tome parámetros y devuelva una instancia de clase constructor. Esta opción se muestra en la clase `Customer` inmutable.

1.  La clase del constructor debe tener un método público llamado `build()` que no acepte parámetros y devuelva una instancia de la clase inmutable. 

Para crear un `TableSchema` para su clase inmutable, utilice el método `fromImmutableClass()` en `TableSchema` como se muestra en el siguiente fragmento.

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

Del mismo modo que puede crear una tabla de DynamoDB a partir de una clase mutable, también puede crear una a partir de una clase inmutable con una *llamada única* a `createTable()` de `DynamoDbTable` como se muestra en el siguiente ejemplo de fragmento.

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

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

Las bibliotecas de terceros, como [Project Lombok](https://projectlombok.org/), ayudan a generar código reutilizable asociado a objetos inmutables. La API de cliente mejorado de DynamoDB funciona con estas bibliotecas siempre que las clases de datos sigan las convenciones detalladas en esta sección. 

En el siguiente ejemplo, se muestra la clase `CustomerImmutable` inmutable con anotaciones de Lombok. Observe cómo la característica `onMethod` de Lombok copia las anotaciones de DynamoDB basadas en atributos, como `@DynamoDbPartitionKey`, en el código generado.

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

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

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

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

# Usar expresiones y condiciones
<a name="ddb-en-client-expressions"></a>

Las expresiones de la API de cliente mejorado de DynamoDB son representaciones Java de las [expresiones de DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.html).

La API de cliente mejorado de DynamoDB utiliza tres tipos de expresiones:

[Expression](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Expression.html)  
La clase `Expression` se usa al definir condiciones y filtros.

[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html)  
Este tipo de expresión representa las [condiciones clave para las](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.KeyConditionExpressions) operaciones de consulta.

[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/update/UpdateExpression.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/update/UpdateExpression.html)  
Esta clase le ayuda a escribir [expresiones de actualización](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html) de DynamoDB y actualmente se usa en el marco de extensiones al actualizar un elemento.

## Anatomía de una expresión
<a name="ddb-en-client-expressions-compoonents"></a>

Una expresión consta de lo siguiente:
+ Una expresión de cadena (obligatoria). La cadena contiene una expresión lógica de DynamoDB con nombres de marcadores de posición para los nombres y valores de los atributos.
+ Un mapa de valores de la expresión (normalmente obligatorio).
+ Un mapa de nombres de expresiones (opcional).

Utilice un generador para generar un objeto `Expression` que adopte la siguiente forma general.

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

Las `Expression` suelen requerir un mapa de valores de expresión. El mapa proporciona los valores de los marcadores de posición de la expresión de cadena. La clave del mapa consta del nombre del marcador de posición precedido de dos puntos (`:`) y el valor del mapa es una instancia de. [AttributeValue](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/AttributeValue.html) La [AttributeValues](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/AttributeValues.html)clase tiene métodos prácticos para generar una `AttributeValue` instancia a partir de un literal. Como alternativa, puede usar el `AttributeValue.Builder` para generar una instancia de `AttributeValue`.

El siguiente fragmento muestra un mapa con dos entradas después de la línea de comentarios 2. La cadena pasada al método `expression()`, mostrada después de la línea de comentario 1, contiene los marcadores de posición que DynamoDB resuelve antes de realizar la operación. Este fragmento no contiene un mapa de nombres de expresiones, ya que el *precio* es un nombre de atributo permitido.

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

Si el nombre de un atributo de la tabla de DynamoDB es una palabra reservada, comienza por un número o contiene un espacio, se requiere un mapa de nombres de expresiones para el `Expression`.

Por ejemplo, si el nombre del atributo fuera `price` en lugar de `1price` en el ejemplo de código anterior, habría que modificar el ejemplo como se muestra en el siguiente ejemplo.

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

Un marcador de posición del nombre de una expresión comienza con el signo de almohadilla (`#`). Una entrada del mapa de nombres de expresiones utiliza el marcador de posición como clave y el nombre del atributo como valor. El mapa se agrega al generador de expresiones con el método `expressionNames()`. DynamoDB resuelve el nombre del atributo antes de efectuar la operación.

Los valores de expresión no son necesarios si se utiliza una función en la expresión de cadena. Un ejemplo de función de expresión es `attribute_exists(<attribute_name>)`.

En el ejemplo siguiente, se crea una `Expression` que utiliza una [función de DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions). La cadena de expresión de este ejemplo no utiliza marcadores de posición. Esta expresión podría usarse en una operación `putItem` para comprobar si ya existe un elemento en la base de datos con un valor de atributo `movie` igual al atributo `movie` del objeto de datos.

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

La Guía para desarrolladores de DynamoDB contiene información completa sobre las [expresiones de bajo nivel](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.html) que se utilizan con DynamoDB.

## Expresiones de condición y condicionales
<a name="ddb-en-client-expressions-cond"></a>

Cuando se utilizan los métodos `putItem()`, `updateItem()` y `deleteItem()`, y también cuando se utilizan operaciones de transacción y lote, se usan objetos `[Expression](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Expression.html)` para especificar las condiciones que DynamoDB debe cumplir para continuar con la operación. Estas expresiones se denominan expresiones de condición. Para ver un ejemplo, consulte la expresión de condición utilizada en el método `addDeleteItem()` (después de la línea de comentario 1) del [ejemplo de transacción](ddb-en-client-use-multiop-trans.md#ddb-en-client-use-multiop-trans-writeitems-opcondition) que se muestra en esta guía.

Cuando se trabaja con los métodos `query()`, una condición se expresa como [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html). La clase `QueryConditional` tiene varios métodos prácticos estáticos que le ayudan a escribir los criterios que determinan qué elementos leer de DynamoDB.

Para ver ejemplos de `QueryConditionals`, consulte el primer ejemplo de código de la sección [Ejemplos del método `Query`](ddb-en-client-use-multirecord.md#ddb-en-client-use-multirecord-query-example) de esta guía.

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

Las expresiones de filtro se utilizan en las operaciones de análisis y consulta para filtrar los elementos que se devuelven. 

Una expresión de filtro se aplica después de haber leído todos los datos de la base de datos, por lo que el coste de lectura es el mismo que si no hubiera filtro. La *Guía para desarrolladores de Amazon DynamoDB* contiene más información sobre el uso de expresiones de filtro para las operaciones de [consulta](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Query.html#Query.FilterExpression) y [análisis](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Scan.html#Scan.FilterExpression).

En el ejemplo siguiente, se muestra una expresión de filtro agregada a una solicitud de análisis. El criterio restringe los artículos devueltos a artículos con un precio entre 8 y 80 euros, ambos incluidos.

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

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

## Expresiones de actualización
<a name="ddb-en-client-expressions-update"></a>

El método `updateItem()` de cliente mejorado de DynamoDB proporciona una forma estándar de actualizar elementos en DynamoDB. [Sin embargo, cuando necesite más funciones, [UpdateExpressions](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/update/UpdateExpression.html)proporcione una representación segura de la sintaxis de las expresiones de actualización de DynamoDB.](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html) Por ejemplo, puede usar `UpdateExpressions` para aumentar los valores sin leer primero los elementos de DynamoDB o agregar miembros individuales a una lista. Las expresiones de actualización están disponibles actualmente en extensiones personalizadas para el método `updateItem()`.

Para ver un ejemplo en el que se utilizan expresiones de actualización, consulte el [ejemplo de extensión personalizada](ddb-en-client-extensions-custom.md) de esta guía.

Encontrará más información sobre las expresiones de actualización en la [Guía para desarrolladores de Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html).

# Trabajar con resultados paginados: análisis y consultas
<a name="ddb-en-client-use-multirecord"></a>

Los métodos `scan`, `query` y `batch` de la API de cliente mejorado de DynamoDB devuelven respuestas con una o varias *páginas*. Una página contiene uno o varios elementos. El código puede procesar la respuesta por página o procesar elementos individuales.

Una respuesta paginada devuelta por el `DynamoDbEnhancedClient` cliente síncrono devuelve un [PageIterable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/PageIterable.html)objeto, mientras que una respuesta devuelta por el cliente `DynamoDbEnhancedAsyncClient` asíncrono devuelve un objeto. [PagePublisher](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/PagePublisher.html)

En esta sección se analiza el procesamiento de los resultados paginados y se proporcionan ejemplos en los que se utilizan el escaneo y la consulta. APIs

## Examinar una tabla
<a name="ddb-en-client-use-multirecord-scan"></a>

El método [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbAsyncTable.html#scan(java.util.function.Consumer)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbAsyncTable.html#scan(java.util.function.Consumer)) del SDK corresponde a la [operación de DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html) del mismo nombre. La API de cliente mejorado de DynamoDB ofrece las mismas opciones, pero utiliza un modelo de objetos conocido y gestiona la paginación por usted.

En primer lugar, exploramos la `PageIterable` interfaz analizando el `scan` método de la clase de mapeo síncrono,. [DynamoDbTable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html)

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

El siguiente ejemplo muestra el método `scan` que usa una [expresión](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Expression.html) para filtrar los elementos que se devuelven. [ProductCatalog](ddb-en-client-use.md#ddb-en-client-use-compare-cs3)Es el objeto modelo que se mostró anteriormente.

La expresión de filtrado que se muestra después de la línea de comentario 2 limita los `ProductCatalog` artículos devueltos a aquellos con un precio comprendido entre 8,00 y 80,00€, ambos inclusive.

En este ejemplo también se excluyen `isbn` los valores mediante el `attributesToProject` método que se muestra después de la línea de comentario 1.

Después de la línea de comentario 3, el objeto `PageIterable`, `pagedResults`, es devuelto por el método `scan`. El método `stream` de `PageIterable` devuelve un objeto [https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html), que puede utilizar para procesar las páginas. En este ejemplo, se cuenta y se registra el número de páginas.

Empezando por la línea de comentarios 4, el ejemplo muestra dos variantes del acceso a los elementos de `ProductCatalog`. La versión posterior a la línea de comentarios 4a recorre cada página y ordena y registra los elementos de cada página. La versión siguiente a la línea de comentarios 4b omite la iteración de la página y accede a los elementos directamente.

La interfaz `PageIterable` ofrece varias formas de procesar los resultados debido a sus dos interfaces principales, [https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html](https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html) y [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/pagination/sync/SdkIterable.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/pagination/sync/SdkIterable.html). `Iterable` trae los métodos `forEach`, `iterator` y `spliterator`, y `SdkIterable` el método `stream`.

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

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

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

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

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

### Utilizar la API asíncrona
<a name="ddb-en-client-use-multirecord-scan-async"></a>

El método `scan` asíncrono devuelve los resultados como un objeto `PagePublisher`. La interfaz de `PagePublisher` tiene dos métodos `subscribe` que puede utilizar para procesar las páginas de respuesta. Un método `subscribe` proviene de la interfaz principal `org.reactivestreams.Publisher`. Para procesar páginas con esta primera opción, pase una instancia `[Subscriber](https://www.reactive-streams.org/reactive-streams-1.0.0-javadoc/org/reactivestreams/Subscriber.html)` al método `subscribe`. En el ejemplo siguiente se muestra el uso del método `subscribe`.

El segundo `subscribe` método proviene de la interfaz. [SdkPublisher](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/SdkPublisher.html) Esta versión de `subscribe` acepta un [https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html](https://docs.oracle.com/javase/8/docs/api/java/util/function/Consumer.html) en lugar de un `Subscriber`. Esta variación del método `subscribe` se muestra en el segundo ejemplo siguiente.

El ejemplo siguiente muestra la versión asíncrona del método `scan` que utiliza la misma expresión de filtro que se muestra en el ejemplo anterior. 

Tras la línea de comentario 3, `DynamoDbAsyncTable.scan` devuelve un objeto `PagePublisher`. En la siguiente línea, el código crea una instancia de la interfaz de `org.reactivestreams.Subscriber`, `ProductCatalogSubscriber`, que se suscribe a la `PagePublisher` después de la línea de comentarios 4.

El objeto `Subscriber` recopila los elementos `ProductCatalog` de cada página del método `onNext` después de la línea de comentarios 8 del ejemplo de clase `ProductCatalogSubscriber`. Los elementos se almacenan en la variable `List` privada y se accede a ellos en el código de llamada con el método `ProductCatalogSubscriber.getSubscribedItems()`. Esto se invoca después de la línea de comentarios 5.

Una vez recuperada la lista, el código ordena todos los artículos `ProductCatalog` por precio y registra cada uno de ellos.

El comando [CountDownLatch](https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html)in the `ProductCatalogSubscriber` class bloquea el hilo de llamada hasta que todos los elementos se hayan agregado a la lista antes de continuar después de la línea de comentarios 5. 

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

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

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

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

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

        @Override
        public void onError(Throwable throwable) {
        }

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

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

En el siguiente ejemplo de fragmento, se utiliza la versión del método `PagePublisher.subscribe` que acepta una `Consumer` después de la línea de comentario 6. El parámetro lambda de Java consume páginas, que procesan aún más cada elemento. En este ejemplo, se procesa cada página y los elementos de cada página se ordenan y, a continuación, se registran.

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

El método `items` de `PagePublisher` separa las instancias del modelo para que el código pueda procesar los elementos directamente. Este método se muestra en el fragmento de código siguiente.

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

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

Puede utilizar DynamoDB Enhanced Client para consultar la tabla y recuperar varios elementos que coincidan con criterios específicos. El método [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html#query(software.amazon.awssdk.enhanced.dynamodb.model.QueryEnhancedRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html#query(software.amazon.awssdk.enhanced.dynamodb.model.QueryEnhancedRequest)) busca los elementos en función de los valores clave principales mediante la anotación `@DynamoDbPartitionKey` y, opcionalmente, la `@DynamoDbSortKey` definidas en la clase de datos.

El método `query()` requiere un valor de clave de partición y, de forma opcional, acepta condiciones de clave de clasificación para afinar aún más los resultados. Al igual que la API `scan`, las consultas devuelven una `PageIterable` para llamadas sincrónicas y una `PagePublisher` para asincrónicas.

### Ejemplos del método `Query`
<a name="ddb-en-client-use-multirecord-query-example"></a>

El ejemplo de código del método `query()` siguiente utiliza la clase `MovieActor`. La clase de datos define una clave primaria compuesta que se compone del atributo **`movie`** como clave de partición y el atributo **`actor`** como clave de clasificación. 

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

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

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

import java.util.Objects;

@DynamoDbBean
public class MovieActor implements Comparable<MovieActor> {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Los ejemplos de código que aparecen a continuación se refieren a los siguientes elementos.

#### Elementos de la tabla `MovieActor`
<a name="ddb-en-client-use-movieactor-items"></a>

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

El siguiente código define dos instancias `QueryConditional`: `keyEqual` (después de la línea de comentario 1) y `sortGreaterThanOrEqualTo` (después de la línea de comentario 1a).

#### Consulta de elementos por clave de partición
<a name="keyEqual-query-conditional-example"></a>

La instancia `keyEqual` asocia los elementos con un valor de clave de partición de **`movie01`**. 

Este ejemplo también define una expresión de filtro después de la línea de comentario 2 que filtra cualquier elemento que no tenga un valor de **`actingschoolname`**.

La `QueryEnhancedRequest` combina la condición de clave y la expresión de filtro de la consulta.

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

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

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

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

**Example — Salida utilizando el condicional de consulta `keyEqual`**  
Se genera la siguiente salida de la ejecución del método. El resultado muestra los elementos con un valor `movieName` de **movie01** y no muestra ningún elemento con `actingSchoolName` igual a **`null`**.  

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

#### Consulta de elementos por clave de partición y clave de clasificación
<a name="sort-type-query-conditional-example"></a>

La `QueryConditional` `sortGreaterThanOrEqualTo` afina la coincidencia de clave de partición (**movie01**) añadiendo una condición de clave de clasificación para valores mayores o iguales que **actor2**.

Los [métodos de `QueryConditional`](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html) que comienzan por `sort` requieren un valor de clave de partición para asociar y afinar aún más la consulta mediante una comparación basada en el valor de la clave de clasificación. `Sort` en el nombre del método no significa que los resultados estén clasificados, sino que se utilizará un valor de clave de clasificación para comparación.

En el siguiente fragmento cambiamos la solicitud de consulta que mostrada anteriormente después de la línea de comentario 3. Este fragmento reemplaza el condicional de consulta «keyEqual» por el condicional de consulta «» que se definió sortGreaterThan OrEqualTo después de la línea de comentario 1a. El código siguiente también elimina la expresión del filtro.

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

**Example — Salida utilizando el condicional de consulta `sortGreaterThanOrEqualTo`**  
La siguiente salida muestra los resultados de la consulta. La consulta devuelve los elementos que tienen un valor `movieName` igual a **movie01** y solo los elementos que tienen un valor `actorName` mayor o igual a **actor2**. Dado que quitamos el filtro, la consulta devuelve elementos que no tienen ningún valor para el atributo `actingSchoolName`.  

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

# Realizar operaciones por lotes
<a name="ddb-en-client-use-multiop-batch"></a>

La API de cliente mejorado de DynamoDB ofrece dos métodos por lotes, [`batchGetItem`()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#batchGetItem(java.util.function.Consumer)) y [`batchWriteItem`()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#batchWriteItem(java.util.function.Consumer)).

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

Con el método [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#batchGetItem(java.util.function.Consumer)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#batchGetItem(java.util.function.Consumer)), puede recuperar hasta 100 elementos individuales de varias tablas en una solicitud general. En el siguiente ejemplo, se utilizan las clases de datos [`Customer`](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust) y [`MovieActor`](ddb-en-client-use-multirecord.md#ddb-en-client-use-movieactor-class) mostradas anteriormente.

En el ejemplo, después de las líneas 1 y 2, se crean objetos `[ReadBatch](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/ReadBatch.html)` que se añaden posteriormente como parámetros al método `batchGetItem()` después de la línea de comentarios 3. 

El código que sigue a la línea de comentarios 1 crea el lote para leerlo de la tabla `Customer`. El código que sigue a la línea de comentario 1a muestra el uso de un compilador de `[GetItemEnhancedRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/GetItemEnhancedRequest.Builder.html)` que toma un valor de clave principal y un valor de clave de clasificación para especificar el elemento que se va a leer. Si la clase de datos tiene una clave compuesta, debe proporcionar tanto el valor de la clave de partición como el valor de la clave de clasificación. 

A diferencia de especificar valores clave para solicitar un elemento, puede usar una clase de datos para solicitar un elemento, como se muestra después de la línea de comentario 1b. El SDK extrae automáticamente los valores clave de manera interna antes de enviar la solicitud.

Cuando especifique el elemento utilizando el enfoque basado en claves, como se muestra en las dos instrucciones posteriores a 2a, también puede especificar que DynamoDB realice una [lectura altamente coherente](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadConsistency.html). Cuando se utilice el método `consistentRead()`, se debe utilizar en todos los elementos solicitados de la misma tabla.

Para recuperar los elementos encontrados por DynamoDB, utilice el método `[resultsForTable() ](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchGetResultPage.html#resultsForTable(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource))` que se muestra después de la línea de comentarios 4. Llame al método de cada tabla que se haya leído en la solicitud. `resultsForTable()` devuelve una lista de los elementos encontrados que puede procesar mediante cualquier método `java.util.List`. En este ejemplo se registra cada elemento.

Para descubrir elementos que DynamoDB no procesó, utilice el enfoque que aparece después de la línea de comentarios 5. La clase `BatchGetResultPage` tiene el método `[unprocessedKeysForTable()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchGetResultPage.html#unprocessedKeysForTable(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource))` que permite acceder a todas las claves que no se hayan procesado. La [referencia de la BatchGetItem API contiene](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchGetItem.html) más información sobre situaciones que dan como resultado elementos sin procesar.

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

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

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

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

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

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

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

Suponga que los siguientes elementos están en las dos tablas antes de ejecutar el código de ejemplo.

### Elementos de la tabla
<a name="ddb-en-client-use-multiop-batch-get-tableitems"></a>

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

El siguiente resultado muestra los elementos devueltos y registrados después de la línea de comentarios 4.

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

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

El método `batchWriteItem()` pone o borra varios elementos en una o varias tablas. Puede especificar hasta 25 operaciones individuales de colocación o borrado en la solicitud. En el siguiente ejemplo, se utilizan las clases de modelos [`ProductCatalog`](ddb-en-client-use.md#ddb-en-client-use-compare-cs3) y [`MovieActor`](ddb-en-client-use-multirecord.md#ddb-en-client-use-movieactor-class) mostradas anteriormente.

Los objetos de `WriteBatch` se crean después de las líneas de comentario 1 y 2. En la tabla `ProductCatalog`, el código coloca un elemento y elimina otro. En la tabla `MovieActor` situada después de la línea de comentarios 2, el código coloca dos elementos y elimina uno.

El método `batchWriteItem` se llama después de la línea de comentarios 3. El parámetro `[builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchWriteItemEnhancedRequest.Builder.html)` proporciona las solicitudes de lote para cada tabla.

El objeto devuelto `[BatchWriteResult](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/BatchWriteResult.html)` proporciona métodos independientes para cada operación a fin de ver las solicitudes no procesadas. El código que sigue a la línea de comentarios 4a proporciona las claves para las solicitudes de eliminación sin procesar y el código que sigue a la línea de comentarios 4b proporciona los elementos de venta sin procesar.

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

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

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

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

Los siguientes métodos auxiliares proporcionan los objetos modelo para las operaciones de colocación y eliminación.

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

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

Suponga que las tablas contienen los siguientes elementos antes de ejecutar el código de ejemplo.

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

Una vez finalizado el código de ejemplo, las tablas contienen los siguientes elementos.

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

Observe en la tabla `MovieActor` que el elemento de la película `Blue Jasmine` se ha sustituido por el elemento utilizado en la solicitud de venta adquirida mediante el método auxiliar `getMovieActorBlanchettPartial()`. Si no se ha proporcionado el valor de un atributo de un objeto de datos, se elimina el valor de la base de datos. Esta es la razón por la que el `actingSchoolName` resultante es nulo para el elemento de la película `Blue Jasmine`.

**nota**  
Aunque la documentación de la API sugiere que pueden utilizarse expresiones de condición y que las métricas de capacidad consumida y de recogida pueden devolverse con solicitudes individuales de [put](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/PutItemEnhancedRequest.html) y [delete](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/DeleteItemEnhancedRequest.html), esto no es así en un escenario de escritura por lotes. Para mejorar el rendimiento de las operaciones por lotes, se ignoran estas opciones individuales.

# Realizar operaciones de transacciones
<a name="ddb-en-client-use-multiop-trans"></a>

La API de cliente mejorado de DynamoDB proporciona los `transactGetItems()` y los métodos `transactWriteItems()`. Los métodos de transacción del SDK para Java proporcionan atomicidad, consistencia, aislamiento y durabilidad (ACID) en las tablas de DynamoDB, ayudándole a mantener la corrección de los datos en sus aplicaciones.

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

El método `[transactGetItems()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#transactGetItems(java.util.function.Consumer))` admite hasta 100 solicitudes individuales de artículos. Todos los artículos se leen en una sola transacción atómica. La *Guía para desarrolladores de Amazon DynamoDB* contiene información sobre las [condiciones que hacen que un método `transactGetItems()` falle](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-txgetitems), y también sobre el nivel de aislamiento utilizado cuando se llama a `[transactGetItem()](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-isolation)`.

Tras la línea de comentarios 1 del siguiente ejemplo, el código llama al método `transactGetItems()` con un parámetro `[builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactGetItemsEnhancedRequest.Builder.html)`. El generador `[addGetItem()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactGetItemsEnhancedRequest.Builder.html#addGetItem(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource,T))` se invoca tres veces con un objeto de datos que contiene los valores clave que el SDK utilizará para generar la solicitud final.

La solicitud devuelve una lista de objetos `[Document](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Document.html)` después de la línea de comentarios 2. La lista de documentos que se devuelve contiene instancias de [documentos](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Document.html) no nulas de los datos de los artículos en el mismo orden solicitado. El método `[Document.getItem(MappedTableResource<T> mappedTableResource)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/Document.html#getItem(software.amazon.awssdk.enhanced.dynamodb.MappedTableResource))` convierte un objeto sin tipo en un objeto `Document` Java con tipo si se devolvieron los datos del elemento; de lo contrario, el método devuelve un valor nulo.

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

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

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

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

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

Las tablas de DynamoDB contienen los siguientes elementos antes de que se ejecute el ejemplo de código.

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

Se registra la siguiente salida. Si se solicita un elemento pero no se encuentra, no se devuelve, como ocurre con la solicitud de la película nombrada `Blue Jasmine`.

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

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

El `[transactWriteItems()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#transactWriteItems(java.util.function.Consumer))` acepta hasta 100 acciones para colocar, actualizar o eliminar en una única transacción atómica a través de varias tablas. La *Guía para desarrolladores de Amazon DynamoDB* contiene detalles sobre las restricciones y las condiciones de fallo del [funcionamiento del servicio DynamoDB subyacente](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html#transaction-apis-txwriteitems).

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

En el ejemplo siguiente, se solicitan cuatro operaciones para dos tablas. Las clases de modelos correspondientes [`ProductCatalog`](ddb-en-client-use.md#ddb-en-client-use-compare-cs3) y [`MovieActor`](ddb-en-client-use-multirecord.md#ddb-en-client-use-movieactor-class) se mostraron anteriormente.

Cada una de las tres operaciones posibles (poner, actualizar y eliminar) utiliza un parámetro de solicitud específico para especificar los detalles. 

El código bajo la línea de comentarios 1 muestra la variación simple del método `addPutItem()`. El método acepta un objeto `[MappedTableResource](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/MappedTableResource.html)` y la instancia del objeto de datos para colocarlo. La declaración que sigue a la línea de comentarios 2 muestra la variación que acepta una instancia `[TransactPutItemEnhancedRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactPutItemEnhancedRequest.html)`. Esta variante permite añadir más opciones a la solicitud, como una expresión de condición. En un [ejemplo](#ddb-en-client-use-multiop-trans-writeitems-opcondition) posterior, se muestra una expresión de condición para una operación individual.

Se solicita una operación de actualización después de la línea de comentario 3. `[TransactUpdateItemEnhancedRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/TransactUpdateItemEnhancedRequest.Builder.html)` tiene un método `ignoreNulls()` que permite configurar lo que hace el SDK con los valores `null` del objeto modelo. Si el método `ignoreNulls()` devuelve el valor true, el SDK no elimina los valores de los atributos de la tabla para los atributos del objeto de datos que son `null`. Si el método `ignoreNulls()` devuelve false, el SDK solicita al servicio DynamoDB que elimine los atributos del elemento de la tabla. El valor predeterminado de `ignoreNulls` es false.

La instrucción tras la línea de comentario 4 muestra la variación de una petición de borrado que toma un objeto de datos. El cliente mejorado extrae los valores clave antes de enviar la solicitud final.

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

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

Los siguientes métodos auxiliares proporcionan los objetos de datos para los parámetros `add*Item`.

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

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

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

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

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

Las tablas de DynamoDB contienen los siguientes elementos antes de que se ejecute el ejemplo de código.

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

Los siguientes elementos aparecen en las tablas después de que el código termine de ejecutarse.

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

Se ha eliminado el elemento de la línea 2 y las líneas 3 y 5 muestran los elementos que se colocaron. La línea 4 muestra la actualización de la línea 1. El valor `price` es el único que ha cambiado en el artículo. Si `ignoreNulls()` hubiera devuelto false, la línea 4 tendría el siguiente aspecto.

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

### Ejemplo de comprobación de estado
<a name="ddb-en-client-use-multiop-trans-writeitems-checkcond"></a>

En el siguiente ejemplo, se muestra el uso de una comprobación de estado. Una comprobación de estado se utiliza para comprobar la existencia de un elemento o para comprobar el estado de atributos concretos de un elemento en la base de datos. El artículo registrado en la verificación de estado no se puede utilizar en otra operación de la transacción.

**nota**  
No se pueden dirigir varias operaciones de la misma transacción al mismo elemento. Por ejemplo, no puede llevar a cabo una comprobación de condiciones y además intentar actualizar el mismo artículo en la misma transacción.

El ejemplo muestra una operación de cada tipo en una solicitud transaccional de escritura de artículos. Después de la línea de comentario 2, el método `addConditionCheck()` suministra la condición que falla la transacción si el parámetro `conditionExpression` se evalúa como `false`. La expresión de condición que devuelve el método mostrado en el bloque Métodos auxiliares comprueba si el año de concesión de la película `Sophie's Choice` no es igual a `1982`. Si es así, la expresión se evalúa como `false` y la transacción no se realiza correctamente.

Esta guía analiza en profundidad [las expresiones](ddb-en-client-expressions.md) en otro tema.

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

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

En el ejemplo de código anterior se utilizan los siguientes métodos auxiliares.

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

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

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

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

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

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

Las tablas de DynamoDB contienen los siguientes elementos antes de que se ejecute el ejemplo de código.

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

Los siguientes elementos aparecen en las tablas después de que el código termine de ejecutarse.

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

Los elementos permanecen sin cambios en las tablas porque se produjo un error en la transacción. El valor `actingYear` de la película `Sophie's Choice` es `1982`, como muestra la línea 2 de los elementos de la tabla antes de llamar al método `transactWriteItem()`.

Para capturar la información de cancelación de la transacción, incluya la llamada al método `transactWriteItems()` en un bloque `try` y `catch` el [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/TransactionCanceledException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/TransactionCanceledException.html). Tras la línea de comentario 4 del ejemplo, el código registra cada objeto `[CancellationReason](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/CancellationReason.html)`. Dado que el código que sigue a la línea de comentario 3 del ejemplo especifica que deben devolverse los valores del elemento que provocó el fallo de la transacción, el registro muestra los valores brutos de la base de datos para el elemento de la película `Sophie's Choice`.

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

### Ejemplo de condición de operación única
<a name="ddb-en-client-use-multiop-trans-writeitems-opcondition"></a>

El siguiente ejemplo muestra el uso de una condición en una sola operación de una solicitud de transacción. La operación de eliminación después de la línea de comentarios 1 contiene una condición que compara el valor del elemento objetivo de la operación con la base de datos. En este ejemplo, la expresión de condición creada con el método auxiliar después de la línea de comentario 2 especifica que el elemento debe eliminarse de la base de datos si el año de actuación de la película no es igual a 2013.

Las [expresiones](ddb-en-client-expressions.md) se describen más adelante en esta guía.

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

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

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

En el ejemplo de código anterior se utilizan los siguientes métodos auxiliares.

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

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

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

Las tablas de DynamoDB contienen los siguientes elementos antes de que se ejecute el ejemplo de código.

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

Los siguientes elementos aparecen en las tablas después de que el código termine de ejecutarse.

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

Los elementos permanecen sin cambios en las tablas porque se produjo un error en la transacción. El valor `actingYear` para la película `Blue Jasmine` es `2013` como se muestra en la línea 2 de la lista de elementos antes de que se ejecute el ejemplo de código.

Las líneas siguientes se registran en la consola.

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

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

Los índices secundarios mejoran el acceso a los datos al definir claves alternativas que se utilizan en las operaciones de consulta y análisis. Índice secundario global (GSI): índice con una clave de partición y una clave de clasificación que pueden ser diferentes de las de la tabla base. Por el contrario, los índices secundarios locales (LSI) utilizan la clave de partición del índice principal.

## Anote la clase de datos con anotaciones de índice secundarias
<a name="ddb-en-client-use-secindex-annomodel"></a>

Los atributos que participan en índices secundarios requieren la anotación `@DynamoDbSecondaryPartitionKey` o `@DynamoDbSecondarySortKey`.

La siguiente clase muestra las anotaciones de dos índices. El GSI denominado *SubjectLastPostedDateIndex*usa el `Subject` atributo para la clave de partición y el atributo `LastPostedDateTime` para la clave de clasificación. El LSI denominado *ForumLastPostedDateIndex*utiliza el `ForumName` como clave de partición y `LastPostedDateTime` como clave de clasificación.

Tenga en cuenta que el atributo `Subject` cumple una doble función. Es la clave de clasificación de la clave principal y la clave de partición del GSI denominado. *SubjectLastPostedDateIndex*

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

La clase `MessageThread` es adecuada para utilizarla como clase de datos en la [tabla Thread de ejemplo](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AppendixSampleTables.html) de la *Guía para desarrolladores de Amazon DynamoDB*.

#### Importaciones
<a name="ddb-en-client-use-secindex-classimports"></a>

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

import java.util.List;
```

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

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

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

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

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

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

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

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

    public String getLastPostedBy() {
        return LastPostedBy;
    }

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

    public Integer getViews() {
        return Views;
    }

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

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

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

    public Integer getAnswered() {
        return Answered;
    }

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

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

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

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

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

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

A partir de la versión 2.20.86 del SDK para Java, el método `createTable()` genera automáticamente índices secundarios a partir de las anotaciones de las clases de datos. De forma predeterminada, todos los atributos de la tabla base se copian en un índice y los valores de rendimiento aprovisionados son 20 unidades de capacidad de lectura y 20 unidades de capacidad de escritura.

Sin embargo, si utiliza una versión del SDK anterior a la 2.20.86, debe crear el índice junto con la tabla, como se muestra en el siguiente ejemplo. En este ejemplo, se crean los dos índices de la tabla `Thread`. El parámetro [constructor](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/CreateTableEnhancedRequest.Builder.html) tiene métodos para configurar ambos tipos de índices, como se muestra después de las líneas de comentario 1 y 2. Utilice el método del generador de índices `indexName()` para asociar los nombres de índice especificados en las anotaciones de las clases de datos con el tipo de índice deseado.

Este código configura todos los atributos de la tabla para que terminen en ambos índices después de las líneas de comentario 3 y 4. Encontrará más información sobre las [proyecciones de atributos](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/LSI.html#LSI.Projections) en la *Guía para desarrolladores de Amazon DynamoDB*.

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

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

El siguiente ejemplo consulta el índice secundario global *ForumLastPostedDateIndex*.

Tras la línea de comentario 2, se crea un [QueryConditional](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/QueryConditional.html)objeto que es obligatorio al llamar al método [DynamoDbIndex.query ()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbIndex.html#query(java.util.function.Consumer)). 

Para obtener una referencia al índice que desea consultar después de la línea de comentario 3, debe pasar al nombre del índice. Tras la línea de comentario 4, se llama al método `query()` del índice que pasa al objeto `QueryConditional`. 

También puede configurar la consulta para que devuelva tres valores de atributo, como se muestra tras la línea de comentario 5. Si no se llama `attributesToProject()`, la consulta devuelve todos los valores de los atributos. Los nombres de los atributos especificados comienzan con letras minúsculas. Estos nombres coinciden con los utilizados en la tabla, no necesariamente con los de la clase de datos.

Siguiendo la línea de comentario 6, se itera a través de los resultados y se registra cada elemento devuelto por la consulta y también se almacena en la lista para devolvérsela a la persona que llama.

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

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

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

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

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

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

En la base de datos existen los siguientes elementos antes de que se ejecute la consulta.

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

Las instrucciones de registro de las líneas 1 y 6 dan como resultado la siguiente salida de consola.

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

La consulta devolvió elementos con un valor `forumName` de *Forum02* y un valor de `lastPostedDateTime` superior o igual a *2023.03.31*. Los resultados muestran valores `message` con una cadena vacía, aunque los atributos `message` tienen valores en el índice. Esto se debe a que el atributo del mensaje no se proyectó mediante código después de la línea de comentario 5. 

# Utilizar características de asignación avanzadas
<a name="ddb-en-client-adv-features"></a>

Obtenga información sobre las características avanzadas del esquema de tablas en la API del cliente mejorado de DynamoDB.

## Comprender los tipos de esquema de tabla
<a name="ddb-en-client-adv-features-schm-overview"></a>

`[TableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/TableSchema.html)` es la interfaz para la funcionalidad de asignación de la API del cliente mejorado de DynamoDB. Puede mapear un objeto de datos hacia y desde un mapa de [AttributeValues](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/AttributeValue.html). Un objeto `TableSchema` necesita conocer la estructura de la tabla que está asignando. Esta información de estructura se almacena en un objeto [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/TableMetadata.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/TableMetadata.html).

La API del cliente mejorado tiene varias implementaciones de `TableSchema`, indicadas a continuación. 

### Esquema de tabla generado a partir de clases anotadas
<a name="ddb-en-client-adv-features-schema-mapped"></a>

Es una operación algo costosa construir un `TableSchema` a partir de clases anotadas, por lo que recomendamos hacerlo una vez, al inicio de la aplicación.

 [ BeanTableSchema ](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/BeanTableSchema.html)   
Esta implementación se construye basándose en atributos y anotaciones de una clase de bean. En la sección [Introducción](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean) se muestra un ejemplo de este enfoque.  
Si un `BeanTableSchema` no se comporta como espera, active el registro de depuración para `software.amazon.awssdk.enhanced.dynamodb.beans`.

[ImmutableTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/ImmutableTableSchema.html)  
Esta implementación se crea a partir de una clase de datos inmutable. Esto se describe en la sección [Trabajar con clases de datos inmutables](ddb-en-client-use-immut.md) anterior.

### Esquema de tabla generado con un generador
<a name="ddb-en-client-adv-features-schema-static"></a>

Los siguientes `TableSchema` se crean a partir de código mediante un generador. Este enfoque es menos costoso que el enfoque que usa clases de datos anotadas. El enfoque de creación evita el uso de anotaciones y no requiere estándares de JavaBean nomenclatura.

[StaticTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticTableSchema.html)  
Esta implementación está creada para clases de datos mutables. En la sección de introducción de esta guía se muestra cómo [crear un `StaticTableSchema` mediante un generador](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-builder).

[StaticImmutableTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticImmutableTableSchema.html)  
De forma similar a como se crea un `StaticTableSchema`, se genera una implementación de este tipo de `TableSchema` utilizando un [generador](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/StaticImmutableTableSchema.html) para su uso con clases de datos inmutables.

### Esquema de tabla para datos sin un esquema fijo
<a name="ddb-en-client-adv-features-schema-document"></a>

[DocumentTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/DocumentTableSchema.html)  
A diferencia de otras implementaciones de `TableSchema`, no define los atributos de una instancia `DocumentTableSchema`. Por lo general, solo especifica las claves principales y los proveedores de convertidores de atributos. Una instancia `EnhancedDocument` proporciona los atributos que se crean a partir de elementos individuales o de una cadena JSON.

# Incluir o excluir explícitamente atributos
<a name="ddb-en-client-adv-features-inex-attr"></a>

La API del cliente mejorado de DynamoDB ofrece anotaciones para impedir que los atributos de las clases de datos se conviertan en atributos de una tabla. Con la API, también puede usar un nombre de atributo diferente del nombre de atributo de la clase de datos.

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

Para ignorar los atributos que no se deben asignar a una tabla de DynamoDB, marque el atributo con la anotación `@DynamoDbIgnore`.

```
private String internalKey;

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

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

Para cambiar el nombre de un atributo utilizado en la tabla de DynamoDB, márquelo con la anotación `@DynamoDbAttribute` y escriba un nombre diferente.

```
private String internalKey;

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

# Conversión de atributos de control
<a name="ddb-en-client-adv-features-conversion"></a>

De forma predeterminada, un esquema de tabla proporciona convertidores para muchos tipos comunes de Java mediante una implementación predeterminada de la interfaz de `[AttributeConverterProvider](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverterProvider.html)`. Puede cambiar el comportamiento predeterminado general con una implementación de `AttributeConverterProvider` personalizada. También puede cambiar el conversor de un solo atributo.

Para obtener una lista de los convertidores disponibles, consulte el documento de la [AttributeConverter](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/AttributeConverter.html)interfaz Java.

## Proporcionar proveedores de convertidores de atributos personalizados
<a name="ddb-en-client-adv-features-conversion-prov"></a>

Puede proporcionar una `AttributeConverterProvider` única o una cadena de `AttributeConverterProvider` ordenadas a través de la anotación `@DynamoDbBean` `(converterProviders = {…})`. Cualquier `AttributeConverterProvider` personalizada debe extender la interfaz `AttributeConverterProvider`.

Tenga en cuenta que si suministra su propia cadena de proveedores de convertidores de atributos, anulará el proveedor de convertidores predeterminado `DefaultAttributeConverterProvider`. Si desea utilizar la funcionalidad del `DefaultAttributeConverterProvider`, debe incluirlo en la cadena. 

También es posible anotar el bean con una matriz vacía `{}`. Esto deshabilita el uso de cualquier proveedor de conversión de atributos, incluido el predeterminado. En este caso, todos los atributos que se van a asignar deben tener su propio convertidor de atributos.

El fragmento siguiente muestra un único proveedor de convertidores.

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

}
```

El siguiente fragmento muestra el uso de una cadena de proveedores de convertidores. Como el SDK predeterminado es el último que se proporciona, tiene la prioridad más baja.

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

}
```

Los creadores de esquemas de tablas estáticas tienen un método `attributeConverterProviders()` que funciona de la misma manera. Esto se muestra en el siguiente fragmento.

```
private static final StaticTableSchema<Customer> CUSTOMER_TABLE_SCHEMA =
  StaticTableSchema.builder(Customer.class)
    .newItemSupplier(Customer::new)
    .addAttribute(String.class, a -> a.name("name")
                                     a.getter(Customer::getName)
                                     a.setter(Customer::setName))
    .attributeConverterProviders(converterProvider1, converterProvider2)
    .build();
```

## Sustituir la asignación de un único atributo
<a name="ddb-en-client-adv-features-conversion-single"></a>

Para sustituir la forma en que se asigna un único atributo, introduzca un `AttributeConverter` para el atributo. Esto anula los convertidores proporcionados por `AttributeConverterProviders` en el esquema de la tabla. Esto añade un conversor personalizado solo para ese atributo. Otros atributos, incluso los del mismo tipo, no utilizarán ese convertidor salvo que se especifique explícitamente para esos otros atributos.

La anotación `@DynamoDbConvertedBy` se usa para especificar la clase `AttributeConverter` personalizada, como se muestra en el siguiente fragmento.

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

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

Los generadores de esquemas estáticos tienen un método `attributeConverter()` de creación de atributos equivalente. Este método toma una instancia de un `AttributeConverter`, como se muestra a continuación.

```
private static final StaticTableSchema<Customer> CUSTOMER_TABLE_SCHEMA =
  StaticTableSchema.builder(Customer.class)
    .newItemSupplier(Customer::new)
    .addAttribute(String.class, a -> a.name("name")
                                     a.getter(Customer::getName)
                                     a.setter(Customer::setName)
                                     a.attributeConverter(customAttributeConverter))
    .build();
```

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

En este ejemplo, se muestra una implementación de `AttributeConverterProvider` que proporciona un conversor de atributos para objetos [https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/HttpCookie.html](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/HttpCookie.html). 

La siguiente clase `SimpleUser` contiene un nombre de atributo `lastUsedCookie` que es una instancia de `HttpCookie`.

El parámetro de las anotaciones de `@DynamoDbBean` muestra las dos clases de `AttributeConverterProvider` que proporcionan convertidores.

------
#### [ Class with annotations ]

```
    @DynamoDbBean(converterProviders = {CookieConverterProvider.class, DefaultAttributeConverterProvider.class})
    public static final class SimpleUser {
        private String name;
        private HttpCookie lastUsedCookie;

        @DynamoDbPartitionKey
        public String getName() {
            return name;
        }

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

        public HttpCookie getLastUsedCookie() {
            return lastUsedCookie;
        }

        public void setLastUsedCookie(HttpCookie lastUsedCookie) {
            this.lastUsedCookie = lastUsedCookie;
        }
```

------
#### [ Static table schema ]

```
    private static final TableSchema<SimpleUser> SIMPLE_USER_TABLE_SCHEMA =
            TableSchema.builder(SimpleUser.class)
                    .newItemSupplier(SimpleUser::new)
                    .attributeConverterProviders(CookieConverterProvider.create(), AttributeConverterProvider.defaultProvider())
                    .addAttribute(String.class, a -> a.name("name")
                            .setter(SimpleUser::setName)
                            .getter(SimpleUser::getName)
                            .tags(StaticAttributeTags.primaryPartitionKey()))
                    .addAttribute(HttpCookie.class, a -> a.name("lastUsedCookie")
                            .setter(SimpleUser::setLastUsedCookie)
                            .getter(SimpleUser::getLastUsedCookie))
                    .build();
```

------

El `CookieConverterProvider` en el ejemplo siguiente proporciona una instancia de un `HttpCookeConverter`.

```
    public static final class CookieConverterProvider implements AttributeConverterProvider {
        private final Map<EnhancedType<?>, AttributeConverter<?>> converterCache = ImmutableMap.of(
                // 1. Add HttpCookieConverter to the internal cache.
                EnhancedType.of(HttpCookie.class), new HttpCookieConverter());

        public static CookieConverterProvider create() {
            return new CookieConverterProvider();
        }

        // The SDK calls this method to find out if the provider contains a AttributeConverter instance
        // for the EnhancedType<T> argument.
        @SuppressWarnings("unchecked")
        @Override
        public <T> AttributeConverter<T> converterFor(EnhancedType<T> enhancedType) {
            return (AttributeConverter<T>) converterCache.get(enhancedType);
        }
    }
```

### Conversión de códigos
<a name="ddb-en-client-adv-features-conversion-example-code"></a>

En el método `transformFrom()` de la clase `HttpCookieConverter` siguiente, el código recibe una instancia `HttpCookie` y la transforma en un mapa de DynamoDB que se almacena como un atributo.

El método `transformTo()` recibe un parámetro de mapa de DynamoDB y, a continuación, invoca el constructor `HttpCookie` que requiere un nombre y un valor.

```
    public static final class HttpCookieConverter implements AttributeConverter<HttpCookie> {

        @Override
        public AttributeValue transformFrom(HttpCookie httpCookie) {

            return AttributeValue.fromM(
            Map.of ("cookieName", AttributeValue.fromS(httpCookie.getName()),
                    "cookieValue", AttributeValue.fromS(httpCookie.getValue()))
            );
        }

        @Override
        public HttpCookie transformTo(AttributeValue attributeValue) {
            Map<String, AttributeValue> map = attributeValue.m();
            return new HttpCookie(
                    map.get("cookieName").s(),
                    map.get("cookieValue").s());
        }

        @Override
        public EnhancedType<HttpCookie> type() {
            return EnhancedType.of(HttpCookie.class);
        }

        @Override
        public AttributeValueType attributeValueType() {
            return AttributeValueType.M;
        }
    }
```

# Cambiar el comportamiento de actualización de los atributos
<a name="ddb-en-client-adv-features-upd-behavior"></a>

Puede personalizar el comportamiento de actualización de los atributos individuales al realizar una operación de *actualización*. [Algunos ejemplos de operaciones de actualización en la API de cliente mejorada de DynamoDB [son updateItem](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html#updateItem(T)) () y (). transactWriteItems](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html#transactWriteItems(java.util.function.Consumer))

Por ejemplo, imagine que desea almacenar en su registro una marca de tiempo de tipo *creado en* el registro. Sin embargo, desea que su valor se escriba solo si no existe ningún valor para el atributo en la base de datos. En este caso, utiliza el comportamiento de actualización de `[WRITE\$1IF\$1NOT\$1EXISTS](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/UpdateBehavior.html#WRITE_IF_NOT_EXISTS)`.

En el siguiente ejemplo, se muestra la anotación que añade el comportamiento al `createdOn` atributo.

```
@DynamoDbBean
public class Customer extends GenericRecord {
    private String id;
    private Instant createdOn;

    @DynamoDbPartitionKey
    public String getId() { return this.id; }
    public void setId(String id) { this.name = id; }

    @DynamoDbUpdateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS)
    public Instant getCreatedOn() { return this.createdOn; }    
    public void setCreatedOn(Instant createdOn) { this.createdOn = createdOn; }
}
```

Puede declarar el mismo comportamiento de actualización al crear un esquema de tabla estática, como se muestra en el siguiente ejemplo después de la línea de comentario 1.

```
static final TableSchema<Customer> CUSTOMER_TABLE_SCHEMA =
     TableSchema.builder(Customer.class)
       .newItemSupplier(Customer::new)
       .addAttribute(String.class, a -> a.name("id")
                                         .getter(Customer::getId)
                                         .setter(Customer::setId)
                                         .tags(StaticAttributeTags.primaryPartitionKey()))
       .addAttribute(Instant.class, a -> a.name("createdOn")
                                          .getter(Customer::getCreatedOn)
                                          .setter(Customer::setCreatedOn)
                                          // 1. Add an UpdateBehavior.
                                          .tags(StaticAttributeTags.updateBehavior(UpdateBehavior.WRITE_IF_NOT_EXISTS)))
       .build();
```

# Aplanar atributos de otras clases
<a name="ddb-en-client-adv-features-flatmap"></a>

Si los atributos de su tabla están repartidos en varias clases Java diferentes, ya sea por herencia o composición, la API del cliente mejorado de DynamoDB proporciona soporte para aplanar los atributos en una sola clase.

## Utilizar la herencia
<a name="ddb-en-client-adv-features-flatmap-inheritance"></a>

Si sus clases utilizan herencias, utilice los siguientes enfoques para aplanar la jerarquía.

### Utilizar objetos bean anotados
<a name="ddb-en-client-adv-features-flatmap-inheritance-anno"></a>

Para el método de anotación, ambas clases deben llevar la anotación `@DynamoDbBean` y una de ellas llevar una o más anotaciones de clave principal.

A continuación, se muestran ejemplos de clases de datos que tienen una relación de herencia.

------
#### [ Standard data class ]

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

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

@DynamoDbBean
public abstract class GenericRecord {
    private String id;
    private String createdDate;

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

    public String getCreatedDate() { return createdDate; }
    public void setCreatedDate(String createdDate) { this.createdDate = createdDate; }
}
```

------
#### [ Lombok ]

La [opción `onMethod` de Lombok](https://projectlombok.org/features/experimental/onX) copia anotaciones de DynamoDB basadas en atributos, como `@DynamoDbPartitionKey`, en el código generado.

```
@DynamoDbBean
@Data
@ToString(callSuper = true)
public class Customer extends GenericRecord {
    private String name;
}

@Data
@DynamoDbBean
public abstract class GenericRecord {
    @Getter(onMethod_=@DynamoDbPartitionKey)
    private String id;
    private String createdDate;
}
```

------

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

Para el enfoque de esquema estático, utilice el método `extend()` del generador para contraer los atributos de la clase principal en la clase secundaria. Esto se muestra después de la línea de comentario 1 en el siguiente ejemplo.

```
        StaticTableSchema<org.example.tests.model.inheritance.stat.GenericRecord> GENERIC_RECORD_SCHEMA =
                StaticTableSchema.builder(org.example.tests.model.inheritance.stat.GenericRecord.class)
                        // The partition key will be inherited by the top level mapper.
                        .addAttribute(String.class, a -> a.name("id")
                                .getter(org.example.tests.model.inheritance.stat.GenericRecord::getId)
                                .setter(org.example.tests.model.inheritance.stat.GenericRecord::setId)
                                .tags(primaryPartitionKey()))
                        .addAttribute(String.class, a -> a.name("created_date")
                                .getter(org.example.tests.model.inheritance.stat.GenericRecord::getCreatedDate)
                                .setter(org.example.tests.model.inheritance.stat.GenericRecord::setCreatedDate))
                        .build();

        StaticTableSchema<org.example.tests.model.inheritance.stat.Customer> CUSTOMER_SCHEMA =
                StaticTableSchema.builder(org.example.tests.model.inheritance.stat.Customer.class)
                        .newItemSupplier(org.example.tests.model.inheritance.stat.Customer::new)
                        .addAttribute(String.class, a -> a.name("name")
                                .getter(org.example.tests.model.inheritance.stat.Customer::getName)
                                .setter(org.example.tests.model.inheritance.stat.Customer::setName))
                        // 1. Use the extend() method to collapse the parent attributes onto the child class.
                        .extend(GENERIC_RECORD_SCHEMA)     // All the attributes of the GenericRecord schema are added to Customer.
                        .build();
```

El ejemplo de esquema estático anterior utiliza las siguientes clases de datos. Dado que la asignación se define cuando se construye el esquema estático de la tabla, las clases de datos no requieren anotaciones.

#### Clases de datos
<a name="gunk"></a>

------
#### [ Standard data class ]

```
public class Customer extends GenericRecord {
    private String name;

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


public abstract class GenericRecord {
    private String id;
    private String createdDate;

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

    public String getCreatedDate() { return createdDate; }
    public void setCreatedDate(String createdDate) { this.createdDate = createdDate; }
```

------
#### [ Lombok ]

```
@Data
@ToString(callSuper = true)
public class Customer extends GenericRecord{
    private String name;
}

@Data
public abstract class GenericRecord {
    private String id;
    private String createdDate;
}
```

------

## Utilizar composiciones
<a name="ddb-en-client-adv-features-flatmap-comp"></a>

Si sus clases utilizan composiciones, siga los siguientes planteamientos para aplanar la jerarquía.

### Utilizar objetos bean anotados
<a name="ddb-en-client-adv-features-flatmap-comp-anno"></a>

La anotación `@DynamoDbFlatten` aplana la clase contenida.

Los siguientes ejemplos de clases de datos utilizan la anotación `@DynamoDbFlatten` para añadir de forma eficaz todos los atributos de la clase `GenericRecord` contenida a la clase `Customer`.

------
#### [ Standard data class ]

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

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

    @DynamoDbFlatten
    public GenericRecord getRecord() { return this.record; }
    public void setRecord(GenericRecord record) { this.record = record; }

@DynamoDbBean
public class GenericRecord {
    private String id;
    private String createdDate;

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

    public String getCreatedDate() { return this.createdDate; }
    public void setCreatedDate(String createdDate) { this.createdDate = createdDate; }
}
```

------
#### [ Lombok ]

```
@Data
@DynamoDbBean
public class Customer {
    private String name;
    @Getter(onMethod_=@DynamoDbFlatten)
    private GenericRecord record;
}

@Data
@DynamoDbBean
public class GenericRecord {
    @Getter(onMethod_=@DynamoDbPartitionKey)
    private String id;
    private String createdDate;
}
```

------

Puede utilizar la anotación aplanar para aplanar tantas clases elegibles diferentes como necesite. Existen las siguientes limitaciones:
+ Después de ser aplanados, todos los nombres de atributos deben ser únicos.
+ Nunca debe haber más de una clave de partición, clave de clasificación o nombre de tabla.

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

Cuando construya un esquema de tabla estático, utilice el método `flatten()` del constructor. También debe suministrar los métodos getter y setter que identifican la clase contenida.

```
        StaticTableSchema<GenericRecord> GENERIC_RECORD_SCHEMA =
                StaticTableSchema.builder(GenericRecord.class)
                        .newItemSupplier(GenericRecord::new)
                        .addAttribute(String.class, a -> a.name("id")
                                .getter(GenericRecord::getId)
                                .setter(GenericRecord::setId)
                                .tags(primaryPartitionKey()))
                        .addAttribute(String.class, a -> a.name("created_date")
                                .getter(GenericRecord::getCreatedDate)
                                .setter(GenericRecord::setCreatedDate))
                        .build();

        StaticTableSchema<Customer> CUSTOMER_SCHEMA =
                StaticTableSchema.builder(Customer.class)
                        .newItemSupplier(Customer::new)
                        .addAttribute(String.class, a -> a.name("name")
                                .getter(Customer::getName)
                                .setter(Customer::setName))
                        // Because we are flattening a component object, we supply a getter and setter so the
                        // mapper knows how to access it.
                        .flatten(GENERIC_RECORD_SCHEMA, Customer::getRecord, Customer::setRecord)
                        .build();
```

El ejemplo de esquema estático anterior utiliza las siguientes clases de datos.

#### Clases de datos
<a name="ddb-en-client-adv-features-flatmap-comp-static-supporting"></a>

------
#### [ Standard data class ]

```
public class Customer {
    private String name;
    private GenericRecord record;

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

    public GenericRecord getRecord() { return this.record; }
    public void setRecord(GenericRecord record) { this.record = record; }

public class GenericRecord {
    private String id;
    private String createdDate;

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

    public String getCreatedDate() { return this.createdDate; }
    public void setCreatedDate(String createdDate) { this.createdDate = createdDate; }
}
```

------
#### [ Lombok ]

```
@Data
public class Customer {
    private String name;
    private GenericRecord record;
}

@Data
public class GenericRecord {
    private String id;
    private String createdDate;
}
```

------

Puede utilizar el patrón constructor para aplanar tantas clases elegibles diferentes como necesite.

## Implicaciones para otro código
<a name="ddb-en-client-adv-features-flatmap-compare"></a>

Cuando se utiliza el atributo `@DynamoDbFlatten` (o el método constructor `flatten()`), el elemento en DynamoDB contiene un atributo para cada atributo del objeto compuesto. También incluye los atributos del objeto que lo compone. 

Por el contrario, si anota una clase de datos con una clase compuesta y no usa `@DynamoDbFlatten`, el elemento se guarda con el objeto compuesto como un atributo único.

Por ejemplo, compare la clase `Customer` que se muestra en el [ejemplo de aplanamiento con composición](#ddb-en-client-adv-features-flatmap-comp-anno) con y sin aplanamiento del atributo `record`. Puede visualizar la diferencia con JSON según muestra la siguiente tabla.


****  

| Con aplanamiento | Con aplanamiento | 
| --- | --- | 
| 3 atributos | 2 atributos | 
|  <pre>{<br />  "id": "1",<br />  "createdDate": "today",<br />  "name": "my name"<br />}</pre>  |  <pre>{<br />  "id": "1",<br />  "record": {<br />      "createdDate": "today",<br />      "name": "my name"<br />  }<br />}</pre>  | 

La diferencia es importante si hay otro código que accede a la tabla de DynamoDB y espera encontrar determinados atributos.

# Uso de atributos que son beans, mapas, listas y conjuntos
<a name="ddb-en-client-adv-features-nested"></a>

Una definición de bean, como la clase `Person` que se muestra a continuación, podría establecer propiedades (o atributos) que hagan referencia a tipos con atributos adicionales. Por ejemplo, en la clase `Person`, `mainAddress` es una propiedad que hace referencia a un bean `Address` que define atributos de valor adicionales. `addresses` hace referencia a un mapa de Java, cuyos elementos hacen referencia a beans `Address`. Estos tipos complejos pueden considerarse contenedores de atributos simples que se utilizan como valor de datos en el contexto de DynamoDB. 

En DynamoDB se denominan *atributos anidados* a las propiedades de valor de elementos anidados, como mapas, listas o beans. En la [Guía para desarrolladores de Amazon DynamoDB](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.NamingRulesDataTypes.html#HowItWorks.DataTypes) se denomina *tipo de documento* al formato guardado de un mapa de Java, lista o bean. Los atributos simples que se utilizan por su valor de datos en Java se denominan *tipos escalares* en DynamoDB. Los conjuntos que contienen varios elementos escalares del mismo tipo se denominan *tipos de conjuntos*. 

Es importante saber que la API DynamoDB Enhanced Client convierte una propiedad que es bean en un tipo de documento de mapa de DynamoDB al guardarla.

## Clase `Person`
<a name="ddb-en-client-adv-features-nested-person"></a>

```
@DynamoDbBean
public class Person {
    private Integer id;
    private String firstName;
    private String lastName;
    private Integer age;
    private Address mainAddress;
    private Map<String, Address> addresses;
    private List<PhoneNumber> phoneNumbers;
    private Set<String> hobbies;

    @DynamoDbPartitionKey
    public Integer getId() {
        return id;
    }

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

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Address getMainAddress() {
        return mainAddress;
    }

    public void setMainAddress(Address mainAddress) {
        this.mainAddress = mainAddress;
    }

    public Map<String, Address> getAddresses() {
        return addresses;
    }

    public void setAddresses(Map<String, Address> addresses) {
        this.addresses = addresses;
    }

    public List<PhoneNumber> getPhoneNumbers() {
        return phoneNumbers;
    }

    public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) {
        this.phoneNumbers = phoneNumbers;
    }

    public Set<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(Set<String> hobbies) {
        this.hobbies = hobbies;
    }

    @Override
    public String toString() {
        return "Person{" +
               "addresses=" + addresses +
               ", id=" + id +
               ", firstName='" + firstName + '\'' +
               ", lastName='" + lastName + '\'' +
               ", age=" + age +
               ", mainAddress=" + mainAddress +
               ", phoneNumbers=" + phoneNumbers +
               ", hobbies=" + hobbies +
               '}';
    }
}
```

## Clase `Address`
<a name="ddb-en-client-adv-features-nested-address"></a>

```
@DynamoDbBean
public class Address {
    private String street;
    private String city;
    private String state;
    private String zipCode;

    public Address() {
    }

    public String getStreet() {
        return this.street;
    }

    public String getCity() {
        return this.city;
    }

    public String getState() {
        return this.state;
    }

    public String getZipCode() {
        return this.zipCode;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public void setState(String state) {
        this.state = state;
    }

    public void setZipCode(String zipCode) {
        this.zipCode = zipCode;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Address address = (Address) o;
        return Objects.equals(street, address.street) && Objects.equals(city, address.city) && Objects.equals(state, address.state) && Objects.equals(zipCode, address.zipCode);
    }

    @Override
    public int hashCode() {
        return Objects.hash(street, city, state, zipCode);
    }

    @Override
    public String toString() {
        return "Address{" +
                "street='" + street + '\'' +
                ", city='" + city + '\'' +
                ", state='" + state + '\'' +
                ", zipCode='" + zipCode + '\'' +
                '}';
    }
}
```

## Clase `PhoneNumber`
<a name="ddb-en-client-adv-features-nested-phonenumber"></a>

```
@DynamoDbBean
public class PhoneNumber {
    String type;
    String number;

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    @Override
    public String toString() {
        return "PhoneNumber{" +
                "type='" + type + '\'' +
                ", number='" + number + '\'' +
                '}';
    }
}
```

## Almacenamiento de tipos complejos
<a name="ddb-en-client-adv-features-nested-mapping"></a>

### Uso de clases de datos anotadas
<a name="ddb-en-client-adv-features-nested-map-anno"></a>

Si desea guardar atributos anidados para clases personalizadas, puede anotarlos simplemente. La clase `Address` y la clase `PhoneNumber` mostradas anteriormente se anotan solo con la anotación `@DynamoDbBean`. Cuando la API del cliente mejorado de DynamoDB construye el esquema de tabla para la clase `Person` con el siguiente fragmento, la API descubre el uso de las clases `Address` y `PhoneNumber`, y construye las asignaciones correspondientes para trabajar con DynamoDB.

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

### Uso de esquemas abstractos con generadores
<a name="ddb-en-client-adv-features-nested-map-builder"></a>

El método alternativo consiste en utilizar compiladores de esquemas de tablas estáticas para cada clase de bean anidada, como se muestra en el código siguiente.

Los esquemas de tabla de las clases `Address` y `PhoneNumber` y son abstractos en el sentido de que no se pueden usar con una tabla de DynamoDB. Esto se debe a que carecen de definiciones para la clave principal. Sin embargo, se utilizan como esquemas anidados en el esquema de tabla de la clase `Person`.

Tras las líneas de comentario 1 y 2 de la definición de `PERSON_TABLE_SCHEMA`, verá el código que utiliza los esquemas de tabla abstractos. El uso de `documentOf` en el método `EnhanceType.documentOf(...)` no indica que este devuelva un tipo `EnhancedDocument` de la API de documentos mejorados. En este contexto, el método `documentOf(...)` devuelve un objeto que sabe cómo asignar su argumento de clase a y desde los atributos de la tabla de DynamoDB mediante el argumento del esquema de la tabla.

#### Código de esquema estático
<a name="ddb-en-client-adv-features-nested-map-builder-code"></a>

```
    // Abstract table schema that cannot be used to work with a DynamoDB table,
    // but can be used as a nested schema.
    public static final TableSchema<Address> TABLE_SCHEMA_ADDRESS = TableSchema.builder(Address.class)
        .newItemSupplier(Address::new)
        .addAttribute(String.class, a -> a.name("street")
            .getter(Address::getStreet)
            .setter(Address::setStreet))
        .addAttribute(String.class, a -> a.name("city")
            .getter(Address::getCity)
            .setter(Address::setCity))
        .addAttribute(String.class, a -> a.name("zipcode")
            .getter(Address::getZipCode)
            .setter(Address::setZipCode))
        .addAttribute(String.class, a -> a.name("state")
            .getter(Address::getState)
            .setter(Address::setState))
        .build();

    // Abstract table schema that cannot be used to work with a DynamoDB table,
    // but can be used as a nested schema.
    public static final TableSchema<PhoneNumber> TABLE_SCHEMA_PHONENUMBER = TableSchema.builder(PhoneNumber.class)
        .newItemSupplier(PhoneNumber::new)
        .addAttribute(String.class, a -> a.name("type")
            .getter(PhoneNumber::getType)
            .setter(PhoneNumber::setType))
        .addAttribute(String.class, a -> a.name("number")
            .getter(PhoneNumber::getNumber)
            .setter(PhoneNumber::setNumber))
        .build();

    // A static table schema that can be used with a DynamoDB table.
    // The table schema contains two nested schemas that are used to perform mapping to/from DynamoDB.
    public static final TableSchema<Person> PERSON_TABLE_SCHEMA =
        TableSchema.builder(Person.class)
            .newItemSupplier(Person::new)
            .addAttribute(Integer.class, a -> a.name("id")
                .getter(Person::getId)
                .setter(Person::setId)
                .addTag(StaticAttributeTags.primaryPartitionKey()))
            .addAttribute(String.class, a -> a.name("firstName")
                .getter(Person::getFirstName)
                .setter(Person::setFirstName))
            .addAttribute(String.class, a -> a.name("lastName")
                .getter(Person::getLastName)
                .setter(Person::setLastName))
            .addAttribute(Integer.class, a -> a.name("age")
                .getter(Person::getAge)
                .setter(Person::setAge))
            .addAttribute(EnhancedType.documentOf(Address.class, TABLE_SCHEMA_ADDRESS), a -> a.name("mainAddress")
                .getter(Person::getMainAddress)
                .setter(Person::setMainAddress))
            .addAttribute(EnhancedType.listOf(String.class), a -> a.name("hobbies")
                .getter(Person::getHobbies)
                .setter(Person::setHobbies))
            .addAttribute(EnhancedType.mapOf(
                EnhancedType.of(String.class),
                // 1. Use mapping functionality of the Address table schema.
                EnhancedType.documentOf(Address.class, TABLE_SCHEMA_ADDRESS)), a -> a.name("addresses")
                .getter(Person::getAddresses)
                .setter(Person::setAddresses))
            .addAttribute(EnhancedType.listOf(
                // 2. Use mapping functionality of the PhoneNumber table schema.
                EnhancedType.documentOf(PhoneNumber.class, TABLE_SCHEMA_PHONENUMBER)), a -> a.name("phoneNumbers")
                .getter(Person::getPhoneNumbers)
                .setter(Person::setPhoneNumbers))
            .build();
```

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

Para los métodos `query()` y `scan()`, puede especificar qué atributos desea que se devuelvan en los resultados mediante llamadas a métodos como `addNestedAttributeToProject()` y `attributesToProject()`. La API del cliente mejorada de DynamoDB convierte los parámetros de llamada al método Java [en expresiones de proyección](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.ProjectionExpressions.html) antes de enviar la solicitud.

En el siguiente ejemplo, se rellena la tabla `Person` con dos elementos y, a continuación, se efectúan tres operaciones de análisis. 

El primer análisis accede a todos los elementos de la tabla para comparar los resultados con las demás operaciones de análisis. 

El segundo análisis aplica el método del constructor [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/ScanEnhancedRequest.Builder.html#addNestedAttributeToProject(software.amazon.awssdk.enhanced.dynamodb.NestedAttributeName)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/ScanEnhancedRequest.Builder.html#addNestedAttributeToProject(software.amazon.awssdk.enhanced.dynamodb.NestedAttributeName)) para devolver solo el valor del atributo `street`.

La tercera operación de análisis sigue el método del constructor [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/ScanEnhancedRequest.Builder.html#attributesToProject(java.lang.String...)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/ScanEnhancedRequest.Builder.html#attributesToProject(java.lang.String...)) para devolver los datos del atributo de primer nivel, `hobbies`. El tipo de atributo `hobbies` es una lista Para acceder a cada elemento de la lista, haga una operación `get()` en la lista.

```
        personDynamoDbTable = getDynamoDbEnhancedClient().table("Person", PERSON_TABLE_SCHEMA);
        PersonUtils.createPersonTable(personDynamoDbTable, getDynamoDbClient());
        // Use a utility class to add items to the Person table.
        List<Person> personList = PersonUtils.getItemsForCount(2);
        // This utility method performs a put against DynamoDB to save the instances in the list argument.
        PersonUtils.putCollection(getDynamoDbEnhancedClient(), personList, personDynamoDbTable);

        // The first scan logs all items in the table to compare to the results of the subsequent scans.
        final PageIterable<Person> allItems = personDynamoDbTable.scan();
        allItems.items().forEach(p ->
                // 1. Log what is in the table.
                logger.info(p.toString()));

        // Scan for nested attributes.
        PageIterable<Person> streetScanResult = personDynamoDbTable.scan(b -> b
                // Use the 'addNestedAttributeToProject()' or 'addNestedAttributesToProject()' to access data nested in maps in DynamoDB.
                .addNestedAttributeToProject(
                        NestedAttributeName.create("addresses", "work", "street")
                ));

        streetScanResult.items().forEach(p ->
                //2. Log the results of requesting nested attributes.
                logger.info(p.toString()));

        // Scan for a top-level list attribute.
        PageIterable<Person> hobbiesScanResult = personDynamoDbTable.scan(b -> b
                // Use the 'attributesToProject()' method to access first-level attributes.
                .attributesToProject("hobbies"));

        hobbiesScanResult.items().forEach((p) -> {
            // 3. Log the results of the request for the 'hobbies' attribute.
            logger.info(p.toString());
            // To access an item in a list, first get the parent attribute, 'hobbies', then access items in the list.
            String hobby = p.getHobbies().get(1);
            // 4. Log an item in the list.
            logger.info(hobby);
        });
```

```
// Logged results from comment line 1.
Person{id=2, firstName='first name 2', lastName='last name 2', age=11, addresses={work=Address{street='street 21', city='city 21', state='state 21', zipCode='33333'}, home=Address{street='street 2', city='city 2', state='state 2', zipCode='22222'}}, phoneNumbers=[PhoneNumber{type='home', number='222-222-2222'}, PhoneNumber{type='work', number='333-333-3333'}], hobbies=[hobby 2, hobby 21]}
Person{id=1, firstName='first name 1', lastName='last name 1', age=11, addresses={work=Address{street='street 11', city='city 11', state='state 11', zipCode='22222'}, home=Address{street='street 1', city='city 1', state='state 1', zipCode='11111'}}, phoneNumbers=[PhoneNumber{type='home', number='111-111-1111'}, PhoneNumber{type='work', number='222-222-2222'}], hobbies=[hobby 1, hobby 11]}

// Logged results from comment line 2.
Person{id=null, firstName='null', lastName='null', age=null, addresses={work=Address{street='street 21', city='null', state='null', zipCode='null'}}, phoneNumbers=null, hobbies=null}
Person{id=null, firstName='null', lastName='null', age=null, addresses={work=Address{street='street 11', city='null', state='null', zipCode='null'}}, phoneNumbers=null, hobbies=null}

// Logged results from comment lines 3 and 4.
Person{id=null, firstName='null', lastName='null', age=null, addresses=null, phoneNumbers=null, hobbies=[hobby 2, hobby 21]}
hobby 21
Person{id=null, firstName='null', lastName='null', age=null, addresses=null, phoneNumbers=null, hobbies=[hobby 1, hobby 11]}
hobby 11
```

**nota**  
Si el método `attributesToProject()` sigue cualquier otro procedimiento de creación que añada los atributos que desee proyectar, la lista de nombres de atributos proporcionada a `attributesToProject()` sustituirá a todos los demás nombres de atributos.  
Un análisis efectuado con la instancia `ScanEnhancedRequest` del fragmento siguiente devuelve solo datos de hobbies.  

```
ScanEnhancedRequest lastOverwrites = ScanEnhancedRequest.builder()
        .addNestedAttributeToProject(
                NestedAttributeName.create("addresses", "work", "street"))
        .addAttributeToProject("firstName")
        // If the 'attributesToProject()' method follows other builder methods that add attributes for projection,
        // its list of attributes replace all previous attributes.
        .attributesToProject("hobbies")
        .build();
PageIterable<Person> hobbiesOnlyResult = personDynamoDbTable.scan(lastOverwrites);
hobbiesOnlyResult.items().forEach(p ->
        logger.info(p.toString()));

// Logged results.
Person{id=null, firstName='null', lastName='null', age=null, addresses=null, phoneNumbers=null, hobbies=[hobby 2, hobby 21]}
Person{id=null, firstName='null', lastName='null', age=null, addresses=null, phoneNumbers=null, hobbies=[hobby 1, hobby 11]}
```
El siguiente fragmento de código usa primero el método `attributesToProject()`. Este orden preserva todos los demás atributos solicitados.  

```
ScanEnhancedRequest attributesPreserved = ScanEnhancedRequest.builder()
        // Use 'attributesToProject()' first so that the method call does not replace all other attributes
        // that you want to project.
        .attributesToProject("firstName")
        .addNestedAttributeToProject(
                NestedAttributeName.create("addresses", "work", "street"))
        .addAttributeToProject("hobbies")
        .build();
PageIterable<Person> allAttributesResult = personDynamoDbTable.scan(attributesPreserved);
allAttributesResult.items().forEach(p ->
        logger.info(p.toString()));

// Logged results.
Person{id=null, firstName='first name 2', lastName='null', age=null, addresses={work=Address{street='street 21', city='null', state='null', zipCode='null'}}, phoneNumbers=null, hobbies=[hobby 2, hobby 21]}
Person{id=null, firstName='first name 1', lastName='null', age=null, addresses={work=Address{street='street 11', city='null', state='null', zipCode='null'}}, phoneNumbers=null, hobbies=[hobby 1, hobby 11]}
```

## Uso de tipos complejos en expresiones
<a name="ddb-en-client-adv-features-nested-expressions"></a>

Puede usar tipos complejos en expresiones, como expresiones de filtro y expresiones de condición, mediante operadores de cancelación de referencias para navegar por la estructura del tipo complejo. Para objetos y mapas, utilice `. (dot)` y para elementos de lista, utilice `[n]` (corchetes en el número de secuencia del elemento). No puede hacer referencia a elementos individuales de un conjunto, pero puede utilizar la [función `contains`](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html#Expressions.OperatorsAndFunctions.Functions).

El siguiente ejemplo muestra dos expresiones de filtro que se utilizan en operaciones de análisis. Las expresiones de filtro especifican las condiciones de coincidencia de los elementos que desea incluir en los resultados. En el ejemplo se utilizan las clases `Person`, `Address` y `PhoneNumber` mostradas anteriormente.

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

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

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


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

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

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

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

### Método auxiliar que rellena la tabla
<a name="nested-expressions-helper-method"></a>

```
    public static void populateDatabase() {
        Person person1 = new Person();
        person1.setId(1);
        person1.setFirstName("FirstName1");
        person1.setLastName("LastName1");

        Address billingAddr1 = new Address();
        billingAddr1.setState("BS1");
        billingAddr1.setCity("BillingTown1");

        Address mailing1 = new Address();
        mailing1.setState("MS1");
        mailing1.setCity("MailingTown1");

        person1.setAddresses(Map.of("billing", billingAddr1, "mailing", mailing1));

        PhoneNumber pn1_1 = new PhoneNumber();
        pn1_1.setType("work");
        pn1_1.setNumber("111-111-1111");

        PhoneNumber pn1_2 = new PhoneNumber();
        pn1_2.setType("home");
        pn1_2.setNumber("222-222-2222");

        List<PhoneNumber> phoneNumbers1 = List.of(pn1_1, pn1_2);
        person1.setPhoneNumbers(phoneNumbers1);

        personDynamoDbTable.putItem(person1);

        Person person2 = person1;
        person2.setId(2);
        person2.setFirstName("FirstName2");
        person2.setLastName("LastName2");

        Address billingAddress2 = billingAddr1;
        billingAddress2.setCity("BillingTown2");
        billingAddress2.setState("BS2");

        Address mailing2 = mailing1;
        mailing2.setCity("MailingTown2");
        mailing2.setState("MS2");

        person2.setAddresses(Map.of("billing", billingAddress2, "mailing", mailing2));

        PhoneNumber pn2_1 = new PhoneNumber();
        pn2_1.setType("work");
        pn2_1.setNumber("333-333-3333");

        PhoneNumber pn2_2 = new PhoneNumber();
        pn2_2.setType("cell");
        pn2_2.setNumber("444-444-4444");

        List<PhoneNumber> phoneNumbers2 = List.of(pn2_1, pn2_2);
        person2.setPhoneNumbers(phoneNumbers2);

        personDynamoDbTable.putItem(person2);
    }
```

### Representación en JSON de elementos de la base de datos
<a name="nested-attributes-expression-json-items"></a>

```
{
 "id": 1,
 "addresses": {
  "billing": {
   "city": "BillingTown1",
   "state": "BS1",
   "street": null,
   "zipCode": null
  },
  "mailing": {
   "city": "MailingTown1",
   "state": "MS1",
   "street": null,
   "zipCode": null
  }
 },
 "firstName": "FirstName1",
 "lastName": "LastName1",
 "phoneNumbers": [
  {
   "number": "111-111-1111",
   "type": "work"
  },
  {
   "number": "222-222-2222",
   "type": "home"
  }
 ]
}

{
 "id": 2,
 "addresses": {
  "billing": {
   "city": "BillingTown2",
   "state": "BS2",
   "street": null,
   "zipCode": null
  },
  "mailing": {
   "city": "MailingTown2",
   "state": "MS2",
   "street": null,
   "zipCode": null
  }
 },
 "firstName": "FirstName2",
 "lastName": "LastName2",
 "phoneNumbers": [
  {
   "number": "333-333-3333",
   "type": "work"
  },
  {
   "number": "444-444-4444",
   "type": "cell"
  }
 ]
}
```

## Actualización de elementos que contienen tipos complejos
<a name="ddb-en-client-adv-features-nested-updates"></a>

Para actualizar un elemento que contiene tipos complejos hay dos métodos básicos:
+ Método 1: recupere primero el elemento (usando `getItem`), actualice el objeto y, a continuación, llame a `DynamoDbTable#updateItem`.
+ Método 2: no recupere el elemento, pero cree una nueva instancia, establezca las propiedades que desee actualizar y envíe la instancia a `DynamoDbTable#updateItem` estableciendo el valor apropiado de [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/IgnoreNullsMode.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/IgnoreNullsMode.html). Con este método no es necesario recuperar el elemento antes de actualizarlo.

Los ejemplos que se muestran en esta sección utilizan las clases `Person`, `Address` y `PhoneNumber` mostradas anteriormente.

### Método de actualización 1: recupere y, a continuación, actualice
<a name="ddb-en-client-adv-features-nested-updates-retreive"></a>

Al utilizar este método, se asegura de que no se pierdan datos durante la actualización. La API DynamoDB Enhanced Client vuelve a crear el bean con los atributos del elemento guardado en DynamoDB, incluidos los valores de tipos complejos. A continuación, debe usar los métodos getters y los setters para actualizar el bean. La desventaja de este método es el costo en el que se incurre al recuperar primero el elemento.

El siguiente ejemplo demuestra que no se pierde ningún dato si se recupera el elemento antes de actualizarlo.

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

        Address mainAddress = new Address();
        mainAddress.setStreet("123 MyStreet");
        mainAddress.setCity("MyCity");
        mainAddress.setState("MyState");
        mainAddress.setZipCode("MyZipCode");
        person.setMainAddress(mainAddress);

        PhoneNumber homePhone = new PhoneNumber();
        homePhone.setNumber("1111111");
        homePhone.setType("HOME");
        person.setPhoneNumbers(List.of(homePhone));

        personDynamoDbTable.putItem(person);

        // Assume that we are running this code now.
        // First, retrieve the item
        Person retrievedPerson = personDynamoDbTable.getItem(Key.builder().partitionValue(1).build());

        // Make any updates.
        retrievedPerson.getMainAddress().setCity("YourCity");

        // Save the updated bean. 'updateItem' returns the bean as it appears after the update.
        Person updatedPerson = personDynamoDbTable.updateItem(retrievedPerson);

        // Verify for this example.
        Address updatedMainAddress = updatedPerson.getMainAddress();
        assert updatedMainAddress.getCity().equals("YourCity");
        assert updatedMainAddress.getState().equals("MyState"); // Unchanged.
        // The list of phone numbers remains; it was not set to null;
        assert updatedPerson.getPhoneNumbers().size() == 1;
    }
```

### Método de actualización 2: utilice una enumeración `IgnoreNullsMode` sin recuperar primero el elemento
<a name="ddb-en-client-adv-features-nested-updates-nullmode"></a>

Para actualizar un elemento en DynamoDB, puede proporcionar un objeto nuevo que solo tenga las propiedades que desee actualizar y dejar los demás valores como nulos. Con este método, debe saber cómo trata el SDK los valores nulos del objeto y cómo puede controlar su comportamiento.

Para especificar qué propiedades con valores nulos quiere que ignore el SDK, proporcione una enumeración `IgnoreNullsMode` al compilar [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/UpdateItemEnhancedRequest.Builder.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/model/UpdateItemEnhancedRequest.Builder.html). Como ejemplo de uso de uno de los valores enumerados, en el siguiente fragmento se utiliza el modo `IgnoreNullsMode.SCALAR_ONLY`.

```
// Create a new Person object to update the existing item in DynamoDB.
Person personForUpdate = new Person();
personForUpdate.setId(1);
personForUpdate.setFirstName("updatedFirstName");  // 'firstName' is a top scalar property.

Address addressForUpdate = new Address();
addressForUpdate.setCity("updatedCity");
personForUpdate.setMainAddress(addressForUpdate);

personDynamoDbTable.updateItem(r -> r
                .item(personForUpdate)
                .ignoreNullsMode(IgnoreNullsMode.SCALAR_ONLY));

/* With IgnoreNullsMode.SCALAR_ONLY provided, The SDK ignores all null properties. The SDK adds or replaces
the 'firstName' property with the provided value, "updatedFirstName". The SDK updates the 'city' value of
'mainAddress', as long as the 'mainAddress' attribute already exists in DynamoDB.

In the background, the SDK generates an update expression that it sends in the request to DynamoDB.
The following JSON object is a simplified version of what it sends. Notice that the SDK includes the paths
to 'mainAddress.city' and 'firstName' in the SET clause of the update expression. No null values in
'personForUpdate' are included.

{
  "TableName": "PersonTable",
  "Key": {
    "id": {
      "N": "1"
    }
  },
  "ReturnValues": "ALL_NEW",
  "UpdateExpression": "SET #mainAddress.#city = :mainAddress_city, #firstName = :firstName",
  "ExpressionAttributeNames": {
    "#city": "city",
    "#firstName": "firstName",
    "#mainAddress": "mainAddress"
  },
  "ExpressionAttributeValues": {
    ":firstName": {
      "S": "updatedFirstName"
    },
    ":mainAddress_city": {
      "S": "updatedCity"
    }
  }
}

Had we chosen 'IgnoreNullsMode.DEFAULT' instead of 'IgnoreNullsMode.SCALAR_ONLY', the SDK would have included
null values in the "ExpressionAttributeValues" section of the request as shown in the following snippet.

  "ExpressionAttributeValues": {
    ":mainAddress": {
      "M": {
        "zipCode": {
          "NULL": true
        },
        "city": {
          "S": "updatedCity"
        },
        "street": {
          "NULL": true
        },
        "state": {
          "NULL": true
        }
      }
    },
    ":firstName": {
      "S": "updatedFirstName"
    }
  }
*/
```

Encontrará más información sobre [expresiones de actualización](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html) en la Guía para desarrolladores de Amazon DynamoDB.

#### Descripción de las opciones de `IgnoreNullsMode`
<a name="ignore-nulls-mode-descriptions"></a>
+ `IgnoreNullsMode.SCALAR_ONLY`: use esta configuración para actualizar atributos escalares en cualquier nivel. El SDK crea una instrucción de actualización que envía solo atributos escalares no nulos a DynamoDB. El SDK ignora los atributos escalares con valores nulos de un bean o mapa y conserva el valor guardado en DynamoDB.

  Al actualizar un atributo escalar de un mapa o bean, el mapa ya debe existir en DynamoDB. Si agrega un mapa o un bean al objeto que aún no existe para el objeto en DynamoDB, se generará una excepción `DynamoDbException` con el mensaje *The document path provided in the update expression is invalid for update*. Debe usar el modo `MAPS_ONLY` para añadir un bean o un mapa a DynamoDB antes de actualizar cualquiera de sus atributos.
+ `IgnoreNullsMode.MAPS_ONLY`: utilice este valor para añadir o reemplazar propiedades que sean un bean o un mapa. El SDK reemplaza o añade cualquier mapa o bean incluido en el objeto. Se ignoran los beans o mapas que son nulos en el objeto y se conserva el mapa que existe en DynamoDB.
+ `IgnoreNullsMode.DEFAULT`: con esta configuración, el SDK nunca ignora los valores nulos. Los atributos escalares de cualquier nivel que sean nulos se actualizan a nulos. El SDK actualiza cualquier propiedad de bean, mapa, lista o conjunto con valores nulos en el objeto a nulos en DynamoDB. Cuando utilice este modo (o no proporcione un modo, ya que es el modo predeterminado), debe recuperar primero el elemento para que los valores de DynamoDB no se establezcan como nulos si ya están presentes en el objeto que se va a actualizar, a menos que su intención sea establecerlos como nulos.

En todos los modos, si proporciona a `updateItem` un objeto que tiene una lista o un conjunto no nulos, la lista o el conjunto se guardan en DynamoDB. 

#### ¿Por qué los modos?
<a name="ddb-en-client-adv-features-nested-updates-nullmodes-why"></a>

Al proporcionar un objeto con un bean o un mapa al `updateItem` método, el SDK no puede determinar si debe usar los valores de las propiedades del bean (o los valores de entrada del mapa) para actualizar el elemento o si todo bean/map debe reemplazar lo que se ha guardado en DynamoDB.

Siguiendo el ejemplo anterior, en el que se muestra primero la recuperación del elemento, intentemos actualizar el atributo `city` de `mainAddress` sin la recuperación.

```
/* The retrieval example saved the Person object with a 'mainAddress' property whose 'city' property value is "MyCity".
/* Note that we create a new Person with only the necessary information to update the city value
of the mainAddress. */
Person personForUpdate = new Person();
personForUpdate.setId(1);
// The update we want to make changes the city.
Address mainAddressForUpdate = new Address();
mainAddressForUpdate.setCity("YourCity");
personForUpdate.setMainAddress(mainAddressForUpdate);

// Lets' try the following:
Person updatedPerson = personDynamoDbTable.updateItem(personForUpdate);
/*
 Since we haven't retrieved the item, we don't know if the 'mainAddress' property
 already exists, so what update expression should the SDK generate?

A) Should it replace or add the 'mainAddress' with the provided object (setting all attributes to null other than city)
   as shown in the following simplified JSON?

      {
        "TableName": "PersonTable",
        "Key": {
          "id": {
            "N": "1"
          }
        },
        "ReturnValues": "ALL_NEW",
        "UpdateExpression": "SET #mainAddress = :mainAddress",
        "ExpressionAttributeNames": {
          "#mainAddress": "mainAddress"
        },
        "ExpressionAttributeValues": {
          ":mainAddress": {
            "M": {
              "zipCode": {
                "NULL": true
              },
              "city": {
                "S": "YourCity"
              },
              "street": {
                "NULL": true
              },
              "state": {
                "NULL": true
              }
            }
          }
        }
      }
 
B) Or should it update only the 'city' attribute of an existing 'mainAddress' as shown in the following simplified JSON?

      {
        "TableName": "PersonTable",
        "Key": {
          "id": {
            "N": "1"
          }
        },
        "ReturnValues": "ALL_NEW",
        "UpdateExpression": "SET #mainAddress.#city = :mainAddress_city",
        "ExpressionAttributeNames": {
          "#city": "city",
          "#mainAddress": "mainAddress"
        },
        "ExpressionAttributeValues": {
          ":mainAddress_city": {
            "S": "YourCity"
          }
        }
      }

However, assume that we don't know if the 'mainAddress' already exists. If it doesn't exist, the SDK would try to update 
an attribute of a non-existent map, which results in an exception.

In this particular case, we would likely select option B (SCALAR_ONLY) to retain the other values of the 'mainAddress'.
*/
```

En los dos ejemplos siguientes se muestran los usos de los valores enumerados `MAPS_ONLY` y `SCALAR_ONLY`. `MAPS_ONLY` agrega un mapa y `SCALAR_ONLY` actualiza un mapa.

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

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

        personDynamoDbTable.putItem(person);

        // Assume that we are running this code now.

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


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

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

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

        personDynamoDbTable.putItem(person);

        // Assume that we are running this code now.

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

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

Consulte la siguiente tabla para ver qué valores nulos se ignoran para cada modo. A menudo puede trabajar con `SCALAR_ONLY` o con `MAPS_ONLY`, salvo cuando trabaja con beans o mapas.


**¿Qué propiedades con valores nulos en el objeto enviado a `updateItem` ignora el SDK para cada modo?**  

| Tipo de propiedad | en modo SCALAR\$1ONLY | en modo MAPS\$1ONLY | en modo DEFAULT | 
| --- | --- | --- | --- | 
| Escalar superior | Sí | Sí | No | 
| Bean o mapa | Sí | Sí | No | 
| Valor escalar de una entrada de bean o mapa | Sí1 | No2 | No | 
| Lista o conjunto | Sí | Sí | No | 

1Asume que el mapa ya existe en DynamoDB. Cualquier valor escalar (nulo o no nulo) del bean o el mapa que proporcione en el objeto para actualización requiere que exista una ruta al valor en DynamoDB. El SDK crea una ruta al atributo mediante el operador de desreferenciación `. (dot)` antes de enviar la solicitud.

2Dado que se utiliza el modo `MAPS_ONLY` para sustituir por completo o añadir un bean o mapa, todos los valores nulos del bean o mapa se conservan en el mapa guardado en DynamoDB.

# Conserva los objetos vacíos con `@DynamoDbPreserveEmptyObject`
<a name="ddb-en-client-adv-features-empty"></a>

Si guarda un bean en Amazon DynamoDB con objetos vacíos y desea que el SDK vuelva a crear los objetos vacíos al recuperarlos, anote el getter del bean interior con `@DynamoDbPreserveEmptyObject`.

Para ilustrar cómo funciona la anotación, en el ejemplo de código se utilizan los dos beans siguientes.

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

La clase de datos siguiente contiene dos campos `InnerBean`. El método de getter, `getInnerBeanWithoutAnno()`, no está anotado con `@DynamoDbPreserveEmptyObject`. El método `getInnerBeanWithAnno()` está anotado.

```
@DynamoDbBean
public class MyBean {

    private String id;
    private String name;
    private InnerBean innerBeanWithoutAnno;
    private InnerBean innerBeanWithAnno;

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

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

    public InnerBean getInnerBeanWithoutAnno() { return innerBeanWithoutAnno; }
    public void setInnerBeanWithoutAnno(InnerBean innerBeanWithoutAnno) { this.innerBeanWithoutAnno = innerBeanWithoutAnno; }

    @DynamoDbPreserveEmptyObject
    public InnerBean getInnerBeanWithAnno() { return innerBeanWithAnno; }
    public void setInnerBeanWithAnno(InnerBean innerBeanWithAnno) { this.innerBeanWithAnno = innerBeanWithAnno; }

    @Override
    public String toString() {
        return new StringJoiner(", ", MyBean.class.getSimpleName() + "[", "]")
                .add("innerBeanWithoutAnno=" + innerBeanWithoutAnno)
                .add("innerBeanWithAnno=" + innerBeanWithAnno)
                .add("id='" + id + "'")
                .add("name='" + name + "'")
                .toString();
    }
}
```

Las instancias de la clase siguiente `InnerBean` son campos de `MyBean` y se inicializan como objetos vacíos en el código de ejemplo.

```
@DynamoDbBean
public class InnerBean {

    private String innerBeanField;

    public String getInnerBeanField() {
        return innerBeanField;
    }

    public void setInnerBeanField(String innerBeanField) {
        this.innerBeanField = innerBeanField;
    }

    @Override
    public String toString() {
        return "InnerBean{" +
                "innerBeanField='" + innerBeanField + '\'' +
                '}';
    }
}
```

El siguiente ejemplo de código guarda un objeto `MyBean` con objetos beans internos inicializados en DynamoDB y, a continuación, recupera el elemento. El resultado registrado muestra que el `innerBeanWithoutAnno` no se ha inicializado, sino que se ha creado `innerBeanWithAnno`.

```
    public MyBean preserveEmptyObjectAnnoUsingGetItemExample(DynamoDbTable<MyBean> myBeanTable) {
        // Save an item to DynamoDB.
        MyBean bean = new MyBean();
        bean.setId("1");
        bean.setInnerBeanWithoutAnno(new InnerBean());   // Instantiate the inner bean.
        bean.setInnerBeanWithAnno(new InnerBean());      // Instantiate the inner bean.
        myBeanTable.putItem(bean);

        GetItemEnhancedRequest request = GetItemEnhancedRequest.builder()
                .key(Key.builder().partitionValue("1").build())
                .build();
        MyBean myBean = myBeanTable.getItem(request);

        logger.info(myBean.toString());
        // Output 'MyBean[innerBeanWithoutAnno=null, innerBeanWithAnno=InnerBean{innerBeanField='null'}, id='1', name='null']'.

        return myBean;
    }
```

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

Puede utilizar la siguiente versión de `StaticTableSchema` de los esquemas de tabla en lugar de las anotaciones de los beans.

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

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

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

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

Puede omitir los atributos nulos de los objetos anidados al guardar un objeto de clase de datos en DynamoDB aplicando la anotación. `@DynamoDbIgnoreNulls` Por el contrario, los atributos de nivel superior con valores nulos nunca se guardan en la base de datos.

Para ilustrar cómo funciona la anotación, en el ejemplo de código se utilizan los dos beans siguientes.

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

La clase de datos siguiente contiene dos campos `InnerBean`. El método de getter, `getInnerBeanWithoutAnno()`, no está anotado. El método `getInnerBeanWithIgnoreNullsAnno()` está anotado con `@DynamoDbIgnoreNulls`.

```
@DynamoDbBean
public class MyBean {

    private String id;
    private String name;
    private InnerBean innerBeanWithoutAnno;
    private InnerBean innerBeanWithIgnoreNullsAnno;

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

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

    public InnerBean getInnerBeanWithoutAnno() { return innerBeanWithoutAnno; }
    public void setInnerBeanWithoutAnno(InnerBean innerBeanWithoutAnno) { this.innerBeanWithoutAnno = innerBeanWithoutAnno; }

    @DynamoDbIgnoreNulls
    public InnerBean getInnerBeanWithIgnoreNullsAnno() { return innerBeanWithIgnoreNullsAnno; }
    public void setInnerBeanWithIgnoreNullsAnno(InnerBean innerBeanWithAnno) { this.innerBeanWithIgnoreNullsAnno = innerBeanWithAnno; }

    @Override
    public String toString() {
        return new StringJoiner(", ", MyBean.class.getSimpleName() + "[", "]")
                .add("innerBeanWithoutAnno=" + innerBeanWithoutAnno)
                .add("innerBeanWithIgnoreNullsAnno=" + innerBeanWithIgnoreNullsAnno)
                .add("id='" + id + "'")
                .add("name='" + name + "'")
                .toString();
    }
}
```

Las instancias de la clase `InnerBean` siguiente son campos de `MyBean` y se inicializan como objetos vacíos en el código de ejemplo.

```
@DynamoDbBean
public class InnerBean {

    private String innerBeanFieldString;
    private Integer innerBeanFieldInteger;

    public String getInnerBeanFieldString() { return innerBeanFieldString; }
    public void setInnerBeanFieldString(String innerBeanFieldString) { this.innerBeanFieldString = innerBeanFieldString; }

    public Integer getInnerBeanFieldInteger() { return innerBeanFieldInteger; }
    public void setInnerBeanFieldInteger(Integer innerBeanFieldInteger) { this.innerBeanFieldInteger = innerBeanFieldInteger; }

    @Override
    public String toString() {
        return new StringJoiner(", ", InnerBean.class.getSimpleName() + "[", "]")
                .add("innerBeanFieldString='" + innerBeanFieldString + "'")
                .add("innerBeanFieldInteger=" + innerBeanFieldInteger)
                .toString();
    }
}
```

El siguiente ejemplo de código crea un objeto `InnerBean` y establece solo uno de sus dos atributos con un valor. 

```
    public void ignoreNullsAnnoUsingPutItemExample(DynamoDbTable<MyBean> myBeanTable) {
        // Create an InnerBean object and give only one attribute a value.
        InnerBean innerBeanOneAttributeSet = new InnerBean();
        innerBeanOneAttributeSet.setInnerBeanFieldInteger(200);

        // Create a MyBean instance and use the same InnerBean instance both for attributes.
        MyBean bean = new MyBean();
        bean.setId("1");
        bean.setInnerBeanWithoutAnno(innerBeanOneAttributeSet);
        bean.setInnerBeanWithIgnoreNullsAnno(innerBeanOneAttributeSet);

        Map<String, AttributeValue> itemMap = myBeanTable.tableSchema().itemToMap(bean, true);
        logger.info(itemMap.toString());
        // Log the map that is sent to the database.
        // {innerBeanWithIgnoreNullsAnno=AttributeValue(M={innerBeanFieldInteger=AttributeValue(N=200)}), id=AttributeValue(S=1), innerBeanWithoutAnno=AttributeValue(M={innerBeanFieldInteger=AttributeValue(N=200), innerBeanFieldString=AttributeValue(NUL=true)})}
        
        // Save the MyBean object to the table.
        myBeanTable.putItem(bean);
    }
```

Para visualizar los datos de bajo nivel que se envían a DynamoDB, el código registra el mapa de atributos antes de guardar el objeto `MyBean`.

La salida registrada muestra que `innerBeanWithIgnoreNullsAnno` emite un atributo,

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

La instancia `innerBeanWithoutAnno` genera dos atributos. Un atributo tiene un valor de 200 y el otro tiene un valor nulo.

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

## Representación en JSON del mapa de atributos
<a name="ddb-en-client-adv-features-ignore-null-ex2"></a>

La siguiente representación en JSON facilita la visualización de los datos guardados en DynamoDB.

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

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

Puede utilizar la siguiente versión de `StaticTableSchema` de los esquemas de tabla en lugar de las anotaciones de los beans.

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

    StaticTableSchema<InnerBean> innerBeanStaticTableSchema =
        StaticTableSchema.builder(InnerBean.class)
            .newItemSupplier(InnerBean::new)
            .addAttribute(String.class, a -> a.name("innerBeanFieldString")
                .getter(InnerBean::getInnerBeanFieldString)
                .setter(InnerBean::setInnerBeanFieldString))
            .addAttribute(Integer.class, a -> a.name("innerBeanFieldInteger")
                .getter(InnerBean::getInnerBeanFieldInteger)
                .setter(InnerBean::setInnerBeanFieldInteger))
            .build();

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

# Trabaje con documentos JSON con la API de documentos mejorada para DynamoDB
<a name="ddb-en-client-doc-api"></a>

La [API de documentos mejorada](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/package-summary.html) para AWS SDK for Java 2.x está diseñada para funcionar con datos orientados a documentos que no tienen un esquema fijo. Sin embargo, también le permite utilizar clases personalizadas para asignar atributos individuales.

 La API de documentos mejorada es la sucesora de la [API de documentos](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/document/DynamoDB.html) de la AWS SDK para Java versión 1.x.

**Contents**
+ [Introducción al uso de API de documentos mejorada](ddb-en-client-doc-api-steps.md)
  + [Crear un `DocumentTableSchema` y una `DynamoDbTable`.](ddb-en-client-doc-api-steps.md#ddb-en-client-doc-api-steps-createschema)
+ [Crear documentos mejorados](ddb-en-client-doc-api-steps-create-ed.md)
  + [Compilar a partir de una cadena JSON](ddb-en-client-doc-api-steps-create-ed.md#ddb-en-client-doc-api-steps-create-ed-fromJson)
  + [Construir a partir de elementos individuales](ddb-en-client-doc-api-steps-create-ed.md#ddb-en-client-doc-api-steps-create-ed-fromparts)
+ [Operaciones CRUD](ddb-en-client-doc-api-steps-use.md)
+ [Acceder a los atributos mejorados del documento como objetos personalizados](ddb-en-client-doc-api-convert.md)
+ [Utilizar un `EnhancedDocument` sin DynamoDB](ddb-en-client-doc-api-standalone.md)

# Introducción al uso de API de documentos mejorada
<a name="ddb-en-client-doc-api-steps"></a>

La API de documentos mejorada requiere las mismas [dependencias](ddb-en-client-getting-started.md#ddb-en-client-gs-dep) que se necesitan para la API de cliente mejorado de DynamoDB. También requiere una [instancia de `DynamoDbEnhancedClient`](ddb-en-client-getting-started-dynamodbTable.md#ddb-en-client-getting-started-dynamodbTable-eclient), como se muestra al principio de este tema.

Como la API de documentos mejorada se publicó con la versión 2.20.3 de AWS SDK for Java 2.x, necesita esa versión o una superior.

## Crear un `DocumentTableSchema` y una `DynamoDbTable`.
<a name="ddb-en-client-doc-api-steps-createschema"></a>

Para invocar comandos en una tabla de DynamoDB mediante la API de documentos mejorada, asocie la tabla a un objeto de recurso < > del lado del [DynamoDbTablecliente EnhancedDocument](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html). 

El método `table()` del cliente mejorado crea una instancia `DynamoDbTable<EnhancedDocument>` y requiere parámetros para el nombre de la tabla de DynamoDB y un `DocumentTableSchema`. 

El generador de a [DocumentTableSchema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/DocumentTableSchema.html)requiere una clave de índice principal y uno o más proveedores de convertidores de atributos. El método `AttributeConverterProvider.defaultProvider()` proporciona convertidores para los [tipos predeterminados](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/internal/converter/attribute/package-summary.html). Debe especificarse incluso si proporciona un proveedor de convertidores de atributos personalizado. Puede añadir una clave de índice secundaria opcional al generador.

El siguiente fragmento de código muestra el código que genera la representación del lado de una tabla `person` de DynamoDB que almacena objetos `EnhancedDocument` sin esquema.

```
DynamoDbTable<EnhancedDocument> documentDynamoDbTable = 
                enhancedClient.table("person",
                        TableSchema.documentSchemaBuilder()
                            // Specify the primary key attributes.
                            .addIndexPartitionKey(TableMetadata.primaryIndexName(),"id", AttributeValueType.S)
                            .addIndexSortKey(TableMetadata.primaryIndexName(), "lastName", AttributeValueType.S)
                            // Specify attribute converter providers. Minimally add the default one.
                            .attributeConverterProviders(AttributeConverterProvider.defaultProvider())
                            .build());
                                                         
// Call documentTable.createTable() if "person" does not exist in DynamoDB.
// createTable() should be called only one time.
```

A continuación, se muestra la representación JSON de un objeto `person` que se utiliza en esta sección.

### Objeto `person` JSON
<a name="ddb-en-client-doc-api-steps-createschema-obj"></a>

```
{
  "id": 1,
  "firstName": "Richard",
  "lastName": "Roe",
  "age": 25,
  "addresses":
    {
      "home": {
        "zipCode": "00000",
        "city": "Any Town",
        "state": "FL",
        "street": "123 Any Street"
      },
      "work": {
        "zipCode": "00001",
        "city": "Anywhere",
        "state": "FL",
        "street": "100 Main Street"
      }
    },
  "hobbies": [
    "Hobby 1",
    "Hobby 2"
  ],
  "phoneNumbers": [
    {
      "type": "Home",
      "number": "555-0100"
    },
    {
      "type": "Work",
      "number": "555-0119"
    }
  ]
}
```

# Crear documentos mejorados
<a name="ddb-en-client-doc-api-steps-create-ed"></a>

Un `[EnhancedDocument](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html)` representa un objeto de tipo documento que tiene una estructura compleja con atributos anidados. Un `EnhancedDocument` requiere atributos de nivel superior que coincidan con los atributos clave principales especificados para el `DocumentTableSchema`. El contenido restante es arbitrario y puede consistir en atributos de nivel superior y también en atributos profundamente anidados.

Para crear una instancia `EnhancedDocument`, utilice un generador que proporciona varias formas de añadir elementos.

## Compilar a partir de una cadena JSON
<a name="ddb-en-client-doc-api-steps-create-ed-fromJson"></a>

Con una cadena JSON, puede construir un `EnhancedDocument` en una llamada al método. El siguiente fragmento de código crea un `EnhancedDocument` a partir de una cadena JSON devuelta por el método auxiliar `jsonPerson()`. El método `jsonPerson()` devuelve la versión de cadena JSON del [objeto persona](ddb-en-client-doc-api-steps.md#ddb-en-client-doc-api-steps-createschema-obj) mostrado anteriormente.

```
EnhancedDocument document = 
        EnhancedDocument.builder()
                        .json( jsonPerson() )
                        .build());
```

## Construir a partir de elementos individuales
<a name="ddb-en-client-doc-api-steps-create-ed-fromparts"></a>

Alternativamente, puede construir una instancia `EnhancedDocument` a partir de componentes individuales utilizando métodos seguros de tipo del constructor.

En el ejemplo siguiente, se crea un documento mejorado `person` similar al que se crea a partir de la cadena JSON del ejemplo anterior.

```
        /* Define the shape of an address map whose JSON representation looks like the following.
           Use 'addressMapEnhancedType' in the following EnhancedDocument.builder() to simplify the code.
           "home": {
             "zipCode": "00000",
             "city": "Any Town",
             "state": "FL",
             "street": "123 Any Street"
           }*/
        EnhancedType<Map<String, String>> addressMapEnhancedType =
                EnhancedType.mapOf(EnhancedType.of(String.class), EnhancedType.of(String.class));


        //  Use the builder's typesafe methods to add elements to the enhanced document.
        EnhancedDocument personDocument = EnhancedDocument.builder()
                .putNumber("id", 50)
                .putString("firstName", "Shirley")
                .putString("lastName", "Rodriguez")
                .putNumber("age", 53)
                .putNull("nullAttribute")
                .putJson("phoneNumbers", phoneNumbersJSONString())
                /* Add the map of addresses whose JSON representation looks like the following.
                        {
                          "home": {
                            "zipCode": "00000",
                            "city": "Any Town",
                            "state": "FL",
                            "street": "123 Any Street"
                          }
                        } */
                .putMap("addresses", getAddresses(), EnhancedType.of(String.class), addressMapEnhancedType)
                .putList("hobbies", List.of("Theater", "Golf"), EnhancedType.of(String.class))
                .build();
```

### Métodos auxiliares
<a name="ddb-en-client-doc-api-steps-use-fromparts-helpers"></a>

```
    private static String phoneNumbersJSONString() {
        return "  [" +
                "    {" +
                "      \"type\": \"Home\"," +
                "      \"number\": \"555-0140\"" +
                "    }," +
                "    {" +
                "      \"type\": \"Work\"," +
                "      \"number\": \"555-0155\"" +
                "    }" +
                "  ]";
    }

    private static Map<String, Map<String, String>> getAddresses() {
        return Map.of(
                "home", Map.of(
                        "zipCode", "00002",
                        "city", "Any Town",
                        "state", "ME",
                        "street", "123 Any Street"));

    }
```

# Operaciones CRUD
<a name="ddb-en-client-doc-api-steps-use"></a>

Una vez definida una instancia `EnhancedDocument`, puede guardarla en una tabla de DynamoDB. El siguiente fragmento de código utiliza el [PersonDocument](ddb-en-client-doc-api-steps-create-ed.md#ddb-en-client-doc-api-steps-create-ed-fromparts) que se creó a partir de elementos individuales.

```
documentDynamoDbTable.putItem(personDocument);
```

Después de leer una instancia de documento mejorada de DynamoDB, puede extraer los valores individuales de los atributos utilizando getters como se muestra en el siguiente fragmento de código que accede a los datos guardados del `personDocument`. Como alternativa, puede extraer el contenido completo en una cadena JSON, como se muestra en la última parte del código de ejemplo.

```
        // Read the item.
        EnhancedDocument personDocFromDb = documentDynamoDbTable.getItem(Key.builder().partitionValue(50).build());

        // Access top-level attributes.
        logger.info("Name: {} {}", personDocFromDb.getString("firstName"), personDocFromDb.getString("lastName"));
        // Name: Shirley Rodriguez

        // Typesafe access of a deeply nested attribute. The addressMapEnhancedType shown previously defines the shape of an addresses map.
        Map<String, Map<String, String>> addresses = personDocFromDb.getMap("addresses", EnhancedType.of(String.class), addressMapEnhancedType);
        addresses.keySet().forEach(k -> logger.info(addresses.get(k).toString()));
        // {zipCode=00002, city=Any Town, street=123 Any Street, state=ME}

        // Alternatively, work with AttributeValue types checking along the way for deeply nested attributes.
        Map<String, AttributeValue> addressesMap = personDocFromDb.getMapOfUnknownType("addresses");
        addressesMap.keySet().forEach((String k) -> {
            logger.info("Looking at data for [{}] address", k);
            // Looking at data for [home] address
            AttributeValue value = addressesMap.get(k);
            AttributeValue cityValue = value.m().get("city");
            if (cityValue != null) {
                logger.info(cityValue.s());
                // Any Town
            }
        });

        List<AttributeValue> phoneNumbers = personDocFromDb.getListOfUnknownType("phoneNumbers");
        phoneNumbers.forEach((AttributeValue av) -> {
            if (av.hasM()) {
                AttributeValue type = av.m().get("type");
                if (type.s() != null) {
                    logger.info("Type of phone: {}", type.s());
                    // Type of phone: Home
                    // Type of phone: Work
                }
            }
        });

        String jsonPerson = personDocFromDb.toJson();
        logger.info(jsonPerson);
        // {"firstName":"Shirley","lastName":"Rodriguez","addresses":{"home":{"zipCode":"00002","city":"Any Town","street":"123 Any Street","state":"ME"}},"hobbies":["Theater","Golf"],
        //     "id":50,"nullAttribute":null,"age":53,"phoneNumbers":[{"number":"555-0140","type":"Home"},{"number":"555-0155","type":"Work"}]}
```

`EnhancedDocument`las instancias se pueden usar con cualquier método `[DynamoDbTable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbTable.html)` o [DynamoDbEnhancedClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedClient.html)en lugar de las clases de datos mapeadas.

# Acceder a los atributos mejorados del documento como objetos personalizados
<a name="ddb-en-client-doc-api-convert"></a>

Además de proporcionar una API para leer y escribir atributos con estructuras sin esquema, la API de documentos mejorada te permite convertir atributos desde y hacia instancias de clases personalizadas.

La API del cliente mejorado utiliza `AttributeConverterProvider`s y `AttributeConverter`s que se mostraron en la sección de [conversión de atributos de control](ddb-en-client-adv-features-conversion.md) como parte de la API del cliente mejorado de DynamoDB.

En el ejemplo siguiente, utilizamos a `CustomAttributeConverterProvider` con su clase `AddressConverter` anidada para convertir objetos `Address`. 

En este ejemplo se muestra que se pueden mezclar datos de clases y también datos de estructuras que se crean según sea necesario. En este ejemplo también se muestra que las clases personalizadas se pueden utilizar en cualquier nivel de una estructura anidada. Los objetos `Address` de este ejemplo son valores que se utilizan en un mapa.

```
    public static void attributeToAddressClassMappingExample(DynamoDbEnhancedClient enhancedClient, DynamoDbClient standardClient) {
        String tableName = "customer";

        // Define the DynamoDbTable for an enhanced document.
        // The schema builder provides methods for attribute converter providers and keys.
        DynamoDbTable<EnhancedDocument> documentDynamoDbTable = enhancedClient.table(tableName,
                DocumentTableSchema.builder()
                        // Add the CustomAttributeConverterProvider along with the default when you build the table schema.
                        .attributeConverterProviders(
                                List.of(
                                        new CustomAttributeConverterProvider(),
                                        AttributeConverterProvider.defaultProvider()))
                        .addIndexPartitionKey(TableMetadata.primaryIndexName(), "id", AttributeValueType.N)
                        .addIndexSortKey(TableMetadata.primaryIndexName(), "lastName", AttributeValueType.S)
                        .build());
        // Create the DynamoDB table if needed.
        documentDynamoDbTable.createTable();
        waitForTableCreation(tableName, standardClient);


        // The getAddressesForCustomMappingExample() helper method that provides 'addresses' shows the use of a custom Address class
        // rather than using a Map<String, Map<String, String> to hold the address data.
        Map<String, Address> addresses = getAddressesForCustomMappingExample();

        // Build an EnhancedDocument instance to save an item with a mix of structures defined as needed and static classes.
        EnhancedDocument personDocument = EnhancedDocument.builder()
                .putNumber("id", 50)
                .putString("firstName", "Shirley")
                .putString("lastName", "Rodriguez")
                .putNumber("age", 53)
                .putNull("nullAttribute")
                .putJson("phoneNumbers", phoneNumbersJSONString())
                // Note the use of 'EnhancedType.of(Address.class)' instead of the more generic
                // 'EnhancedType.mapOf(EnhancedType.of(String.class), EnhancedType.of(String.class))' that was used in a previous example.
                .putMap("addresses", addresses, EnhancedType.of(String.class), EnhancedType.of(Address.class))
                .putList("hobbies", List.of("Hobby 1", "Hobby 2"), EnhancedType.of(String.class))
                .build();
        // Save the item to DynamoDB.
        documentDynamoDbTable.putItem(personDocument);

        // Retrieve the item just saved.
        EnhancedDocument srPerson = documentDynamoDbTable.getItem(Key.builder().partitionValue(50).sortValue("Rodriguez").build());

        // Access the addresses attribute.
        Map<String, Address> srAddresses = srPerson.get("addresses",
                EnhancedType.mapOf(EnhancedType.of(String.class), EnhancedType.of(Address.class)));

        srAddresses.keySet().forEach(k -> logger.info(addresses.get(k).toString()));

        documentDynamoDbTable.deleteTable();

// The content logged to the console shows that the saved maps were converted to Address instances.
Address{street='123 Main Street', city='Any Town', state='NC', zipCode='00000'}
Address{street='100 Any Street', city='Any Town', state='NC', zipCode='00000'}
```

## `CustomAttributeConverterProvider` code
<a name="ddb-en-client-doc-api-convert-provider"></a>

```
public class CustomAttributeConverterProvider implements AttributeConverterProvider {

    private final Map<EnhancedType<?>, AttributeConverter<?>> converterCache = ImmutableMap.of(
            // 1. Add AddressConverter to the internal cache.
            EnhancedType.of(Address.class), new AddressConverter());

    public static CustomAttributeConverterProvider create() {
        return new CustomAttributeConverterProvider();
    }

    // 2. The enhanced client queries the provider for attribute converters if it
    //    encounters a type that it does not know how to convert.
    @SuppressWarnings("unchecked")
    @Override
    public <T> AttributeConverter<T> converterFor(EnhancedType<T> enhancedType) {
        return (AttributeConverter<T>) converterCache.get(enhancedType);
    }

    // 3. Custom attribute converter
    private class AddressConverter implements AttributeConverter<Address> {
        // 4. Transform an Address object into a DynamoDB map.
        @Override
        public AttributeValue transformFrom(Address address) {

            Map<String, AttributeValue> attributeValueMap = Map.of(
                    "street", AttributeValue.fromS(address.getStreet()),
                    "city", AttributeValue.fromS(address.getCity()),
                    "state", AttributeValue.fromS(address.getState()),
                    "zipCode", AttributeValue.fromS(address.getZipCode()));

            return AttributeValue.fromM(attributeValueMap);
        }

        // 5. Transform the DynamoDB map attribute to an Address oject.
        @Override
        public Address transformTo(AttributeValue attributeValue) {
            Map<String, AttributeValue> m = attributeValue.m();
            Address address = new Address();
            address.setStreet(m.get("street").s());
            address.setCity(m.get("city").s());
            address.setState(m.get("state").s());
            address.setZipCode(m.get("zipCode").s());

            return address;
        }

        @Override
        public EnhancedType<Address> type() {
            return EnhancedType.of(Address.class);
        }

        @Override
        public AttributeValueType attributeValueType() {
            return AttributeValueType.M;
        }
    }
}
```

## Clase `Address`
<a name="ddb-en-client-doc-api-convert-address"></a>

```
public class Address {
                  private String street;
                  private String city;
                  private String state;
                  private String zipCode;

                  public Address() {
                  }

                  public String getStreet() {
                  return this.street;
                  }

                  public String getCity() {
                  return this.city;
                  }

                  public String getState() {
                  return this.state;
                  }

                  public String getZipCode() {
                  return this.zipCode;
                  }

                  public void setStreet(String street) {
                  this.street = street;
                  }

                  public void setCity(String city) {
                  this.city = city;
                  }

                  public void setState(String state) {
                  this.state = state;
                  }

                  public void setZipCode(String zipCode) {
                  this.zipCode = zipCode;
                  }
                  }
```

## Método auxiliar que proporciona direcciones
<a name="ddb-en-client-doc-api-convert-helper"></a>

El siguiente método auxiliar proporciona el mapa que utiliza instancias `Address` personalizadas para los valores en lugar de instancias `Map<String, String>` genéricas para los valores.

```
    private static Map<String, Address> getAddressesForCustomMappingExample() {
        Address homeAddress = new Address();
        homeAddress.setStreet("100 Any Street");
        homeAddress.setCity("Any Town");
        homeAddress.setState("NC");
        homeAddress.setZipCode("00000");

        Address workAddress = new Address();
        workAddress.setStreet("123 Main Street");
        workAddress.setCity("Any Town");
        workAddress.setState("NC");
        workAddress.setZipCode("00000");

        return Map.of("home", homeAddress,
                "work", workAddress);
    }
```

# Utilizar un `EnhancedDocument` sin DynamoDB
<a name="ddb-en-client-doc-api-standalone"></a>

Aunque normalmente se utiliza una instancia de un `EnhancedDocument` para leer y escribir elementos de DynamoDB de tipo documento, también se puede utilizar con independencia de DynamoDB. 

Puede utilizar `EnhancedDocuments` por su capacidad de convertir cadenas JSON u objetos personalizados en mapas de `AttributeValues` de bajo nivel, como se muestra en el siguiente ejemplo.

```
    public static void conversionWithoutDynamoDbExample() {
        Address address = new Address();
        address.setCity("my city");
        address.setState("my state");
        address.setStreet("my street");
        address.setZipCode("00000");

        // Build an EnhancedDocument instance for its conversion functionality alone.
        EnhancedDocument addressEnhancedDoc = EnhancedDocument.builder()
                // Important: You must specify attribute converter providers when you build an EnhancedDocument instance not used with a DynamoDB table.
                .attributeConverterProviders(new CustomAttributeConverterProvider(), DefaultAttributeConverterProvider.create())
                .put("addressDoc", address, Address.class)
                .build();

        // Convert address to a low-level item representation.
        final Map<String, AttributeValue> addressAsAttributeMap = addressEnhancedDoc.getMapOfUnknownType("addressDoc");
        logger.info("addressAsAttributeMap: {}", addressAsAttributeMap.toString());

        // Convert address to a JSON string.
        String addressAsJsonString = addressEnhancedDoc.getJson("addressDoc");
        logger.info("addressAsJsonString: {}", addressAsJsonString);
        // Convert addressEnhancedDoc back to an Address instance.
        Address addressConverted =  addressEnhancedDoc.get("addressDoc", Address.class);
        logger.info("addressConverted: {}", addressConverted.toString());
    }

   /* Console output:
          addressAsAttributeMap: {zipCode=AttributeValue(S=00000), state=AttributeValue(S=my state), street=AttributeValue(S=my street), city=AttributeValue(S=my city)}
          addressAsJsonString: {"zipCode":"00000","state":"my state","street":"my street","city":"my city"}
          addressConverted: Address{street='my street', city='my city', state='my state', zipCode='00000'}
   */
```

**nota**  
Cuando utilice un documento mejorado independiente de una tabla de DynamoDB, asegúrese de establecer explícitamente los proveedores de convertidores de atributos en el generador.  
Por el contrario, el esquema de la tabla de documentos proporciona los proveedores de conversión cuando se utiliza un documento mejorado con una tabla de DynamoDB.

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

# Utilizar la API de cliente mejorado de DynamoDB de forma asíncrona
<a name="ddb-en-client-async"></a>

Si la aplicación requiere llamadas asíncronas y sin bloqueo a DynamoDB, puede usar [DynamoDBEnhancedAsyncClient.](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/DynamoDbEnhancedAsyncClient.html) Es similar a la implementación síncrona, pero con las siguientes diferencias clave:

1. Al compilar `DynamoDbEnhancedAsyncClient`, debe proporcionar la versión asíncrona del cliente estándar, `DynamoDbAsyncClient`, como se muestra en el siguiente fragmento.

   ```
    DynamoDbEnhancedAsyncClient enhancedClient = 
        DynamoDbEnhancedAsyncClient.builder()
                                   .dynamoDbClient(dynamoDbAsyncClient)
                                   .build();
   ```

1. Los métodos que devuelven un único objeto de datos devuelven un `CompletableFuture` del resultado en lugar de solo el resultado. De este modo, la aplicación podrá hacer otras tareas sin bloquear el resultado. En el siguiente fragmento se muestra el método `getItem()` asíncrono. 

   ```
   CompletableFuture<Customer> result = customerDynamoDbTable.getItem(customer);
   // Perform other work here.
   return result.join();   // Now block and wait for the result.
   ```

1. Los métodos que devuelven listas paginadas de resultados devuelven un [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/SdkPublisher.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/SdkPublisher.html), en lugar de un [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/pagination/sync/SdkIterable.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/pagination/sync/SdkIterable.html) que el método sincrónico `DynamoDbEnhanceClient` devuelve para los mismos métodos. A continuación, su aplicación puede suscribir un controlador a ese publicador para tratar los resultados de forma asíncrona sin tener que bloquearse.

   ```
   PagePublisher<Customer> results = customerDynamoDbTable.query(r -> r.queryConditional(keyEqualTo(k -> k.partitionValue("Smith"))));
   results.subscribe(myCustomerResultsProcessor);
   // Perform other work and let the processor handle the results asynchronously.
   ```

   Para ver un ejemplo más completo de cómo trabajar con el `SdkPublisher API`, consulte el [ejemplo](ddb-en-client-use-multirecord.md#ddb-en-client-use-multirecord-scan-async) de la sección que trata sobre el método `scan()` asíncrono de esta guía.

# Anotaciones de clases de datos
<a name="ddb-en-client-anno-index"></a>

En la tabla siguiente se enumeran las anotaciones que se pueden usar en las clases de datos y se proporcionan enlaces a información y ejemplos en esta guía. La tabla está ordenada alfabéticamente en orden ascendente por nombre de anotación.


**Anotaciones de clases de datos utilizadas en esta guía**  

| Nombre de la anotación | La anotación se aplica a 1 | ¿Qué hace? | Dónde aparece en esta guía | 
| --- | --- | --- | --- | 
| DynamoDbAtomicCounter | atributo 2 | Incrementa un atributo numérico etiquetado cada vez que se escribe un registro en la base de datos. | [Introducción y discusión.](ddb-en-client-extensions.md#ddb-en-client-extensions-ACE) | 
| DynamoDbAttribute | atributo | Define o cambia el nombre de una propiedad de bean que está asignada a un atributo de tabla de DynamoDB. |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbAutoGeneratedTimestampAttribute | atributo | Actualiza un atributo etiquetado con una marca de tiempo actual cada vez que el elemento se escribe correctamente en la base de datos. | [Introducción y discusión](ddb-en-client-extensions.md#ddb-en-client-extensions-AGTE). | 
| DynamoDbAutoGeneratedUuid | atributo | Genere un UUID (identificador único universal) único para un atributo cuando se escriba un registro nuevo en la base de datos. | [Introducción y discusión.](ddb-en-client-extensions.md#ddb-en-client-extensions-AGUE) | 
| DynamoDbBean | class | Marca una clase de datos como asignable a un esquema de tabla. | Primer uso en la [clase Customer](ddb-en-client-gs-tableschema.md#ddb-en-client-gs-tableschema-anno-bean-cust) en la sección Comenzar. A lo largo de la guía aparecen varios usos. | 
| DynamoDbConvertedBy | atributo | Asocia un AttributeConverter personalizado al atributo anotado. | [Discusión inicial y ejemplo.](ddb-en-client-adv-features-conversion.md#ddb-en-client-adv-features-conversion-single) | 
| DynamoDbFlatten | atributo | Aplana todos los atributos de una clase de datos de DynamoDB independiente y los agrega como atributos de nivel superior al registro que se lee y escribe en la base de datos.  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDBIgnore | atributo |  Hace que el atributo quede sin asignar.  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbIgnoreNulls | atributo | Impide guardar los atributos nulos de los objetos anidados de DynamoDB. | [Discusión y ejemplos.](ddb-en-client-adv-features-ignore-null.md) | 
| DynamoDbImmutable | class |  Marca una clase de datos inmutable como asignable a un esquema de tabla.  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbPartitionKey | atributo |  Marca un atributo como clave de partición principal (clave hash) de la tabla de DynamoDB.  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbPreserveEmptyObject | atributo |  Especifica que, si no hay datos presentes para el objeto asignado al atributo anotado, el objeto debe inicializarse con todos los campos nulos.  | [Discusión y ejemplos.](ddb-en-client-adv-features-empty.md) | 
| DynamoDbSecondaryPartitionKey | atributo |  Marca un atributo como clave de partición para un índice secundario global.  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbSecondarySortKey | atributo |  Marca un atributo como clave de clasificación opcional para un índice secundario global o local.  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbSortKey | atributo |  Marca un atributo como clave de clasificación principal opcional (clave de rango).  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)  | 
| DynamoDbUpdateBehavior | atributo |  Especifica el comportamiento cuando este atributo se actualiza como parte de una operación de «actualización», como UpdateItem.  | [Introducción y ejemplo.](ddb-en-client-adv-features-upd-behavior.md) | 
| DynamoDbVersionAttribute | atributo | Incrementa el número de versión de un artículo. | [Introducción y discusión.](ddb-en-client-extensions.md#ddb-en-client-extensions-VRE) | 

1Puede aplicar anotaciones de nivel de atributo al getter o setter, pero no a ambos. Esta guía muestra las anotaciones en getters

2El término `property` se utiliza normalmente para un valor encapsulado en una clase de datos JavaBean. Sin embargo, en esta guía se utiliza el término `attribute` en su lugar para mantener la coherencia con la terminología utilizada por DynamoDB.

# Trabajar con de Amazon EC2
<a name="examples-ec2"></a>

En esta sección se proporcionan ejemplos de programación de [Amazon EC2](https://docs.aws.amazon.com/ec2/) que usan el AWS SDK para Java 2.x.

**Topics**
+ [Administrar Amazon EC2 instancias](examples-ec2-instances.md)
+ [Zonas de uso Regiones de AWS y disponibilidad](examples-ec2-regions-zones.md)
+ [Trabajar con grupos de seguridad en Amazon EC2](examples-ec2-security-groups.md)
+ [Trabajar con metadatos de la instancia de Amazon EC2](examples-ec2-IMDS.md)

# Administrar Amazon EC2 instancias
<a name="examples-ec2-instances"></a>

## Creación de una instancia
<a name="create-an-instance"></a>

Cree una nueva Amazon EC2 instancia llamando al [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#runInstances(software.amazon.awssdk.services.ec2.model.RunInstancesRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#runInstances(software.amazon.awssdk.services.ec2.model.RunInstancesRequest))método de [Ec2Client](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html) y proporcionándole la [imagen de máquina [RunInstancesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/RunInstancesRequest.html)de Amazon (AMI)](https://docs.aws.amazon.com//AWSEC2/latest/UserGuide/AMIs.html) que va a utilizar y un tipo de [instancia](https://docs.aws.amazon.com//AWSEC2/latest/UserGuide/instance-types.html).

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.InstanceType;
import software.amazon.awssdk.services.ec2.model.RunInstancesRequest;
import software.amazon.awssdk.services.ec2.model.RunInstancesResponse;
import software.amazon.awssdk.services.ec2.model.Tag;
import software.amazon.awssdk.services.ec2.model.CreateTagsRequest;
import software.amazon.awssdk.services.ec2.model.Ec2Exception;
```

 **Código** 

```
   public static String createEC2Instance(Ec2Client ec2,String name, String amiId ) {

        RunInstancesRequest runRequest = RunInstancesRequest.builder()
                .imageId(amiId)
                .instanceType(InstanceType.T1_MICRO)
                .maxCount(1)
                .minCount(1)
                .build();

        RunInstancesResponse response = ec2.runInstances(runRequest);
        String instanceId = response.instances().get(0).instanceId();

        Tag tag = Tag.builder()
                .key("Name")
                .value(name)
                .build();

        CreateTagsRequest tagRequest = CreateTagsRequest.builder()
                .resources(instanceId)
                .tags(tag)
                .build();

        try {
            ec2.createTags(tagRequest);
            System.out.printf(
                    "Successfully started EC2 Instance %s based on AMI %s",
                    instanceId, amiId);

          return instanceId;

        } catch (Ec2Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }

        return "";
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/ec2/src/main/java/com/example/ec2/CreateInstance.java) en. GitHub

## Inicio de una instancia
<a name="start-an-instance"></a>

Para iniciar una Amazon EC2 instancia, llama al [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#startInstances(software.amazon.awssdk.services.ec2.model.StartInstancesRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#startInstances(software.amazon.awssdk.services.ec2.model.StartInstancesRequest))método Ec2Client e incluye el ID de [StartInstancesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/StartInstancesRequest.html)la instancia que se va a iniciar.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.StartInstancesRequest;
import software.amazon.awssdk.services.ec2.model.StopInstancesRequest;
```

 **Código** 

```
    public static void startInstance(Ec2Client ec2, String instanceId) {

        StartInstancesRequest request = StartInstancesRequest.builder()
                .instanceIds(instanceId)
                .build();

        ec2.startInstances(request);
        System.out.printf("Successfully started instance %s", instanceId);
    }
```

Consulta el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/ec2/src/main/java/com/example/ec2/StartStopInstance.java) en. GitHub

## Detener una instancia
<a name="stop-an-instance"></a>

Para detener una Amazon EC2 instancia, llama al [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#stopInstances(software.amazon.awssdk.services.ec2.model.StopInstancesRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#stopInstances(software.amazon.awssdk.services.ec2.model.StopInstancesRequest))método Ec2Client e incluye el ID de [StopInstancesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/StopInstancesRequest.html)la instancia que se va a detener.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.StartInstancesRequest;
import software.amazon.awssdk.services.ec2.model.StopInstancesRequest;
```

 **Código** 

```
    public static void stopInstance(Ec2Client ec2, String instanceId) {

        StopInstancesRequest request = StopInstancesRequest.builder()
                .instanceIds(instanceId)
                .build();

        ec2.stopInstances(request);
        System.out.printf("Successfully stopped instance %s", instanceId);
    }
```

Consulta el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/ec2/src/main/java/com/example/ec2/StartStopInstance.java) en. GitHub

## Reinicio de una instancia
<a name="reboot-an-instance"></a>

Para reiniciar una Amazon EC2 instancia, llama al [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#rebootInstances(software.amazon.awssdk.services.ec2.model.RebootInstancesRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#rebootInstances(software.amazon.awssdk.services.ec2.model.RebootInstancesRequest))método Ec2Client e incluye el ID de [RebootInstancesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/RebootInstancesRequest.html)la instancia que se va a reiniciar.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.Ec2Exception;
import software.amazon.awssdk.services.ec2.model.RebootInstancesRequest;
```

 **Código** 

```
    public static void rebootEC2Instance(Ec2Client ec2, String instanceId) {

      try {
            RebootInstancesRequest request = RebootInstancesRequest.builder()
                .instanceIds(instanceId)
                    .build();

            ec2.rebootInstances(request);
            System.out.printf(
                "Successfully rebooted instance %s", instanceId);
    } catch (Ec2Exception e) {
          System.err.println(e.awsErrorDetails().errorMessage());
          System.exit(1);
     }
  }
```

Consulta el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/ec2/src/main/java/com/example/ec2/RebootInstance.java) en. GitHub

## Descripción de instancias
<a name="describe-instances"></a>

Para enumerar sus instancias, cree un método de Ec2Client [DescribeInstancesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/DescribeInstancesRequest.html)y llámelo. [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#describeInstances(software.amazon.awssdk.services.ec2.model.DescribeInstancesRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#describeInstances(software.amazon.awssdk.services.ec2.model.DescribeInstancesRequest)) Devolverá un [DescribeInstancesResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/DescribeInstancesResponse.html)objeto que puede usar para enumerar las Amazon EC2 instancias de su cuenta y región.

Las instancias se agrupan por *reserva*. Cada reserva se corresponde con la llamada a `startInstances` que lanzó la instancia. Para mostrar sus instancias, primero debe llamar al método `reservations` de la clase `DescribeInstancesResponse` y después llamar a `instances` en cada objeto [Reservation](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/Reservation.html) devuelto.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.DescribeInstancesRequest;
import software.amazon.awssdk.services.ec2.model.DescribeInstancesResponse;
import software.amazon.awssdk.services.ec2.model.Instance;
import software.amazon.awssdk.services.ec2.model.Reservation;
import software.amazon.awssdk.services.ec2.model.Ec2Exception;
```

 **Código** 

```
    public static void describeEC2Instances( Ec2Client ec2){

        String nextToken = null;

        try {

            do {
                DescribeInstancesRequest request = DescribeInstancesRequest.builder().maxResults(6).nextToken(nextToken).build();
                DescribeInstancesResponse response = ec2.describeInstances(request);

                for (Reservation reservation : response.reservations()) {
                    for (Instance instance : reservation.instances()) {
                        System.out.println("Instance Id is " + instance.instanceId());
                        System.out.println("Image id is "+  instance.imageId());
                        System.out.println("Instance type is "+  instance.instanceType());
                        System.out.println("Instance state name is "+  instance.state().name());
                        System.out.println("monitoring information is "+  instance.monitoring().state());

                }
            }
                nextToken = response.nextToken();
            } while (nextToken != null);

        } catch (Ec2Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Los resultados se paginan; puede obtener más resultados pasando el valor devuelto del método `nextToken` del objeto resultante al método `nextToken` del nuevo objeto de la solicitud, usando el nuevo objeto de la solicitud en la siguiente llamada a `describeInstances`.

Consulta el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/ec2/src/main/java/com/example/ec2/DescribeInstances.java) en GitHub.

## Monitorear una instancia
<a name="monitor-an-instance"></a>

Puede supervisar varios aspectos de las Amazon EC2 instancias, como el uso de la CPU y la red, la memoria disponible y el espacio en disco restante. Para obtener más información sobre la supervisión de instancias, consulta la [sección Supervisión Amazon EC2](https://docs.aws.amazon.com//AWSEC2/latest/UserGuide/monitoring_ec2.html) en la Guía del Amazon EC2 usuario de instancias de Linux.

Para empezar a monitorizar una instancia, debes crear una [MonitorInstancesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/MonitorInstancesRequest.html)con el ID de la instancia que quieres monitorizar y pasarla al método de Ec2Client. [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#monitorInstances(software.amazon.awssdk.services.ec2.model.MonitorInstancesRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#monitorInstances(software.amazon.awssdk.services.ec2.model.MonitorInstancesRequest))

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.MonitorInstancesRequest;
import software.amazon.awssdk.services.ec2.model.UnmonitorInstancesRequest;
```

 **Código** 

```
    public static void monitorInstance( Ec2Client ec2, String instanceId) {

        MonitorInstancesRequest request = MonitorInstancesRequest.builder()
                .instanceIds(instanceId).build();

        ec2.monitorInstances(request);
        System.out.printf(
                "Successfully enabled monitoring for instance %s",
                instanceId);
    }
```

Consulta el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/ec2/src/main/java/com/example/ec2/MonitorInstance.java) en. GitHub

## Detener la monitorización de instancias
<a name="stop-instance-monitoring"></a>

Para detener la supervisión de una instancia, cree una [UnmonitorInstancesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/UnmonitorInstancesRequest.html)con el ID de la instancia que desee detener la supervisión y pásela al método de Ec2Client. [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#unmonitorInstances(software.amazon.awssdk.services.ec2.model.UnmonitorInstancesRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/Ec2Client.html#unmonitorInstances(software.amazon.awssdk.services.ec2.model.UnmonitorInstancesRequest))

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.MonitorInstancesRequest;
import software.amazon.awssdk.services.ec2.model.UnmonitorInstancesRequest;
```

 **Código** 

```
    public static void unmonitorInstance(Ec2Client ec2, String instanceId) {
        UnmonitorInstancesRequest request = UnmonitorInstancesRequest.builder()
                .instanceIds(instanceId).build();

        ec2.unmonitorInstances(request);

        System.out.printf(
                "Successfully disabled monitoring for instance %s",
                instanceId);
    }
```

Consulta el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/ec2/src/main/java/com/example/ec2/MonitorInstance.java) en. GitHub

## Más información
<a name="more-information"></a>
+  [RunInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_RunInstances.html)en la referencia Amazon EC2 de la API
+  [DescribeInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeInstances.html)en la referencia Amazon EC2 de la API
+  [StartInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_StartInstances.html)en la referencia Amazon EC2 de la API
+  [StopInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_StopInstances.html)en la referencia Amazon EC2 de la API
+  [RebootInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_RebootInstances.html)en la referencia Amazon EC2 de la API
+  [MonitorInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_MonitorInstances.html)en la referencia Amazon EC2 de la API
+  [UnmonitorInstances](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_UnmonitorInstances.html)en la referencia Amazon EC2 de la API

# Zonas de uso Regiones de AWS y disponibilidad
<a name="examples-ec2-regions-zones"></a>

## Descripción de regiones
<a name="describe-regions"></a>

Para mostrar las regiones disponibles para su cuenta, llame al método `describeRegions` de Ec2Client. Este método devuelve un objeto [DescribeRegionsResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/DescribeRegionsResponse.html). Llame al método `regions` del objeto devuelto para obtener una lista de objetos [Region](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/Region.html) que representan cada región.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2AsyncClient;
import software.amazon.awssdk.services.ec2.model.DescribeRegionsResponse;
import software.amazon.awssdk.services.ec2.model.DescribeAvailabilityZonesResponse;
import java.util.concurrent.CompletableFuture;
```

 **Código** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2AsyncClient;
import software.amazon.awssdk.services.ec2.model.DescribeRegionsResponse;
import software.amazon.awssdk.services.ec2.model.DescribeAvailabilityZonesResponse;
import java.util.concurrent.CompletableFuture;

/**
 * Before running this Java V2 code example, set up your development
 * environment, including your credentials.
 *
 * For more information, see the following documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 */
public class DescribeRegionsAndZones {
    public static void main(String[] args) {
        Ec2AsyncClient ec2AsyncClient = Ec2AsyncClient.builder()
            .region(Region.US_EAST_1)
            .build();

        try {
            CompletableFuture<Void> future = describeEC2RegionsAndZonesAsync(ec2AsyncClient);
            future.join(); // Wait for both async operations to complete.
        } catch (RuntimeException rte) {
            System.err.println("An exception occurred: " + (rte.getCause() != null ? rte.getCause().getMessage() : rte.getMessage()));
        }
    }

    /**
     * Asynchronously describes the EC2 regions and availability zones.
     *
     * @param ec2AsyncClient the EC2 async client used to make the API calls
     * @return a {@link CompletableFuture} that completes when both the region and availability zone descriptions are complete
     */
    public static CompletableFuture<Void> describeEC2RegionsAndZonesAsync(Ec2AsyncClient ec2AsyncClient) {
        // Initiate the asynchronous request to describe regions
        CompletableFuture<DescribeRegionsResponse> regionsResponse = ec2AsyncClient.describeRegions();

        // Handle the response or exception for regions
        CompletableFuture<DescribeRegionsResponse> regionsFuture = regionsResponse.whenComplete((regionsResp, ex) -> {
            if (ex != null) {
                // Handle the exception by throwing a RuntimeException
                throw new RuntimeException("Failed to describe EC2 regions.", ex);
            } else if (regionsResp == null || regionsResp.regions().isEmpty()) {
                // Throw an exception if the response is null or the result is empty
                throw new RuntimeException("No EC2 regions found.");
            } else {
                // Process the response if no exception occurred and the result is not empty
                regionsResp.regions().forEach(region -> {
                    System.out.printf(
                        "Found Region %s with endpoint %s%n",
                        region.regionName(),
                        region.endpoint());
                });
            }
        });

        CompletableFuture<DescribeAvailabilityZonesResponse> zonesResponse = ec2AsyncClient.describeAvailabilityZones();
        CompletableFuture<DescribeAvailabilityZonesResponse> zonesFuture = zonesResponse.whenComplete((zonesResp, ex) -> {
            if (ex != null) {
                throw new RuntimeException("Failed to describe EC2 availability zones.", ex);
            } else if (zonesResp == null || zonesResp.availabilityZones().isEmpty()) {
                throw new RuntimeException("No EC2 availability zones found.");
            } else {
                zonesResp.availabilityZones().forEach(zone -> {
                    System.out.printf(
                        "Found Availability Zone %s with status %s in region %s%n",
                        zone.zoneName(),
                        zone.state(),
                        zone.regionName()
                    );
                });
            }
        });

        return CompletableFuture.allOf(regionsFuture, zonesFuture);
    }
}
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/7486a1a092aa8e16a21698ef26f9d524fef62e55/javav2/example_code/ec2/src/main/java/com/example/ec2/DescribeRegionsAndZones.java) en. GitHub

## Describir zonas de disponibilidad
<a name="describe-availability-zones"></a>

Para mostrar las zonas de disponibilidad disponibles para su cuenta, llame al método `describeAvailabilityZones` de Ec2Client. Este método devuelve un objeto [DescribeAvailabilityZonesResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/DescribeAvailabilityZonesResponse.html). Llame a su `availabilityZones` método para obtener una lista de [AvailabilityZone](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/AvailabilityZone.html)objetos que representan cada zona de disponibilidad.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2AsyncClient;
import software.amazon.awssdk.services.ec2.model.DescribeRegionsResponse;
import software.amazon.awssdk.services.ec2.model.DescribeAvailabilityZonesResponse;
import java.util.concurrent.CompletableFuture;
```

 **Código** 

Crear el Ec2Client.

```
        Ec2AsyncClient ec2AsyncClient = Ec2AsyncClient.builder()
            .region(Region.US_EAST_1)
            .build();
```

A continuación, llame a describeAvailabilityZones () y recupere los resultados.

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2AsyncClient;
import software.amazon.awssdk.services.ec2.model.DescribeRegionsResponse;
import software.amazon.awssdk.services.ec2.model.DescribeAvailabilityZonesResponse;
import java.util.concurrent.CompletableFuture;

/**
 * Before running this Java V2 code example, set up your development
 * environment, including your credentials.
 *
 * For more information, see the following documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 */
public class DescribeRegionsAndZones {
    public static void main(String[] args) {
        Ec2AsyncClient ec2AsyncClient = Ec2AsyncClient.builder()
            .region(Region.US_EAST_1)
            .build();

        try {
            CompletableFuture<Void> future = describeEC2RegionsAndZonesAsync(ec2AsyncClient);
            future.join(); // Wait for both async operations to complete.
        } catch (RuntimeException rte) {
            System.err.println("An exception occurred: " + (rte.getCause() != null ? rte.getCause().getMessage() : rte.getMessage()));
        }
    }

    /**
     * Asynchronously describes the EC2 regions and availability zones.
     *
     * @param ec2AsyncClient the EC2 async client used to make the API calls
     * @return a {@link CompletableFuture} that completes when both the region and availability zone descriptions are complete
     */
    public static CompletableFuture<Void> describeEC2RegionsAndZonesAsync(Ec2AsyncClient ec2AsyncClient) {
        // Initiate the asynchronous request to describe regions
        CompletableFuture<DescribeRegionsResponse> regionsResponse = ec2AsyncClient.describeRegions();

        // Handle the response or exception for regions
        CompletableFuture<DescribeRegionsResponse> regionsFuture = regionsResponse.whenComplete((regionsResp, ex) -> {
            if (ex != null) {
                // Handle the exception by throwing a RuntimeException
                throw new RuntimeException("Failed to describe EC2 regions.", ex);
            } else if (regionsResp == null || regionsResp.regions().isEmpty()) {
                // Throw an exception if the response is null or the result is empty
                throw new RuntimeException("No EC2 regions found.");
            } else {
                // Process the response if no exception occurred and the result is not empty
                regionsResp.regions().forEach(region -> {
                    System.out.printf(
                        "Found Region %s with endpoint %s%n",
                        region.regionName(),
                        region.endpoint());
                });
            }
        });

        CompletableFuture<DescribeAvailabilityZonesResponse> zonesResponse = ec2AsyncClient.describeAvailabilityZones();
        CompletableFuture<DescribeAvailabilityZonesResponse> zonesFuture = zonesResponse.whenComplete((zonesResp, ex) -> {
            if (ex != null) {
                throw new RuntimeException("Failed to describe EC2 availability zones.", ex);
            } else if (zonesResp == null || zonesResp.availabilityZones().isEmpty()) {
                throw new RuntimeException("No EC2 availability zones found.");
            } else {
                zonesResp.availabilityZones().forEach(zone -> {
                    System.out.printf(
                        "Found Availability Zone %s with status %s in region %s%n",
                        zone.zoneName(),
                        zone.state(),
                        zone.regionName()
                    );
                });
            }
        });

        return CompletableFuture.allOf(regionsFuture, zonesFuture);
    }
}
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/7486a1a092aa8e16a21698ef26f9d524fef62e55/javav2/example_code/ec2/src/main/java/com/example/ec2/DescribeRegionsAndZones.java) en GitHub.

## Describir cuentas
<a name="describe-accounts"></a>

Para ver información EC2 relacionada con su cuenta, llame al método Ec2Client. `describeAccountAttributes` Este método devuelve un objeto [DescribeAccountAttributesResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/DescribeAccountAttributesResponse.html). Invoca este `accountAttributes` método de objetos para obtener una lista de objetos. [AccountAttribute](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/AccountAttribute.html) Puede recorrer en iteración la lista para recuperar un objeto `AccountAttribute`.

Puede obtener los valores de los atributos de su cuenta invocando el método `attributeValues` del objeto `AccountAttribute`. Este método devuelve una lista de [AccountAttributeValue](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/AccountAttributeValue.html)objetos. Puede recorrer en iteración esta segunda lista para mostrar el valor de los atributos (consulte el siguiente ejemplo de código).

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2AsyncClient;
import software.amazon.awssdk.services.ec2.model.DescribeAccountAttributesResponse;
import java.util.concurrent.CompletableFuture;
```

 **Código** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2AsyncClient;
import software.amazon.awssdk.services.ec2.model.DescribeAccountAttributesResponse;
import java.util.concurrent.CompletableFuture;

/**
 * Before running this Java V2 code example, set up your development
 * environment, including your credentials.
 *
 * For more information, see the following documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 */
public class DescribeAccount {
    public static void main(String[] args) {
        Ec2AsyncClient ec2AsyncClient = Ec2AsyncClient.builder()
            .region(Region.US_EAST_1)
            .build();

        try {
            CompletableFuture<DescribeAccountAttributesResponse> future = describeEC2AccountAsync(ec2AsyncClient);
            future.join();
            System.out.println("EC2 Account attributes described successfully.");
        } catch (RuntimeException rte) {
            System.err.println("An exception occurred: " + (rte.getCause() != null ? rte.getCause().getMessage() : rte.getMessage()));
        }
    }

    /**
     * Describes the EC2 account attributes asynchronously.
     *
     * @param ec2AsyncClient the EC2 asynchronous client to use for the operation
     * @return a {@link CompletableFuture} containing the {@link DescribeAccountAttributesResponse} with the account attributes
     */
    public static CompletableFuture<DescribeAccountAttributesResponse> describeEC2AccountAsync(Ec2AsyncClient ec2AsyncClient) {
        CompletableFuture<DescribeAccountAttributesResponse> response = ec2AsyncClient.describeAccountAttributes();
        return response.whenComplete((accountResults, ex) -> {
            if (ex != null) {
                // Handle the exception by throwing a RuntimeException.
                throw new RuntimeException("Failed to describe EC2 account attributes.", ex);
            } else if (accountResults == null || accountResults.accountAttributes().isEmpty()) {
                // Throw an exception if the response is null or no account attributes are found.
                throw new RuntimeException("No account attributes found.");
            } else {
                // Process the response if no exception occurred.
                accountResults.accountAttributes().forEach(attribute -> {
                    System.out.println("\nThe name of the attribute is " + attribute.attributeName());
                    attribute.attributeValues().forEach(
                        myValue -> System.out.println("The value of the attribute is " + myValue.attributeValue()));
                });
            }
        });
    }
}
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/7486a1a092aa8e16a21698ef26f9d524fef62e55/javav2/example_code/ec2/src/main/java/com/example/ec2/DescribeAccount.java) en GitHub.

## Más información
<a name="more-information"></a>
+  [Regiones y zonas de disponibilidad](https://docs.aws.amazon.com//AWSEC2/latest/UserGuide/using-regions-availability-zones.html) en la guía Amazon EC2 del usuario de instancias de Linux
+  [DescribeRegions](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeRegions.html)en la referencia Amazon EC2 de la API
+  [DescribeAvailabilityZones](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeAvailabilityZones.html)en la referencia Amazon EC2 de la API

# Trabajar con grupos de seguridad en Amazon EC2
<a name="examples-ec2-security-groups"></a>

## Creación de un grupo de seguridad
<a name="create-a-security-group"></a>

Para crear un grupo de seguridad, llame al método `createSecurityGroup` de Ec2Client con un objeto [CreateSecurityGroupRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/CreateSecurityGroupRequest.html) que contenga el nombre de la clave.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.CreateSecurityGroupRequest;
import software.amazon.awssdk.services.ec2.model.AuthorizeSecurityGroupIngressRequest;
import software.amazon.awssdk.services.ec2.model.AuthorizeSecurityGroupIngressResponse;
import software.amazon.awssdk.services.ec2.model.Ec2Exception;
import software.amazon.awssdk.services.ec2.model.IpPermission;
import software.amazon.awssdk.services.ec2.model.CreateSecurityGroupResponse;
import software.amazon.awssdk.services.ec2.model.IpRange;
```

 **Código de** 

```
            CreateSecurityGroupRequest createRequest = CreateSecurityGroupRequest.builder()
                .groupName(groupName)
                .description(groupDesc)
                .vpcId(vpcId)
                .build();

            CreateSecurityGroupResponse resp= ec2.createSecurityGroup(createRequest);
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/ec2/src/main/java/com/example/ec2/CreateSecurityGroup.java) en GitHub.

## Configuración de un grupo de seguridad
<a name="configure-a-security-group"></a>

Un grupo de seguridad puede controlar el tráfico de entrada y salida a sus instancias Amazon EC2.

Para añadir reglas de entrada al grupo de seguridad, utilice el método `authorizeSecurityGroupIngress` de Ec2Client, proporcionando el nombre del grupo de seguridad y las reglas de acceso ([IpPermission](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/IpPermission.html)) que desea asignar al grupo dentro de un objeto [AuthorizeSecurityGroupIngressRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/AuthorizeSecurityGroupIngressRequest.html). El siguiente ejemplo muestra cómo añadir permisos de IP a un grupo de seguridad.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.CreateSecurityGroupRequest;
import software.amazon.awssdk.services.ec2.model.AuthorizeSecurityGroupIngressRequest;
import software.amazon.awssdk.services.ec2.model.AuthorizeSecurityGroupIngressResponse;
import software.amazon.awssdk.services.ec2.model.Ec2Exception;
import software.amazon.awssdk.services.ec2.model.IpPermission;
import software.amazon.awssdk.services.ec2.model.CreateSecurityGroupResponse;
import software.amazon.awssdk.services.ec2.model.IpRange;
```

 **Código de** 

Primero, cree un Ec2Client

```
        Region region = Region.US_WEST_2;
        Ec2Client ec2 = Ec2Client.builder()
                .region(region)
                .build();
```

Luego, use el método `authorizeSecurityGroupIngress` de Ec2Client,

```
            IpRange ipRange = IpRange.builder()
                .cidrIp("0.0.0.0/0").build();

            IpPermission ipPerm = IpPermission.builder()
                .ipProtocol("tcp")
                .toPort(80)
                .fromPort(80)
                .ipRanges(ipRange)
                .build();

            IpPermission ipPerm2 = IpPermission.builder()
                .ipProtocol("tcp")
                .toPort(22)
                .fromPort(22)
                .ipRanges(ipRange)
                .build();

            AuthorizeSecurityGroupIngressRequest authRequest =
                AuthorizeSecurityGroupIngressRequest.builder()
                        .groupName(groupName)
                        .ipPermissions(ipPerm, ipPerm2)
                        .build();

            AuthorizeSecurityGroupIngressResponse authResponse =
            ec2.authorizeSecurityGroupIngress(authRequest);

            System.out.printf(
                "Successfully added ingress policy to Security Group %s",
                groupName);

            return resp.groupId();

        } catch (Ec2Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        return "";
    }
```

Para añadir una regla de salida al grupo de seguridad, proporcione datos similares en un objeto [AuthorizeSecurityGroupEgressRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/AuthorizeSecurityGroupEgressRequest.html) al método `authorizeSecurityGroupEgress` de Ec2Client.

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/ec2/src/main/java/com/example/ec2/CreateSecurityGroup.java) en GitHub.

## Describir grupos de seguridad
<a name="describe-security-groups"></a>

Para describir los grupos de seguridad o para obtener información sobre ellos, llame al método `describeSecurityGroups` de Ec2Client. Este método devuelve [DescribeSecurityGroupsResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/DescribeSecurityGroupsResponse.html), que puede usar para obtener acceso a la lista de grupos de seguridad llamando a su método `securityGroups`, que devuelve una lista de objetos [SecurityGroup](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/SecurityGroup.html).

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.DescribeSecurityGroupsRequest;
import software.amazon.awssdk.services.ec2.model.DescribeSecurityGroupsResponse;
import software.amazon.awssdk.services.ec2.model.SecurityGroup;
import software.amazon.awssdk.services.ec2.model.Ec2Exception;
```

 **Código de** 

```
     public static void describeEC2SecurityGroups(Ec2Client ec2, String groupId) {

        try {
            DescribeSecurityGroupsRequest request =
                DescribeSecurityGroupsRequest.builder()
                        .groupIds(groupId).build();

            DescribeSecurityGroupsResponse response =
                ec2.describeSecurityGroups(request);

             for(SecurityGroup group : response.securityGroups()) {
                System.out.printf(
                    "Found Security Group with id %s, " +
                            "vpc id %s " +
                            "and description %s",
                    group.groupId(),
                    group.vpcId(),
                    group.description());
            }
        } catch (Ec2Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/ec2/src/main/java/com/example/ec2/DescribeSecurityGroups.java) en GitHub.

## Eliminación de un grupo de seguridad
<a name="delete-a-security-group"></a>

Para eliminar un grupo de seguridad, llame al método `deleteSecurityGroup` de Ec2Client pasando un objeto [DeleteSecurityGroupRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/ec2/model/DeleteSecurityGroupRequest.html) que contenga el ID del grupo de seguridad que desea eliminar.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.ec2.Ec2Client;
import software.amazon.awssdk.services.ec2.model.DeleteSecurityGroupRequest;
import software.amazon.awssdk.services.ec2.model.Ec2Exception;
```

 **Código de** 

```
    public static void deleteEC2SecGroup(Ec2Client ec2,String groupId) {

        try {
            DeleteSecurityGroupRequest request = DeleteSecurityGroupRequest.builder()
                .groupId(groupId)
                .build();

            ec2.deleteSecurityGroup(request);
            System.out.printf(
                "Successfully deleted Security Group with id %s", groupId);

        } catch (Ec2Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
     }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/ec2/src/main/java/com/example/ec2/DeleteSecurityGroup.java) en GitHub.

## Más información
<a name="more-information"></a>
+  [Grupos de seguridad de Amazon EC2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-security-groups.html) en la Guía del usuario de Amazon EC2 para instancias de Linux
+  [Autorización del tráfico entrante para sus instancias de Linux](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/authorizing-access-to-an-instance.html) en la Guía del usuario de Amazon EC2 para instancias de Linux
+  [CreateSecurityGroup](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_CreateSecurityGroup.html) en la Referencia de la API de Amazon EC2
+  [DescribeSecurityGroups](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeSecurityGroups.html) en la Referencia de la API de Amazon EC2
+  [DeleteSecurityGroup](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DeleteSecurityGroup.html) en la Referencia de la API de Amazon EC2
+  [AuthorizeSecurityGroupIngress](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_AuthorizeSecurityGroupIngress.html) en la Referencia de la API de Amazon EC2

# Trabajar con metadatos de la instancia de Amazon EC2
<a name="examples-ec2-IMDS"></a>

Un cliente Java SDK para el servicio de metadatos de la instancia de Amazon EC2 (cliente de metadatos) permite a sus aplicaciones acceder a los metadatos de su instancia EC2 local. El cliente de metadatos funciona con la instancia local de [IMDSv2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html)(Instance Metadata Service v2) y utiliza solicitudes orientadas a la sesión. 

Hay dos clases de clientes disponibles en el SDK. El síncrono `[Ec2MetadataClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/imds/Ec2MetadataClient.html)` es para operaciones de bloqueo, y el [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/imds/Ec2MetadataAsyncClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/imds/Ec2MetadataAsyncClient.html) es para casos prácticos asíncronos, no de bloqueo. 

## Introducción
<a name="examples-ec2-IMDS-getstarted"></a>

Para usar el cliente de metadatos, agregue el artefacto Maven `imds` a su proyecto. También necesita clases para una `[SdkHttpClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/SdkHttpClient.html)` (o una `[SdkAsyncHttpClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/async/SdkAsyncHttpClient.html)` para la variante asíncrona) de la ruta de clases. 

El siguiente XML de Maven muestra fragmentos de dependencia para usar el cliente síncrono [UrlConnectionHttpClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/urlconnection/UrlConnectionHttpClient.html)junto con la dependencia para los clientes de metadatos.

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

<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>imds</artifactId>
    </dependency>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>url-connection-client</artifactId>
    </dependency>
    <!-- other dependencies --> 
</dependencies>
```

Busca en el [repositorio central de Maven](https://central.sonatype.com/artifact/software.amazon.awssdk/bom) la última versión del artefacto `bom`.

Para usar un cliente HTTP asíncrono, sustituya el fragmento de dependencia del artefacto `url-connection-client`. Por ejemplo, el siguiente fragmento incluye la implementación. [NettyNioAsyncHttpClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/nio/netty/NettyNioAsyncHttpClient.html)

```
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>netty-nio-client</artifactId>
    </dependency>
```

## Utilizar el cliente de metadatos
<a name="examples-ec2-IMDS-use"></a>

### Crear una instancia de un cliente de metadatos
<a name="examples-ec2-IMDS-use-create"></a>

Puede crear una instancia de un sistema síncrono `Ec2MetadataClient` cuando solo haya una implementación de la interfaz `SdkHttpClient` en la ruta de clases. Para ello, llame al método estático `Ec2MetadataClient#create()`, como se muestra en el siguiente fragmento de código. 

```
Ec2MetadataClient client = Ec2MetadataClient.create(); // 'Ec2MetadataAsyncClient#create' is the asynchronous version.
```

Si su aplicación tiene varias implementaciones de la interfaz `SdkHttpClient` o `SdkHttpAsyncClient`, debe especificar una implementación para que la utilice el cliente de metadatos, como se muestra en la sección [Cliente HTTP configurable](#examples-ec2-IMDS-features-http). 

**nota**  
Para la mayoría de los clientes de servicios, como Amazon S3, el SDK para Java añade automáticamente las implementaciones de la interfaz `SdkHttpClient` o `SdkHttpAsyncClient`. Si su cliente de metadatos usa la misma implementación, `Ec2MetadataClient#create()` funcionará. Si necesita una implementación diferente, debe especificarla al crear el cliente de metadatos.

### Enviar solicitudes
<a name="examples-ec2-IMDS-use-req"></a>

Para recuperar metadatos de la instancia, instancie la clase `EC2MetadataClient` y llame al método `get` con un parámetro de ruta que especifique la [categoría de metadatos de la instancia](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-categories.html).

En el siguiente ejemplo, se imprime el valor asociado a la clave `ami-id` de la consola.

```
Ec2MetadataClient client = Ec2MetadataClient.create();
Ec2MetadataResponse response = client.get("/latest/meta-data/ami-id");
System.out.println(response.asString());
client.close(); // Closes the internal resources used by the Ec2MetadataClient class.
```

Si la ruta no es válida, el método `get` genera una excepción. 

Puede reutilizar la misma instancia de cliente para varias solicitudes, pero ejecute `close` en el cliente cuando ya no sea necesario, con el fin de liberar recursos. Después de llamar al método close, la instancia de cliente ya no se puede usar.

### Analizar las respuestas
<a name="examples-ec2-IMDS-use-pares"></a>

Los metadatos de la instancia EC2 se pueden generar en diferentes formatos. Los más utilizados son el texto sin formato y JSON. Los clientes de metadatos ofrecen formas de trabajar con esos formatos. 

Como se muestra en el ejemplo siguiente, utilice el método `asString` para obtener los datos como una cadena Java. También puede usar el método `asList` para separar una respuesta de texto sin formato que devuelva varias líneas. 

```
Ec2MetadataClient client = Ec2MetadataClient.create();
Ec2MetadataResponse response = client.get("/latest/meta-data/");
String fullResponse = response.asString();
List<String> splits = response.asList();
```

Si la respuesta está en JSON, use el método `Ec2MetadataResponse#asDocument` para analizarla y convertirla en una instancia de [Document](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/document/Document.html), como se muestra en el fragmento de código siguiente.

```
Document fullResponse = response.asDocument();
```

Si el formato de los metadatos no está en JSON, se producirá una excepción. Si la respuesta se ha analizado correctamente, puede utilizar la [API de documentos](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/document/package-summary.html) para inspeccionarla con más detalle. Consulte la [tabla de categorías de metadatos](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-categories.html) de instancia para saber qué categorías de metadatos ofrecen respuestas con formato JSON.

## Configurar un cliente de metadatos
<a name="examples-ec2-IMDS-config"></a>

### Reintentos
<a name="examples-ec2-IMDS-config-retries"></a>

Puede configurar un cliente de metadatos con un mecanismo de reintento. Si lo hace, el cliente podrá reintentar automáticamente las solicitudes que no puedan completarse por motivos inesperados. De forma predeterminada, el cliente lo vuelve a intentar tres veces en caso de una solicitud fallida, con un tiempo de retroceso exponencial entre intentos.

Si su caso de uso requiere un mecanismo de reintento diferente, puede personalizar el cliente mediante el método `retryPolicy` de su generador. Por ejemplo, el siguiente ejemplo muestra un cliente síncrono configurado con un retraso fijo de dos segundos entre intentos y cinco reintentos.

```
BackoffStrategy fixedBackoffStrategy = FixedDelayBackoffStrategy.create(Duration.ofSeconds(2));
Ec2MetadataClient client =
    Ec2MetadataClient.builder()
                     .retryPolicy(retryPolicyBuilder -> retryPolicyBuilder.numRetries(5)
                                                                           .backoffStrategy(fixedBackoffStrategy))
                     .build();
```

Hay varios [BackoffStrategies](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/backoff/package-summary.html)que puede usar con un cliente de metadatos.

También puede inhabilitar por completo el mecanismo de reintento, como se muestra en el siguiente fragmento de código.

```
Ec2MetadataClient client =
    Ec2MetadataClient.builder()
                    .retryPolicy(Ec2MetadataRetryPolicy.none())
                    .build();
```

El uso de `Ec2MetadataRetryPolicy#none()` desactiva la política de reintentos predeterminada para que el cliente de metadatos no efectúe reintentos.

### Versión de IP
<a name="examples-ec2-IMDS-config-ipversion"></a>

De forma predeterminada, un cliente de metadatos usa el IPV4 punto final en`http://169.254.169.254`. Para cambiar el cliente y usar la IPV6 versión, utilice el `endpoint` método `endpointMode` o el del generador. Si se llaman ambos métodos en el constructor se produce una excepción.

En los ejemplos siguientes se muestran ambas IPV6 opciones.

```
Ec2MetadataClient client =
    Ec2MetadataClient.builder()
                     .endpointMode(EndpointMode.IPV6)
                     .build();
```

```
Ec2MetadataClient client =
    Ec2MetadataClient.builder()
                     .endpoint(URI.create("http://[fd00:ec2::254]"))
                     .build();
```

## Características principales de
<a name="examples-ec2-IMDS-features"></a>

### Cliente asíncrono
<a name="examples-ec2-IMDS-features-async"></a>

Para utilizar la versión no bloqueante del cliente, instancie una instancia de la clase `Ec2MetadataAsyncClient`. El código del siguiente ejemplo crea un cliente asíncrono con la configuración predeterminada y utiliza el método `get` para recuperar el valor de la clave `ami-id`.

```
Ec2MetadataAsyncClient asyncClient = Ec2MetadataAsyncClient.create();
CompletableFuture<Ec2MetadataResponse> response = asyncClient.get("/latest/meta-data/ami-id");
```

El valor de `java.util.concurrent.CompletableFuture` devuelto por el método `get` se completa cuando se devuelve la respuesta. En el ejemplo siguiente, se imprimen los metadatos `ami-id` en la consola.

```
response.thenAccept(metadata -> System.out.println(metadata.asString()));
```

### Cliente HTTP configurable
<a name="examples-ec2-IMDS-features-http"></a>

El generador de cada cliente de metadatos tiene un método `httpClient` que puede utilizar para proporcionar un cliente HTTP personalizado. 

El ejemplo siguiente muestra el código para una instancia `UrlConnectionHttpClient` personalizada.

```
SdkHttpClient httpClient =
    UrlConnectionHttpClient.builder()
                           .socketTimeout(Duration.ofMinutes(5))
                           .proxyConfiguration(proxy -> proxy.endpoint(URI.create("http://proxy.example.net:8888"))))
                           .build();
Ec2MetadataClient metaDataClient =
    Ec2MetadataClient.builder()
                     .httpClient(httpClient)
                     .build();
// Use the metaDataClient instance.
metaDataClient.close();   // Close the instance when no longer needed.
```

En el ejemplo siguiente, se muestra el código de una instancia `NettyNioAsyncHttpClient` personalizada con un cliente de metadatos asíncrono.

```
SdkAsyncHttpClient httpAsyncClient = 
    NettyNioAsyncHttpClient.builder()
                           .connectionTimeout(Duration.ofMinutes(5))
                           .maxConcurrency(100)
                           .build();
Ec2MetadataAsyncClient asyncMetaDataClient =
    Ec2MetadataAsyncClient.builder()
                          .httpClient(httpAsyncClient)
                          .build();
// Use the asyncMetaDataClient instance.
asyncMetaDataClient.close();   // Close the instance when no longer needed.
```

El tema [Configure los clientes HTTP en el AWS SDK for Java 2.x](http-configuration.md) de esta guía proporciona detalles sobre cómo configurar los clientes de HTTP disponibles en el SDK para Java.

### Almacenamiento en caché de tokens
<a name="examples-ec2-IMDS-features-token"></a>

Como los metadatos que utilizan los clientes IMDSv2, todas las solicitudes están asociadas a una sesión. Una sesión se define mediante un token que tiene una caducidad, que el cliente de metadatos administra en su lugar. Cada solicitud de metadatos reutiliza automáticamente el token hasta que caduque. 

De forma predeterminada, un token dura seis horas (21 600 segundos). Le recomendamos que mantenga el time-to-live valor predeterminado, a menos que su caso de uso específico requiera una configuración avanzada. 

Si es necesario, configure la duración utilizando el método de generador `tokenTtl`. Por ejemplo, el código del siguiente fragmento crea un cliente con una duración de sesión de cinco minutos. 

```
Ec2MetadataClient client =
    Ec2MetadataClient.builder()
                     .tokenTtl(Duration.ofMinutes(5))
                     .build();
```

Si omite llamar al método `tokenTtl` en el generador, se utilizará en su lugar la duración predeterminada de 21 600. 

# Trabaja con IAM
<a name="examples-iam"></a>

En esta sección se proporcionan ejemplos de programación AWS Identity and Access Management (IAM) mediante la versión AWS SDK para Java 2.x.

 AWS Identity and Access Management (IAM) le permite controlar de forma segura el acceso de sus AWS usuarios a los servicios y recursos. Con IAMél, puede crear y administrar AWS usuarios y grupos, y utilizar los permisos para permitir o denegar su acceso a AWS los recursos. Para obtener una guía completa IAM, visita la [Guía IAM del usuario](https://docs.aws.amazon.com//IAM/latest/UserGuide/introduction.html).

Los siguientes ejemplos incluyen únicamente el código necesario para demostrar cada técnica. El [código de ejemplo completo está disponible en GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2). Desde allí, puede descargar un único archivo de código fuente o clonar el repositorio localmente para obtener todos los ejemplos para compilarlos y ejecutarlos.

**Topics**
+ [Administrar claves de acceso de IAM](examples-iam-access-keys.md)
+ [Administrar usuarios de IAM](examples-iam-users.md)
+ [Crear políticas de IAM](feature-iam-policy-builder.md)
+ [Trabajar con políticas de IAM](examples-iam-policies.md)
+ [Trabajar con certificados IAM de servidor](examples-iam-server-certificates.md)

# Administrar claves de acceso de IAM
<a name="examples-iam-access-keys"></a>

## Creación de una clave de acceso
<a name="create-an-access-key"></a>

Para crear una clave de acceso de IAM, llame al método `IamClient’s` de `createAccessKey` con un objeto [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/CreateAccessKeyRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/CreateAccessKeyRequest.html).

**nota**  
Debe establecer la región en **AWS\$1GLOBAL** para que las llamadas a `IamClient` funcionen, porque IAM es un servicio global.

 **Importaciones** 

```
import software.amazon.awssdk.services.iam.model.CreateAccessKeyRequest;
import software.amazon.awssdk.services.iam.model.CreateAccessKeyResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.IamException;
```

 **Código de** 

```
    public static String createIAMAccessKey(IamClient iam,String user) {

        try {
            CreateAccessKeyRequest request = CreateAccessKeyRequest.builder()
                .userName(user).build();

            CreateAccessKeyResponse response = iam.createAccessKey(request);
           String keyId = response.accessKey().accessKeyId();
           return keyId;

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        return "";
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/iam/src/main/java/com/example/iam/CreateAccessKey.java) en GitHub.

## Mostrar claves de acceso
<a name="list-access-keys"></a>

Para mostrar las claves de acceso de un determinado usuario, cree un objeto [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListAccessKeysRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListAccessKeysRequest.html) que contenga el nombre de usuario para el que se desean mostrar la claves y páselo al método `listAccessKeys` de `IamClient’s`.

**nota**  
Si no proporciona un nombre de usuario a `listAccessKeys`, intentará enumerar las claves de acceso asociadas a la Cuenta de AWS que firmó la solicitud.

 **Importaciones** 

```
import software.amazon.awssdk.services.iam.model.AccessKeyMetadata;
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.services.iam.model.ListAccessKeysRequest;
import software.amazon.awssdk.services.iam.model.ListAccessKeysResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
```

 **Código de** 

```
    public static void listKeys( IamClient iam,String userName ){

        try {
            boolean done = false;
            String newMarker = null;

            while (!done) {
                ListAccessKeysResponse response;

            if(newMarker == null) {
                ListAccessKeysRequest request = ListAccessKeysRequest.builder()
                        .userName(userName).build();
                response = iam.listAccessKeys(request);
            } else {
                ListAccessKeysRequest request = ListAccessKeysRequest.builder()
                        .userName(userName)
                        .marker(newMarker).build();
                response = iam.listAccessKeys(request);
            }

            for (AccessKeyMetadata metadata :
                    response.accessKeyMetadata()) {
                System.out.format("Retrieved access key %s",
                        metadata.accessKeyId());
            }

            if (!response.isTruncated()) {
                done = true;
            } else {
                newMarker = response.marker();
            }
        }

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Los resultados de `listAccessKeys` están paginados (con un máximo de 100 registros por llamada). Puede llamar a `isTruncated` en el objeto [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListAccessKeysResponse.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListAccessKeysResponse.html) devuelto para saber si la consulta ha devuelto menos resultados de los que están disponibles. En tal caso, llame a `marker` en el objeto `ListAccessKeysResponse` y úselo al crear una nueva solicitud. Utilice esta nueva solicitud en la siguiente invocación de `listAccessKeys`.

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/iam/src/main/java/com/example/iam/ListAccessKeys.java) en GitHub.

## Recuperar el momento en que se usó por última vez una clave de acceso
<a name="retrieve-an-access-key-s-last-used-time"></a>

Para obtener la hora en que se utilizó una clave de acceso por última vez, llame al método `getAccessKeyLastUsed` de `IamClient’s` con el ID de clave de acceso (que se puede pasar mediante un objeto [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/GetAccessKeyLastUsedRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/GetAccessKeyLastUsedRequest.html)).

A continuación, puede utilizar el objeto [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/GetAccessKeyLastUsedResponse.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/GetAccessKeyLastUsedResponse.html) devuelto para recuperar el momento en que se usó por última vez la clave.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.GetAccessKeyLastUsedRequest;
import software.amazon.awssdk.services.iam.model.GetAccessKeyLastUsedResponse;
import software.amazon.awssdk.services.iam.model.IamException;
```

 **Código de** 

```
    public static void getAccessKeyLastUsed(IamClient iam, String accessId ){

        try {
            GetAccessKeyLastUsedRequest request = GetAccessKeyLastUsedRequest.builder()
                    .accessKeyId(accessId).build();

            GetAccessKeyLastUsedResponse response = iam.getAccessKeyLastUsed(request);

            System.out.println("Access key was last used at: " +
                    response.accessKeyLastUsed().lastUsedDate());

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        System.out.println("Done");
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/iam/src/main/java/com/example/iam/AccessKeyLastUsed.java) en GitHub.

## Activar o desactivar claves de acceso
<a name="iam-access-keys-update"></a>

Puede activar o desactivar una clave de acceso creando un objeto [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/UpdateAccessKeyRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/UpdateAccessKeyRequest.html), proporcionando el ID de clave de acceso, el nombre de usuario (de forma opcional) y el [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/StatusType.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/StatusType.html) deseado y, a continuación, pasando el objeto de la solicitud al método `updateAccessKey` de `IamClient’s`.

 **Importaciones** 

```
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.services.iam.model.StatusType;
import software.amazon.awssdk.services.iam.model.UpdateAccessKeyRequest;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
```

 **Código de** 

```
       public static void updateKey(IamClient iam, String username, String accessId, String status ) {

          try {
              if (status.toLowerCase().equalsIgnoreCase("active")) {
                  statusType = StatusType.ACTIVE;
              } else if (status.toLowerCase().equalsIgnoreCase("inactive")) {
                  statusType = StatusType.INACTIVE;
              } else {
                  statusType = StatusType.UNKNOWN_TO_SDK_VERSION;
              }
              UpdateAccessKeyRequest request = UpdateAccessKeyRequest.builder()
                .accessKeyId(accessId)
                .userName(username)
                .status(statusType)
                .build();

              iam.updateAccessKey(request);

              System.out.printf(
                "Successfully updated the status of access key %s to" +
                        "status %s for user %s", accessId, status, username);

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/iam/src/main/java/com/example/iam/UpdateAccessKey.java) en GitHub.

## Eliminación de una clave de acceso
<a name="delete-an-access-key"></a>

Para eliminar de forma permanente una clave de acceso, llame al método `deleteKey` de `IamClient’s`, proporcionándole un [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/DeleteAccessKeyRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/DeleteAccessKeyRequest.html) que contenga el ID y el nombre de usuario de la clave de acceso.

**nota**  
Una vez eliminada una clave, ya no se puede recuperar ni utilizar. Para desactivar temporalmente una clave de forma que pueda activarse de nuevo más adelante, utilice el método [`updateAccessKey`](#iam-access-keys-update) en su lugar.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.DeleteAccessKeyRequest;
import software.amazon.awssdk.services.iam.model.IamException;
```

 **Código de** 

```
    public static void deleteKey(IamClient iam ,String username, String accessKey ) {

        try {
            DeleteAccessKeyRequest request = DeleteAccessKeyRequest.builder()
                    .accessKeyId(accessKey)
                    .userName(username)
                    .build();

            iam.deleteAccessKey(request);
            System.out.println("Successfully deleted access key " + accessKey +
                " from user " + username);

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/iam/src/main/java/com/example/iam/DeleteAccessKey.java) en GitHub.

## Más información
<a name="more-information"></a>
+  [CreateAccessKey](https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreateAccessKey.html) en la Referencia de la API de IAM
+  [ListAccessKeys](https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListAccessKeys.html) en la referencia de la API IAM
+  [GetAccessKeyLastUsed](https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetAccessKeyLastUsed.html) en la referencia de la API de IAM
+  [UpdateAccessKey](https://docs.aws.amazon.com/IAM/latest/APIReference/API_UpdateAccessKey.html) en la Referencia de la API de IAM
+  [DeleteAccessKey](https://docs.aws.amazon.com/IAM/latest/APIReference/API_DeleteAccessKey.html) en la Referencia de la API de IAM

# Administrar usuarios de IAM
<a name="examples-iam-users"></a>

## Crear un usuario
<a name="creating-a-user"></a>

Para crear un nuevo usuario de IAM, proporcione el nombre al método `createUser` de IamClient mediante un objeto [CreateUserRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/CreateUserRequest.html) que contenga el nombre de usuario.

 **Importaciones** 

```
import software.amazon.awssdk.core.waiters.WaiterResponse;
import software.amazon.awssdk.services.iam.model.CreateUserRequest;
import software.amazon.awssdk.services.iam.model.CreateUserResponse;
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.waiters.IamWaiter;
import software.amazon.awssdk.services.iam.model.GetUserRequest;
import software.amazon.awssdk.services.iam.model.GetUserResponse;
```

 **Código de** 

```
    public static String createIAMUser(IamClient iam, String username ) {

        try {
            // Create an IamWaiter object
            IamWaiter iamWaiter = iam.waiter();

            CreateUserRequest request = CreateUserRequest.builder()
                    .userName(username)
                    .build();

            CreateUserResponse response = iam.createUser(request);

            // Wait until the user is created
            GetUserRequest userRequest = GetUserRequest.builder()
                    .userName(response.user().userName())
                    .build();

            WaiterResponse<GetUserResponse> waitUntilUserExists = iamWaiter.waitUntilUserExists(userRequest);
            waitUntilUserExists.matched().response().ifPresent(System.out::println);
            return response.user().userName();

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
       return "";
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/iam/src/main/java/com/example/iam/CreateUser.java) en GitHub.

## Enumerar usuarios
<a name="listing-users"></a>

Para enumerar los usuarios de IAM de su cuenta, cree un nuevo objeto [ListUsersRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListUsersRequest.html) y páselo al método `listUsers` de i. Puede recuperar la lista de usuarios llamando a `users` en el objeto [ListUsersResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListUsersResponse.html) devuelto.

La lista de usuarios devuelta por `listUsers` está paginada. Puede comprobar que no haya más resultados que recuperar llamando al método `isTruncated` del objeto de respuesta. Si devuelve `true`, llame al método `marker()` del objeto de respuesta. Utilice el valor del marcador para crear un nuevo objeto de solicitud. Llame entonces al método `listUsers` de nuevo con la nueva solicitud.

 **Importaciones** 

```
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.services.iam.model.ListUsersRequest;
import software.amazon.awssdk.services.iam.model.ListUsersResponse;
import software.amazon.awssdk.services.iam.model.User;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
```

 **Código de** 

```
    public static void listAllUsers(IamClient iam ) {

        try {

             boolean done = false;
             String newMarker = null;

             while(!done) {
                ListUsersResponse response;

                if (newMarker == null) {
                    ListUsersRequest request = ListUsersRequest.builder().build();
                    response = iam.listUsers(request);
                } else {
                    ListUsersRequest request = ListUsersRequest.builder()
                        .marker(newMarker).build();
                    response = iam.listUsers(request);
                }

                for(User user : response.users()) {
                 System.out.format("\n Retrieved user %s", user.userName());
                }

                if(!response.isTruncated()) {
                  done = true;
                } else {
                    newMarker = response.marker();
                }
            }
        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/iam/src/main/java/com/example/iam/ListUsers.java) en GitHub.

## Actualizar un usuario
<a name="updating-a-user"></a>

Para actualizar un usuario, llame al método `updateUser` de IamClient, que toma un objeto [UpdateUserRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/UpdateUserRequest.html) que puede utilizar para cambiar el *nombre* o la *ruta* del usuario.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.services.iam.model.UpdateUserRequest;
```

 **Código de** 

```
    public static void updateIAMUser(IamClient iam, String curName,String newName ) {

        try {
            UpdateUserRequest request = UpdateUserRequest.builder()
                    .userName(curName)
                    .newUserName(newName)
                    .build();

            iam.updateUser(request);
            System.out.printf("Successfully updated user to username %s",
                newName);
        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
      }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/iam/src/main/java/com/example/iam/UpdateUser.java) en GitHub.

## Eliminación de un usuario
<a name="deleting-a-user"></a>

Para eliminar un usuario, llame a la solicitud `deleteUser` de IamClient con un objeto [UpdateUserRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/UpdateUserRequest.html) definido con el nombre de usuario que desea eliminar.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.DeleteUserRequest;
import software.amazon.awssdk.services.iam.model.IamException;
```

 **Código de** 

```
    public static void deleteIAMUser(IamClient iam, String userName) {

        try {
            DeleteUserRequest request = DeleteUserRequest.builder()
                    .userName(userName)
                    .build();

            iam.deleteUser(request);
            System.out.println("Successfully deleted IAM user " + userName);
        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/iam/src/main/java/com/example/iam/DeleteUser.java) en GitHub.

## Más información
<a name="more-information"></a>
+  [Usuarios de IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users.html) en la Guía del usuario de IAM
+  [Gestión de usuarios de IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_manage.html) en la Guía del usuario de IAM
+  [CreateUser](https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreateUser.html) en la Referencia de la API de IAM
+  [ListUsers](https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListUsers.html) en la Referencia de la API de IAM
+  [UpdateUser](https://docs.aws.amazon.com/IAM/latest/APIReference/API_UpdateUser.html) en la Referencia de la API de IAM
+  [DeleteUser](https://docs.aws.amazon.com/IAM/latest/APIReference/API_DeleteUser.html) en la Referencia de la API de IAM

# Cree políticas de IAM con AWS SDK for Java 2.x
<a name="feature-iam-policy-builder"></a>

La [API IAM Policy Builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/policybuilder/iam/package-summary.html) es una biblioteca que puede utilizar para crear [políticas de IAM en Java y cargarlas](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html) en AWS Identity and Access Management (IAM). 

En lugar de crear una política de IAM mediante el ensamblaje manual de una cadena JSON o la lectura de un archivo, la API proporciona un enfoque del cliente y orientado a los objetos para generar la cadena JSON. Al leer una política de IAM existente en formato JSON, la API la convierte en una [IamPolicy](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/policybuilder/iam/IamPolicy.html)instancia para su gestión.

La API Policy Builder de IAM comenzó a estar disponible con la versión 2.20.105 del SDK, así que utilice esa versión o una posterior en su archivo de compilación de Maven. El número de versión más reciente del SDK [aparece en Maven Central](https://central.sonatype.com/artifact/software.amazon.awssdk/iam-policy-builder).

En el fragmento siguiente, se muestra un bloque de dependencia de ejemplo para un archivo `pom.xml` Maven. Esto le permite utilizar la API del creador de políticas de IAM en su proyecto. 

```
<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>iam-policy-builder</artifactId>
    <version>2.27.21</version>
</dependency>
```

## Crear un `IamPolicy`
<a name="iam-policy-builder-create"></a>

En esta sección se muestran varios ejemplos de cómo crear políticas mediante la API del creador de políticas de IAM.

En cada uno de los ejemplos siguientes, comience con el `[IamPolicy.Builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/policybuilder/iam/IamPolicy.Builder.html)` y añada una o más instrucciones mediante el método `addStatement`. Siguiendo este patrón, el [IamStatement.Builder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/policybuilder/iam/IamStatement.Builder.html) tiene métodos para añadir el efecto, las acciones, los recursos y las condiciones a la declaración.

### Ejemplo: crear una política basada en el tiempo.
<a name="iam-policy-builder-create-ex-time-based"></a>

El siguiente ejemplo crea una política basada en la identidad que permite la acción `GetItem` de Amazon DynamoDB entre dos momentos.

```
    public String timeBasedPolicyExample() {
        IamPolicy policy = IamPolicy.builder()
                .addStatement(b -> b
                        .effect(IamEffect.ALLOW)
                        .addAction("dynamodb:GetItem")
                        .addResource(IamResource.ALL)
                        .addCondition(b1 -> b1
                                .operator(IamConditionOperator.DATE_GREATER_THAN)
                                .key("aws:CurrentTime")
                                .value("2020-04-01T00:00:00Z"))
                        .addCondition(b1 -> b1
                                .operator(IamConditionOperator.DATE_LESS_THAN)
                                .key("aws:CurrentTime")
                                .value("2020-06-30T23:59:59Z")))
                .build();

        // Use an IamPolicyWriter to write out the JSON string to a more readable format.
        return policy.toJson(IamPolicyWriter.builder()
                        .prettyPrint(true)
                        .build());
    }
```

#### Salida de JSON
<a name="iam-builder-ex-json-date"></a>

La última instrucción del ejemplo anterior devuelve la siguiente cadena JSON. 

Para obtener más información sobre este [ejemplo](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_examples_aws-dates.html), consulte la *Guía del usuario de AWS Identity and Access Management *.

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": {
        "Effect": "Allow",
        "Action": "dynamodb:GetItem",
        "Resource": "*",
        "Condition": {
            "DateGreaterThan": {
                "aws:CurrentTime": "2020-04-01T00:00:00Z"
            },
            "DateLessThan": {
                "aws:CurrentTime": "2020-06-30T23:59:59Z"
            }
        }
    }
}
```

------

### Ejemplo: especificar varias condiciones
<a name="iam-policy-builder-create-ex-multi-conditions"></a>

En el ejemplo siguiente se muestra cómo crear una política basada en identidad que permita el acceso a los atributos específicos de DynamoDB. La política contiene dos condiciones.

```
    public String multipleConditionsExample() {
        IamPolicy policy = IamPolicy.builder()
                .addStatement(b -> b
                        .effect(IamEffect.ALLOW)
                        .addAction("dynamodb:GetItem")
                        .addAction("dynamodb:BatchGetItem")
                        .addAction("dynamodb:Query")
                        .addAction("dynamodb:PutItem")
                        .addAction("dynamodb:UpdateItem")
                        .addAction("dynamodb:DeleteItem")
                        .addAction("dynamodb:BatchWriteItem")
                        .addResource("arn:aws:dynamodb:*:*:table/table-name")
                        .addConditions(IamConditionOperator.STRING_EQUALS.addPrefix("ForAllValues:"),
                                "dynamodb:Attributes",
                                List.of("column-name1", "column-name2", "column-name3"))
                        .addCondition(b1 -> b1.operator(IamConditionOperator.STRING_EQUALS.addSuffix("IfExists"))
                                .key("dynamodb:Select")
                                .value("SPECIFIC_ATTRIBUTES")))
                .build();

        return policy.toJson(IamPolicyWriter.builder()
                .prettyPrint(true).build());
    }
```

#### Salida de JSON
<a name="iam-builder-ex-json-multi-cond"></a>

La última instrucción del ejemplo anterior devuelve la siguiente cadena JSON. 

Para obtener más información sobre este [ejemplo](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_examples_dynamodb_attributes.html) consulte la *Guía del usuario de AWS Identity and Access Management *.

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": {
        "Effect": "Allow",
        "Action": [
            "dynamodb:GetItem",
            "dynamodb:BatchGetItem",
            "dynamodb:Query",
            "dynamodb:PutItem",
            "dynamodb:UpdateItem",
            "dynamodb:DeleteItem",
            "dynamodb:BatchWriteItem"
        ],
        "Resource": "arn:aws:dynamodb:*:*:table/table-name",
        "Condition": {
            "ForAllValues:StringEquals": {
                "dynamodb:Attributes": [
                    "column-name1",
                    "column-name2",
                    "column-name3"
                ]
            },
            "StringEqualsIfExists": {
                "dynamodb:Select": "SPECIFIC_ATTRIBUTES"
            }
        }
    }
}
```

------

### Ejemplo: especificar entidades principales
<a name="iam-policy-builder-create-ex-principals"></a>

En el siguiente ejemplo, se muestra cómo crear una política basada en recursos que deniegue el acceso a un bucket a todas las entidades principales, excepto a las especificadas en la condición.

```
    public String specifyPrincipalsExample() {
        IamPolicy policy = IamPolicy.builder()
                .addStatement(b -> b
                        .effect(IamEffect.DENY)
                        .addAction("s3:*")
                        .addPrincipal(IamPrincipal.ALL)
                        .addResource("arn:aws:s3:::BUCKETNAME/*")
                        .addResource("arn:aws:s3:::BUCKETNAME")
                        .addCondition(b1 -> b1
                                .operator(IamConditionOperator.ARN_NOT_EQUALS)
                                .key("aws:PrincipalArn")
                                .value("arn:aws:iam::444455556666:user/user-name")))
                .build();
        return policy.toJson(IamPolicyWriter.builder()
                .prettyPrint(true).build());
    }
```

#### Salida de JSON
<a name="iam-policy-builder-create-json-ex-principals"></a>

La última instrucción del ejemplo anterior devuelve la siguiente cadena JSON. 

Para obtener más información sobre este [ejemplo](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_principal.html#principal-anonymous) consulte la *Guía del usuario de AWS Identity and Access Management *.

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement" : {
    "Effect" : "Deny",
    "Principal" : "*",
    "Action" : "s3:*",
    "Resource" : [ "arn:aws:s3:::BUCKETNAME/*", "arn:aws:s3:::BUCKETNAME" ],
    "Condition" : {
      "ArnNotEquals" : {
        "aws:PrincipalArn" : "arn:aws:iam::444455556666:user/user-name"
      }
    }
  }
}
```

------

### Ejemplo: permitir el acceso entre cuentas
<a name="iam-policy-builder-create-ex-cross-account"></a>

En el siguiente ejemplo, se muestra cómo permitir que otra persona Cuenta de AWS cargue objetos en tu bucket y, al mismo tiempo, mantener el control total del propietario de los objetos cargados. 

```
    public String allowCrossAccountAccessExample() {
        IamPolicy policy = IamPolicy.builder()
                .addStatement(b -> b
                        .effect(IamEffect.ALLOW)
                        .addPrincipal(IamPrincipalType.AWS, "111122223333")
                        .addAction("s3:PutObject")
                        .addResource("arn:aws:s3:::amzn-s3-demo-bucket/*")
                        .addCondition(b1 -> b1
                                .operator(IamConditionOperator.STRING_EQUALS)
                                .key("s3:x-amz-acl")
                                .value("bucket-owner-full-control")))
                .build();
        return policy.toJson(IamPolicyWriter.builder()
                .prettyPrint(true).build());
    }
```

#### Salida de JSON
<a name="iam-policy-builder-create-ex-json-cross-account"></a>

La última instrucción del ejemplo anterior devuelve la siguiente cadena JSON. 

Para más información sobre este [ejemplo](https://docs.aws.amazon.com/AmazonS3/latest/userguide/example-bucket-policies.html#example-bucket-policies-acl-2), consulte la *Guía del usuario de Amazon Simple Storage Service*.

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Statement" : {
    "Effect" : "Allow",
    "Principal" : {
      "AWS" : "111122223333"
    },
    "Action" : "s3:PutObject",
    "Resource" : "arn:aws:s3:::amzn-s3-demo-bucket/*",
    "Condition" : {
      "StringEquals" : {
        "s3:x-amz-acl" : "bucket-owner-full-control"
      }
    }
  }
}
```

------

## Utilizar una `IamPolicy` con IAM
<a name="iam-policy-builder-work-with-service"></a>

Tras crear una instancia de `IamPolicy`, utilice un [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/IamClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/IamClient.html) para trabajar con el servicio de IAM. 

El ejemplo siguiente crea una política que permite a una [identidad de IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/id.html) escribir elementos en una tabla de DynamoDB de la cuenta que se especifica con el parámetro `accountID`. A continuación, la política se carga en IAM como una cadena JSON.

```
    public String createAndUploadPolicyExample(IamClient iam, String accountID, String policyName) {
        // Build the policy.
        IamPolicy policy =
                IamPolicy.builder() // 'version' defaults to "2012-10-17".
                        .addStatement(IamStatement.builder()
                                .effect(IamEffect.ALLOW)
                                .addAction("dynamodb:PutItem")
                                .addResource("arn:aws:dynamodb:us-east-1:" + accountID + ":table/exampleTableName")
                                .build())
                        .build();
        // Upload the policy.
        iam.createPolicy(r -> r.policyName(policyName).policyDocument(policy.toJson()));
        return policy.toJson(IamPolicyWriter.builder().prettyPrint(true).build());
    }
```

El ejemplo siguiente se basa en el anterior. El código descarga la política y la utiliza como base para una nueva política copiando y modificando la declaración. A continuación, se carga la nueva política.

```
    public String createNewBasedOnExistingPolicyExample(IamClient iam, String accountID, String policyName, String newPolicyName) {

        String policyArn = "arn:aws:iam::" + accountID + ":policy/" + policyName;
        GetPolicyResponse getPolicyResponse = iam.getPolicy(r -> r.policyArn(policyArn));

        String policyVersion = getPolicyResponse.policy().defaultVersionId();
        GetPolicyVersionResponse getPolicyVersionResponse =
                iam.getPolicyVersion(r -> r.policyArn(policyArn).versionId(policyVersion));

        // Create an IamPolicy instance from the JSON string returned from IAM.
        String decodedPolicy = URLDecoder.decode(getPolicyVersionResponse.policyVersion().document(), StandardCharsets.UTF_8);
        IamPolicy policy = IamPolicy.fromJson(decodedPolicy);

            /*
             All IamPolicy components are immutable, so use the copy method that creates a new instance that
             can be altered in the same method call.

             Add the ability to get an item from DynamoDB as an additional action.
            */
        IamStatement newStatement = policy.statements().get(0).copy(s -> s.addAction("dynamodb:GetItem"));

        // Create a new statement that replaces the original statement.
        IamPolicy newPolicy = policy.copy(p -> p.statements(Arrays.asList(newStatement)));

        // Upload the new policy. IAM now has both policies.
        iam.createPolicy(r -> r.policyName(newPolicyName)
                .policyDocument(newPolicy.toJson()));

        return newPolicy.toJson(IamPolicyWriter.builder().prettyPrint(true).build());
    }
```

### IamClient
<a name="iam-policy-builder-work-with-serivce-create-client"></a>

Los ejemplos anteriores utilizan un argumento `IamClient` que se crea tal y como se muestra en el siguiente fragmento.

```
IamClient iam = IamClient.builder().region(Region.AWS_GLOBAL).build();
```

### Políticas en JSON
<a name="iam-policy-builder-work-with-serivce-json"></a>

Los ejemplos devuelven las siguientes cadenas JSON.

```
First example
{
  "Version": "2012-10-17",		 	 	 
  "Statement" : {
    "Effect" : "Allow",
    "Action" : "dynamodb:PutItem",
    "Resource" : "arn:aws:dynamodb:us-east-1:111122223333:table/exampleTableName"
  }
}

Second example
{
  "Version": "2012-10-17",		 	 	 
  "Statement" : {
    "Effect" : "Allow",
    "Action" : [ "dynamodb:PutItem", "dynamodb:GetItem" ],
    "Resource" : "arn:aws:dynamodb:us-east-1:111122223333:table/exampleTableName"
  }
}
```

# Trabajar con políticas de IAM
<a name="examples-iam-policies"></a>

## Crear una política.
<a name="create-a-policy"></a>

Para crear una nueva política, proporcione el nombre de la política y un documento de política en formato JSON en un objeto [CreatePolicyRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/CreatePolicyRequest.html) al método `createPolicy` de IamClient

 **Importaciones** 

```
import software.amazon.awssdk.core.waiters.WaiterResponse;
import software.amazon.awssdk.services.iam.model.CreatePolicyRequest;
import software.amazon.awssdk.services.iam.model.CreatePolicyResponse;
import software.amazon.awssdk.services.iam.model.GetPolicyRequest;
import software.amazon.awssdk.services.iam.model.GetPolicyResponse;
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.waiters.IamWaiter;
```

 **Código de** 

```
    public static String createIAMPolicy(IamClient iam, String policyName ) {

        try {
            // Create an IamWaiter object
            IamWaiter iamWaiter = iam.waiter();

            CreatePolicyRequest request = CreatePolicyRequest.builder()
                .policyName(policyName)
                .policyDocument(PolicyDocument).build();

            CreatePolicyResponse response = iam.createPolicy(request);

            // Wait until the policy is created
            GetPolicyRequest polRequest = GetPolicyRequest.builder()
                    .policyArn(response.policy().arn())
                    .build();

            WaiterResponse<GetPolicyResponse> waitUntilPolicyExists = iamWaiter.waitUntilPolicyExists(polRequest);
            waitUntilPolicyExists.matched().response().ifPresent(System.out::println);
            return response.policy().arn();

         } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        return "" ;
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/e41bacfd5e671e7ef1a9f73e972540dc6a434664/javav2/example_code/iam/src/main/java/com/example/iam/CreatePolicy.java) en GitHub.

## Obtención de una política
<a name="get-a-policy"></a>

Para recuperar una política existente, llame al método `getPolicy` de IamClient proporcionando el ARN de la política en un objeto [GetPolicyRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/GetPolicyRequest.html).

 **Importaciones** 

```
import software.amazon.awssdk.services.iam.model.GetPolicyRequest;
import software.amazon.awssdk.services.iam.model.GetPolicyResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.IamException;
```

 **Código de** 

```
    public static void getIAMPolicy(IamClient iam, String policyArn) {

        try {
            GetPolicyRequest request = GetPolicyRequest.builder()
                .policyArn(policyArn).build();

            GetPolicyResponse response = iam.getPolicy(request);
            System.out.format("Successfully retrieved policy %s",
                response.policy().policyName());

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/iam/src/main/java/com/example/iam/GetPolicy.java) en GitHub.

## Asociar una política de rol
<a name="attach-a-role-policy"></a>

Puede asociar una política a un [rol](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html) de IAM llamando al método `attachRolePolicy` de IamClient, con el nombre de rol y el ARN de política en un objeto [AttachRolePolicyRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/AttachRolePolicyRequest.html).

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.services.iam.model.AttachRolePolicyRequest;
import software.amazon.awssdk.services.iam.model.AttachedPolicy;
import software.amazon.awssdk.services.iam.model.ListAttachedRolePoliciesRequest;
import software.amazon.awssdk.services.iam.model.ListAttachedRolePoliciesResponse;
import java.util.List;
```

 **Código de** 

```
    public static void attachIAMRolePolicy(IamClient iam, String roleName, String policyArn ) {

        try {

             ListAttachedRolePoliciesRequest request = ListAttachedRolePoliciesRequest.builder()
                    .roleName(roleName)
                    .build();

            ListAttachedRolePoliciesResponse  response = iam.listAttachedRolePolicies(request);
            List<AttachedPolicy> attachedPolicies = response.attachedPolicies();

            // Ensure that the policy is not attached to this role
            String polArn = "";
            for (AttachedPolicy policy: attachedPolicies) {
                polArn = policy.policyArn();
                if (polArn.compareTo(policyArn)==0) {
                   System.out.println(roleName +
                            " policy is already attached to this role.");
                    return;
                }
          }

            AttachRolePolicyRequest attachRequest =
                AttachRolePolicyRequest.builder()
                        .roleName(roleName)
                        .policyArn(policyArn)
                        .build();

            iam.attachRolePolicy(attachRequest);

            System.out.println("Successfully attached policy " + policyArn +
                " to role " + roleName);

         } catch (IamException e) {
                System.err.println(e.awsErrorDetails().errorMessage());
                System.exit(1);
          }

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

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/iam/src/main/java/com/example/iam/AttachRolePolicy.java) en GitHub.

## Mostrar las políticas de rol asociadas
<a name="list-attached-role-policies"></a>

Enumere las políticas asociadas a un rol llamando al método `listAttachedRolePolicies` de IamClient. Este método toma un objeto [ListAttachedRolePoliciesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListAttachedRolePoliciesRequest.html) que contiene el nombre del rol para el que se desea mostrar las políticas.

Llame a `getAttachedPolicies` en el objeto [ListAttachedRolePoliciesResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListAttachedRolePoliciesResponse.html) devuelto para obtener la lista de políticas asociadas. Los resultados pueden aparecer truncados; si el método `ListAttachedRolePoliciesResponse`del objeto `isTruncated` devuelve `true`, llame al método `ListAttachedRolePoliciesResponse` del objeto `marker`. Utilice el marcador devuelto para crear una nueva solicitud y use esta solicitud para llamar de nuevo a `listAttachedRolePolicies` para obtener el siguiente lote de resultados.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.services.iam.model.AttachRolePolicyRequest;
import software.amazon.awssdk.services.iam.model.AttachedPolicy;
import software.amazon.awssdk.services.iam.model.ListAttachedRolePoliciesRequest;
import software.amazon.awssdk.services.iam.model.ListAttachedRolePoliciesResponse;
import java.util.List;
```

 **Código de** 

```
    public static void attachIAMRolePolicy(IamClient iam, String roleName, String policyArn ) {

        try {

             ListAttachedRolePoliciesRequest request = ListAttachedRolePoliciesRequest.builder()
                    .roleName(roleName)
                    .build();

            ListAttachedRolePoliciesResponse  response = iam.listAttachedRolePolicies(request);
            List<AttachedPolicy> attachedPolicies = response.attachedPolicies();

            // Ensure that the policy is not attached to this role
            String polArn = "";
            for (AttachedPolicy policy: attachedPolicies) {
                polArn = policy.policyArn();
                if (polArn.compareTo(policyArn)==0) {
                   System.out.println(roleName +
                            " policy is already attached to this role.");
                    return;
                }
          }

            AttachRolePolicyRequest attachRequest =
                AttachRolePolicyRequest.builder()
                        .roleName(roleName)
                        .policyArn(policyArn)
                        .build();

            iam.attachRolePolicy(attachRequest);

            System.out.println("Successfully attached policy " + policyArn +
                " to role " + roleName);

         } catch (IamException e) {
                System.err.println(e.awsErrorDetails().errorMessage());
                System.exit(1);
          }

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

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/iam/src/main/java/com/example/iam/AttachRolePolicy.java) en GitHub.

## Desasociar una política de rol
<a name="detach-a-role-policy"></a>

Para desvincular una política de un rol, llame al método `detachRolePolicy` de IamClient proporcionando el nombre de rol y el ARN de política en un objeto [DetachRolePolicyRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/DetachRolePolicyRequest.html).

 **Importaciones** 

```
import software.amazon.awssdk.services.iam.model.DetachRolePolicyRequest;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.IamException;
```

 **Código de** 

```
    public static void detachPolicy(IamClient iam, String roleName, String policyArn ) {

        try {
            DetachRolePolicyRequest request = DetachRolePolicyRequest.builder()
                    .roleName(roleName)
                    .policyArn(policyArn)
                    .build();

            iam.detachRolePolicy(request);
            System.out.println("Successfully detached policy " + policyArn +
                " from role " + roleName);

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/iam/src/main/java/com/example/iam/DetachRolePolicy.java) en GitHub.

## Más información
<a name="more-information"></a>
+  [Información general sobre políticas de IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies.html) en la Guía del usuario de IAM.
+ [Referencia de políticas de AWS IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies.html) en la Guía del usuario de IAM.
+  [CreatePolicy](https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreatePolicy.html) en la Referencia de la API de IAM
+  [GetPolicy](https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetPolicy.html) en la referencia de la API de IAM
+  [AttachrolePolicy](https://docs.aws.amazon.com/IAM/latest/APIReference/API_AttachRolePolicy.html) en la referencia de la API de IAM
+  [ListatTachedrolePolicies](https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListAttachedRolePolicies.html) en la referencia de la API de IAM
+  [DetachRolePolicy](https://docs.aws.amazon.com/IAM/latest/APIReference/API_DetachRolePolicy.html) en la referencia de la API de IAM

# Trabajar con certificados IAM de servidor
<a name="examples-iam-server-certificates"></a>

Para habilitar las conexiones HTTPS a su sitio web o aplicación AWS, necesita un *certificado de SSL/TLS servidor*. Puede utilizar un certificado de servidor proporcionado AWS Certificate Manager o obtenido de un proveedor externo.

Le recomendamos que lo utilice ACM para aprovisionar, administrar e implementar sus certificados de servidor. Con él ACM puede solicitar un certificado, implementarlo en sus AWS recursos y ACM encargarse de las renovaciones de los certificados por usted. Los certificados proporcionados por ACM son gratuitos. Para obtener más información al respecto ACM, consulte la [Guía AWS Certificate Manager del usuario](https://docs.aws.amazon.com/acm/latest/userguide/acm-overview.html).

## Obtener un certificado de servidor
<a name="get-a-server-certificate"></a>

Para recuperar un certificado de servidor, llame al `getServerCertificate` método IamClient es y páselo [GetServerCertificateRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/GetServerCertificateRequest.html)con el nombre del certificado.

 **Importaciones** 

```
import software.amazon.awssdk.services.iam.model.GetServerCertificateRequest;
import software.amazon.awssdk.services.iam.model.GetServerCertificateResponse;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.IamException;
```

 **Código** 

```
    public static void getCertificate(IamClient iam,String certName ) {

        try {
            GetServerCertificateRequest request = GetServerCertificateRequest.builder()
                    .serverCertificateName(certName)
                    .build();

            GetServerCertificateResponse response = iam.getServerCertificate(request);
            System.out.format("Successfully retrieved certificate with body %s",
                response.serverCertificate().certificateBody());

         } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulta el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/iam/src/main/java/com/example/iam/GetServerCertificate.java) en GitHub.

## Elaborar listas de certificados de servidor
<a name="list-server-certificates"></a>

Para ver una lista de los certificados de su servidor, llame al `listServerCertificates` método IamClient's con un [ListServerCertificatesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListServerCertificatesRequest.html). Este método devuelve un objeto [ListServerCertificatesResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ListServerCertificatesResponse.html).

Llama al `serverCertificateMetadataList` método del `ListServerCertificateResponse` objeto devuelto para obtener una lista de [ServerCertificateMetadata](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/ServerCertificateMetadata.html)objetos que puedes usar para obtener información sobre cada certificado.

Los resultados pueden aparecer truncados; si el método `ListServerCertificateResponse` del objeto `isTruncated` devuelve `true`, llame al método `ListServerCertificatesResponse` del objeto `marker` y use el marcador para crear una nueva solicitud. Utilice la nueva solicitud para llamar de nuevo a `listServerCertificates` para obtener el siguiente lote de resultados.

 **Importaciones** 

```
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.services.iam.model.ListServerCertificatesRequest;
import software.amazon.awssdk.services.iam.model.ListServerCertificatesResponse;
import software.amazon.awssdk.services.iam.model.ServerCertificateMetadata;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
```

 **Código** 

```
    public static void listCertificates(IamClient iam) {

        try {
            boolean done = false;
            String newMarker = null;

            while(!done) {
              ListServerCertificatesResponse response;

            if (newMarker == null) {
                ListServerCertificatesRequest request =
                        ListServerCertificatesRequest.builder().build();
                response = iam.listServerCertificates(request);
            } else {
                ListServerCertificatesRequest request =
                        ListServerCertificatesRequest.builder()
                                .marker(newMarker).build();
                response = iam.listServerCertificates(request);
            }

            for(ServerCertificateMetadata metadata :
                    response.serverCertificateMetadataList()) {
                System.out.printf("Retrieved server certificate %s",
                        metadata.serverCertificateName());
            }

            if(!response.isTruncated()) {
                done = true;
            } else {
                newMarker = response.marker();
            }
        }

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/iam/src/main/java/com/example/iam/ListServerCertificates.java) en GitHub.

## Actualizar un certificado de servidor
<a name="update-a-server-certificate"></a>

Puede actualizar el nombre o la ruta de un certificado IamClient de servidor llamando al `updateServerCertificate` método. Se necesita un conjunto de [UpdateServerCertificateRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/UpdateServerCertificateRequest.html)objetos con el nombre actual del certificado de servidor y un nombre nuevo o una nueva ruta para su uso.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.IamException;
import software.amazon.awssdk.services.iam.model.UpdateServerCertificateRequest;
import software.amazon.awssdk.services.iam.model.UpdateServerCertificateResponse;
```

 **Código** 

```
    public static void updateCertificate(IamClient iam, String curName, String newName) {

        try {
            UpdateServerCertificateRequest request =
                UpdateServerCertificateRequest.builder()
                        .serverCertificateName(curName)
                        .newServerCertificateName(newName)
                        .build();

            UpdateServerCertificateResponse response =
                iam.updateServerCertificate(request);


            System.out.printf("Successfully updated server certificate to name %s",
                newName);

        } catch (IamException e) {
             System.err.println(e.awsErrorDetails().errorMessage());
             System.exit(1);
        }
     }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/iam/src/main/java/com/example/iam/UpdateServerCertificate.java) en GitHub.

## Eliminar un certificado de servidor
<a name="delete-a-server-certificate"></a>

Para eliminar un certificado de servidor, llame al IamClient `deleteServerCertificate` método [DeleteServerCertificateRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/iam/model/DeleteServerCertificateRequest.html)que contenga el nombre del certificado.

 **Importaciones** 

```
import software.amazon.awssdk.services.iam.model.DeleteServerCertificateRequest;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.iam.IamClient;
import software.amazon.awssdk.services.iam.model.IamException;
```

 **Código** 

```
    public static void deleteCert(IamClient iam,String certName ) {

        try {
            DeleteServerCertificateRequest request =
                DeleteServerCertificateRequest.builder()
                        .serverCertificateName(certName)
                        .build();

            iam.deleteServerCertificate(request);
            System.out.println("Successfully deleted server certificate " +
                    certName);

        } catch (IamException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/0b1785e42949ebf959eaa0f0da4dc2a48f92ea25/javav2/example_code/iam/src/main/java/com/example/iam/DeleteServerCertificate.java) en GitHub.

## Más información
<a name="more-information"></a>
+  [Cómo trabajar con certificados de servidor](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_server-certs.html) en la Guía IAM del usuario
+  [GetServerCertificate](https://docs.aws.amazon.com/IAM/latest/APIReference/API_GetServerCertificate.html)en la referencia IAM de la API
+  [ListServerCertificates](https://docs.aws.amazon.com/IAM/latest/APIReference/API_ListServerCertificates.html)en la referencia IAM de la API
+  [UpdateServerCertificate](https://docs.aws.amazon.com/IAM/latest/APIReference/API_UpdateServerCertificate.html)en la referencia IAM de la API
+  [DeleteServerCertificate](https://docs.aws.amazon.com/IAM/latest/APIReference/API_DeleteServerCertificate.html)en la referencia IAM de la API
+  [AWS Certificate Manager Guía del usuario](https://docs.aws.amazon.com/acm/latest/userguide/) 

# Trabajar con de Kinesis
<a name="examples-kinesis"></a>

En esta sección se proporcionan ejemplos de programación [Amazon Kinesis](https://docs.aws.amazon.com/kinesis/) mediante AWS SDK para Java 2.x.

Para obtener más información sobre Kinesis, consulte [la Guía para desarrolladores de Amazon Kinesis.](https://docs.aws.amazon.com/streams/latest/dev/introduction.html)

Los siguientes ejemplos incluyen únicamente el código necesario para demostrar cada técnica. El [código de ejemplo completo está disponible en GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2). Desde allí, puede descargar un único archivo de código fuente o clonar el repositorio localmente para obtener todos los ejemplos para compilarlos y ejecutarlos.

**Topics**
+ [Suscripción a Amazon Kinesis Data Streams](examples-kinesis-stream.md)

# Suscripción a Amazon Kinesis Data Streams
<a name="examples-kinesis-stream"></a>

Los siguientes ejemplos muestran cómo recuperar y procesar datos de flujos de datos de Amazon Kinesis con el método `subscribeToShard`. Kinesis Data Streams ahora emplea la característica de distribución ramificada mejorada y una API de recuperación de datos de HTTP/2 de baja latencia, que permite a los desarrolladores ejecutar fácilmente varias aplicaciones de baja latencia y de alto desempeño en la misma secuencia de datos de Kinesis.

## Configuración
<a name="set-up"></a>

En primer lugar, cree un cliente de Kinesis asíncrono y un objeto [SubscribeToShardRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/kinesis/model/SubscribeToShardRequest.html). Estos objetos se utilizan en cada uno de los siguientes ejemplos para suscribirse a eventos de Kinesis.

 **Importaciones** 

```
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicInteger;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import software.amazon.awssdk.core.async.SdkPublisher;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.kinesis.KinesisAsyncClient;
import software.amazon.awssdk.services.kinesis.model.ShardIteratorType;
import software.amazon.awssdk.services.kinesis.model.SubscribeToShardEvent;
import software.amazon.awssdk.services.kinesis.model.SubscribeToShardEventStream;
import software.amazon.awssdk.services.kinesis.model.SubscribeToShardRequest;
import software.amazon.awssdk.services.kinesis.model.SubscribeToShardResponse;
import software.amazon.awssdk.services.kinesis.model.SubscribeToShardResponseHandler;
```

 **Código de** 

```
        Region region = Region.US_EAST_1;
        KinesisAsyncClient client = KinesisAsyncClient.builder()
        .region(region)
        .build();

        SubscribeToShardRequest request = SubscribeToShardRequest.builder()
                .consumerARN(CONSUMER_ARN)
                .shardId("arn:aws:kinesis:us-east-1:111122223333:stream/StockTradeStream")
                .startingPosition(s -> s.type(ShardIteratorType.LATEST)).build();
```

## Usar la interfaz del compilador
<a name="use-the-builder-interface"></a>

Puede utilizar el método `builder` para simplificar la creación de [SubscribeToShardResponseHandler](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/kinesis/model/SubscribeToShardResponseHandler.html).

Con el creador, puede configurar cada devolución de llamada de ciclo de vida con una llamada de método en lugar de implementar la interfaz completa.

 **Código de** 

```
    private static CompletableFuture<Void> responseHandlerBuilder(KinesisAsyncClient client, SubscribeToShardRequest request) {
        SubscribeToShardResponseHandler responseHandler = SubscribeToShardResponseHandler
                .builder()
                .onError(t -> System.err.println("Error during stream - " + t.getMessage()))
                .onComplete(() -> System.out.println("All records stream successfully"))
                // Must supply some type of subscriber
                .subscriber(e -> System.out.println("Received event - " + e))
                .build();
        return client.subscribeToShard(request, responseHandler);
    }
```

Para tener más control sobre publicador, puede usar el método `publisherTransformer` para personalizar el publicador.

 **Código de** 

```
    private static CompletableFuture<Void> responseHandlerBuilderPublisherTransformer(KinesisAsyncClient client, SubscribeToShardRequest request) {
        SubscribeToShardResponseHandler responseHandler = SubscribeToShardResponseHandler
                .builder()
                .onError(t -> System.err.println("Error during stream - " + t.getMessage()))
                .publisherTransformer(p -> p.filter(e -> e instanceof SubscribeToShardEvent).limit(100))
                .subscriber(e -> System.out.println("Received event - " + e))
                .build();
        return client.subscribeToShard(request, responseHandler);
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/kinesis/src/main/java/com/example/kinesis/KinesisStreamEx.java) en GitHub.

## Usa un controlador de respuesta personalizado
<a name="use-a-custom-response-handler"></a>

Para tener un control completo del suscriptor y el publicador, implemente la interfaz de `SubscribeToShardResponseHandler`.

En este ejemplo, se implementa el método `onEventStream`, lo que le permite el acceso completo al publicador. Esto demuestra cómo transformar el publicador en registros de eventos para su impresión por parte del suscriptor.

 **Código de** 

```
    private static CompletableFuture<Void> responseHandlerBuilderClassic(KinesisAsyncClient client, SubscribeToShardRequest request) {
        SubscribeToShardResponseHandler responseHandler = new SubscribeToShardResponseHandler() {

            @Override
            public void responseReceived(SubscribeToShardResponse response) {
                System.out.println("Receieved initial response");
            }

            @Override
            public void onEventStream(SdkPublisher<SubscribeToShardEventStream> publisher) {
                publisher
                        // Filter to only SubscribeToShardEvents
                        .filter(SubscribeToShardEvent.class)
                        // Flat map into a publisher of just records
                        .flatMapIterable(SubscribeToShardEvent::records)
                        // Limit to 1000 total records
                        .limit(1000)
                        // Batch records into lists of 25
                        .buffer(25)
                        // Print out each record batch
                        .subscribe(batch -> System.out.println("Record Batch - " + batch));
            }

            @Override
            public void complete() {
                System.out.println("All records stream successfully");
            }

            @Override
            public void exceptionOccurred(Throwable throwable) {
                System.err.println("Error during stream - " + throwable.getMessage());
            }
        };
        return client.subscribeToShard(request, responseHandler);
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/kinesis/src/main/java/com/example/kinesis/KinesisStreamEx.java) en GitHub.

## Usa la interfaz de visitante
<a name="use-the-visitor-interface"></a>

Puede utilizar un objeto [Visitor](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/kinesis/model/SubscribeToShardResponseHandler.Visitor.html) para suscribirse a eventos específicos que le interese ver.

 **Código de** 

```
    private static CompletableFuture<Void> responseHandlerBuilderVisitorBuilder(KinesisAsyncClient client, SubscribeToShardRequest request) {
        SubscribeToShardResponseHandler.Visitor visitor = SubscribeToShardResponseHandler.Visitor
                .builder()
                .onSubscribeToShardEvent(e -> System.out.println("Received subscribe to shard event " + e))
                .build();
        SubscribeToShardResponseHandler responseHandler = SubscribeToShardResponseHandler
                .builder()
                .onError(t -> System.err.println("Error during stream - " + t.getMessage()))
                .subscriber(visitor)
                .build();
        return client.subscribeToShard(request, responseHandler);
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/kinesis/src/main/java/com/example/kinesis/KinesisStreamEx.java) en GitHub.

## Usa un suscriptor personalizado
<a name="use-a-custom-subscriber"></a>

También puede implementar su propio suscriptor personalizado para suscribirse a la secuencia.

Este fragmento de código muestra un ejemplo de suscriptor.

 **Código de** 

```
    private static class MySubscriber implements Subscriber<SubscribeToShardEventStream> {

        private Subscription subscription;
        private AtomicInteger eventCount = new AtomicInteger(0);

        @Override
        public void onSubscribe(Subscription subscription) {
            this.subscription = subscription;
            this.subscription.request(1);
        }

        @Override
        public void onNext(SubscribeToShardEventStream shardSubscriptionEventStream) {
            System.out.println("Received event " + shardSubscriptionEventStream);
            if (eventCount.incrementAndGet() >= 100) {
                // You can cancel the subscription at any time if you wish to stop receiving events.
                subscription.cancel();
            }
            subscription.request(1);
        }

        @Override
        public void onError(Throwable throwable) {
            System.err.println("Error occurred while stream - " + throwable.getMessage());
        }

        @Override
        public void onComplete() {
            System.out.println("Finished streaming all events");
        }
    }
```

Puede pasar el suscriptor personalizado al método `subscribe`, como se muestra en el siguiente fragmento de código.

 **Código de** 

```
    private static CompletableFuture<Void> responseHandlerBuilderSubscriber(KinesisAsyncClient client, SubscribeToShardRequest request) {
        SubscribeToShardResponseHandler responseHandler = SubscribeToShardResponseHandler
                .builder()
                .onError(t -> System.err.println("Error during stream - " + t.getMessage()))
                .subscriber(MySubscriber::new)
                .build();
        return client.subscribeToShard(request, responseHandler);
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/kinesis/src/main/java/com/example/kinesis/KinesisStreamEx.java) en GitHub.

## Escribir registros de datos en una secuencia de datos de Kinesis
<a name="write-data-records-into-a-kinesis-data-stream"></a>

Puede utilizar el objeto [KinesisClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/kinesis/KinesisClient.html) para escribir registros de datos en una secuencia de datos de Kinesis mediante el método `putRecords`. Para invocar correctamente este método, cree un objeto [PutRecordsRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/kinesis/model/PutRecordsRequest.html). Debe pasar el nombre de la secuencia de datos al método `streamName`. También debe pasar los datos mediante el método `putRecords` (como se muestra en el siguiente ejemplo de código).

 **Importaciones** 

```
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.kinesis.KinesisClient;
import software.amazon.awssdk.services.kinesis.model.PutRecordRequest;
import software.amazon.awssdk.services.kinesis.model.KinesisException;
import software.amazon.awssdk.services.kinesis.model.DescribeStreamRequest;
import software.amazon.awssdk.services.kinesis.model.DescribeStreamResponse;
```

En el siguiente ejemplo de código Java, observe que el objeto **StockTrade** se utiliza como los datos para escribir en la secuencia de datos de Kinesis. Antes de ejecutar este ejemplo, asegúrese de haber creado la secuencia de datos.

 **Código de** 

```
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.kinesis.KinesisClient;
import software.amazon.awssdk.services.kinesis.model.PutRecordRequest;
import software.amazon.awssdk.services.kinesis.model.KinesisException;
import software.amazon.awssdk.services.kinesis.model.DescribeStreamRequest;
import software.amazon.awssdk.services.kinesis.model.DescribeStreamResponse;

/**
 * Before running this Java V2 code example, set up your development
 * environment, including your credentials.
 *
 * For more information, see the following documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 */
public class StockTradesWriter {
    public static void main(String[] args) {
        final String usage = """

                Usage:
                    <streamName>

                Where:
                    streamName - The Amazon Kinesis data stream to which records are written (for example, StockTradeStream)
                """;

        if (args.length != 1) {
            System.out.println(usage);
            System.exit(1);
        }

        String streamName = args[0];
        Region region = Region.US_EAST_1;
        KinesisClient kinesisClient = KinesisClient.builder()
                .region(region)
                .build();

        // Ensure that the Kinesis Stream is valid.
        validateStream(kinesisClient, streamName);
        setStockData(kinesisClient, streamName);
        kinesisClient.close();
    }

    public static void setStockData(KinesisClient kinesisClient, String streamName) {
        try {
            // Repeatedly send stock trades with a 100 milliseconds wait in between.
            StockTradeGenerator stockTradeGenerator = new StockTradeGenerator();

            // Put in 50 Records for this example.
            int index = 50;
            for (int x = 0; x < index; x++) {
                StockTrade trade = stockTradeGenerator.getRandomTrade();
                sendStockTrade(trade, kinesisClient, streamName);
                Thread.sleep(100);
            }

        } catch (KinesisException | InterruptedException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
        System.out.println("Done");
    }

    private static void sendStockTrade(StockTrade trade, KinesisClient kinesisClient,
            String streamName) {
        byte[] bytes = trade.toJsonAsBytes();

        // The bytes could be null if there is an issue with the JSON serialization by
        // the Jackson JSON library.
        if (bytes == null) {
            System.out.println("Could not get JSON bytes for stock trade");
            return;
        }

        System.out.println("Putting trade: " + trade);
        PutRecordRequest request = PutRecordRequest.builder()
                .partitionKey(trade.getTickerSymbol()) // We use the ticker symbol as the partition key, explained in
                                                       // the Supplemental Information section below.
                .streamName(streamName)
                .data(SdkBytes.fromByteArray(bytes))
                .build();

        try {
            kinesisClient.putRecord(request);
        } catch (KinesisException e) {
            System.err.println(e.getMessage());
        }
    }

    private static void validateStream(KinesisClient kinesisClient, String streamName) {
        try {
            DescribeStreamRequest describeStreamRequest = DescribeStreamRequest.builder()
                    .streamName(streamName)
                    .build();

            DescribeStreamResponse describeStreamResponse = kinesisClient.describeStream(describeStreamRequest);

            if (!describeStreamResponse.streamDescription().streamStatus().toString().equals("ACTIVE")) {
                System.err.println("Stream " + streamName + " is not active. Please wait a few moments and try again.");
                System.exit(1);
            }

        } catch (KinesisException e) {
            System.err.println("Error found while describing the stream " + streamName);
            System.err.println(e);
            System.exit(1);
        }
    }
}
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/master/javav2/example_code/kinesis/src/main/java/com/example/kinesis/StockTradesWriter.java) en GitHub.

## Usar una biblioteca de terceros
<a name="use-a-third-party-library"></a>

Puede utilizar otras bibliotecas de terceros en lugar de implementar un suscriptor personalizado. Este ejemplo muestra el uso de la implementación RxJava, pero puede utilizar cualquier biblioteca que implemente las interfaces de secuencias reactivas. Consulte la [página wiki de RxJava en Github](https://github.com/ReactiveX/RxJava/wiki) para obtener más información acerca de dicha biblioteca.

Para utilizar la biblioteca, añádala como una dependencia. Si está utilizando Maven, el ejemplo muestra el fragmento de POM que se debe utilizar.

 **Entrada de POM** 

```
<dependency>
 <groupId>io.reactivex.rxjava2</groupId>
 <artifactId>rxjava</artifactId>
 <version>2.2.21</version>
</dependency>
```

 **Importaciones** 

```
import java.net.URI;
import java.util.concurrent.CompletableFuture;

import io.reactivex.Flowable;
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
import software.amazon.awssdk.core.async.SdkPublisher;
import software.amazon.awssdk.http.Protocol;
import software.amazon.awssdk.http.SdkHttpConfigurationOption;
import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.kinesis.KinesisAsyncClient;
import software.amazon.awssdk.services.kinesis.model.ShardIteratorType;
import software.amazon.awssdk.services.kinesis.model.StartingPosition;
import software.amazon.awssdk.services.kinesis.model.SubscribeToShardEvent;
import software.amazon.awssdk.services.kinesis.model.SubscribeToShardRequest;
import software.amazon.awssdk.services.kinesis.model.SubscribeToShardResponseHandler;
import software.amazon.awssdk.utils.AttributeMap;
```

En este ejemplo se utiliza RxJava en el método de ciclo de vida `onEventStream`. Esto le ofrece acceso completo al publicador, que se puede utilizar para crear un Rx Flowable.

 **Código de** 

```
        SubscribeToShardResponseHandler responseHandler = SubscribeToShardResponseHandler
            .builder()
            .onError(t -> System.err.println("Error during stream - " + t.getMessage()))
            .onEventStream(p -> Flowable.fromPublisher(p)
                                        .ofType(SubscribeToShardEvent.class)
                                        .flatMapIterable(SubscribeToShardEvent::records)
                                        .limit(1000)
                                        .buffer(25)
                                        .subscribe(e -> System.out.println("Record batch = " + e)))
            .build();
```

También puede usar el método `publisherTransformer` con el publicador `Flowable`. Debe adaptar el publicador `Flowable` a un *SdkPublisher*, tal y como se muestra en el siguiente ejemplo.

 **Código de** 

```
        SubscribeToShardResponseHandler responseHandler = SubscribeToShardResponseHandler
            .builder()
            .onError(t -> System.err.println("Error during stream - " + t.getMessage()))
            .publisherTransformer(p -> SdkPublisher.adapt(Flowable.fromPublisher(p).limit(100)))
            .build();
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/kinesis/src/main/java/com/example/kinesis/KinesisStreamRxJavaEx.java) en GitHub.

## Más información
<a name="more-information"></a>
+  [SubscribeToShardEvent](https://docs.aws.amazon.com/kinesis/latest/APIReference/API_SubscribeToShardEvent.html) en la referencia de la API de Amazon Kinesis
+  [SubscribeToShard](https://docs.aws.amazon.com/kinesis/latest/APIReference/API_SubscribeToShard.html) en la referencia de la API de Amazon Kinesis

# Invocar, enumerar y eliminar funciones AWS Lambda
<a name="examples-lambda"></a>

En esta sección se proporcionan ejemplos de programación con el cliente Lambda de servicio mediante la versión AWS SDK para Java 2.x.

**Topics**
+ [Invocar una función Lambda](#invoke-function)
+ [Enumerar funciones de Lambda](#list-function)
+ [Eliminación de una función de Lambda](#delete-function)

## Invocar una función Lambda
<a name="invoke-function"></a>

Para invocar una Lambda función, cree un [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/LambdaClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/LambdaClient.html)objeto e invoque su método. `invoke` Crea un [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/model/InvokeRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/model/InvokeRequest.html)objeto para especificar información adicional, como el nombre de la función y la carga útil que se transferirá a la función. Lambda Los nombres de las funciones aparecen como *arn:aws:lambda:us-east- 1:123456789012:function:*. HelloFunction Puede recuperar el valor viendo la función en la Consola de administración de AWS.

Para pasar datos de carga útil a una función, cree un objeto [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/SdkBytes.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/SdkBytes.html) que contenga información. Por ejemplo, en el siguiente ejemplo de código, observe los datos JSON pasados a la función Lambda .

 **Importaciones** 

```
import software.amazon.awssdk.services.lambda.LambdaClient;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.lambda.model.InvokeRequest;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.services.lambda.model.InvokeResponse;
import software.amazon.awssdk.services.lambda.model.LambdaException;
```

 **Código** 

El siguiente ejemplo de código muestra cómo invocar una función. Lambda 

```
    public static void invokeFunction(LambdaClient awsLambda, String functionName) {

         InvokeResponse res = null ;
        try {
            //Need a SdkBytes instance for the payload
            String json = "{\"Hello \":\"Paris\"}";
            SdkBytes payload = SdkBytes.fromUtf8String(json) ;

            //Setup an InvokeRequest
            InvokeRequest request = InvokeRequest.builder()
                    .functionName(functionName)
                    .payload(payload)
                    .build();

            res = awsLambda.invoke(request);
            String value = res.payload().asUtf8String() ;
            System.out.println(value);

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

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/lambda/src/main/java/com/example/lambda/LambdaInvoke.java) en. GitHub

## Enumerar funciones de Lambda
<a name="list-function"></a>

Construya un objeto de `[Lambda Client](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/LambdaClient.html)` e invoque su método `listFunctions`. Este método devuelve un objeto [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/model/ListFunctionsResponse.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/model/ListFunctionsResponse.html). Puede invocar el método `functions` de este objeto para obtener una lista de objetos [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/model/FunctionConfiguration.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/model/FunctionConfiguration.html). Puede recorrer la lista en iteración para recuperar información sobre las funciones. Por ejemplo, el siguiente ejemplo de código Java muestra cómo obtener el nombre de cada función.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.lambda.LambdaClient;
import software.amazon.awssdk.services.lambda.model.LambdaException;
import software.amazon.awssdk.services.lambda.model.ListFunctionsResponse;
import software.amazon.awssdk.services.lambda.model.FunctionConfiguration;
import java.util.List;
```

 **Código** 

El siguiente ejemplo de código Java muestra cómo recuperar una lista de nombres de funciones de .

```
    public static void listFunctions(LambdaClient awsLambda) {

        try {
            ListFunctionsResponse functionResult = awsLambda.listFunctions();
            List<FunctionConfiguration> list = functionResult.functions();

            for (FunctionConfiguration config: list) {
                System.out.println("The function name is "+config.functionName());
            }

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

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/lambda/src/main/java/com/example/lambda/ListLambdaFunctions.java) en GitHub.

## Eliminación de una función de Lambda
<a name="delete-function"></a>

Construya un objeto de [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/LambdaClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/LambdaClient.html) e invoque su método `deleteFunction`. Cree un objeto [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/model/DeleteFunctionRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/lambda/model/DeleteFunctionRequest.html) y páselo al método `deleteFunction`. Este objeto contiene información como el nombre de la función que se va a eliminar. Los nombres de las funciones aparecen como *arn:aws:lambda:us-east- 1:123456789012:function:*. HelloFunction Puede recuperar el valor viendo la función en la Consola de administración de AWS.

 **Importaciones** 

```
import software.amazon.awssdk.services.lambda.LambdaClient;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.lambda.model.DeleteFunctionRequest;
import software.amazon.awssdk.services.lambda.model.LambdaException;
```

 **Código** 

El siguiente código de Java muestra cómo eliminar una función. Lambda 

```
    public static void deleteLambdaFunction(LambdaClient awsLambda, String functionName ) {
        try {
            DeleteFunctionRequest request = DeleteFunctionRequest.builder()
                    .functionName(functionName)
                    .build();

            awsLambda.deleteFunction(request);
            System.out.println("The "+functionName +" function was deleted");

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

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/f807d60010caf3d14fe4cd0801b842fb8e9511ca/javav2/example_code/lambda/src/main/java/com/example/lambda/DeleteFunction.java) en GitHub.

# Trabajo con Amazon S3
<a name="examples-s3"></a>

En esta sección se proporciona información básica para trabajar con Amazon S3 mediante AWS SDK for Java 2.x. Esta sección complementa los [ejemplos de Java v2 de Amazon S3](java_s3_code_examples.md) presentados en la sección *Ejemplos de código* de esta guía.

## Clientes S3 en el AWS SDK for Java 2.x
<a name="s3-clients"></a>

 AWS SDK for Java 2.x Proporciona diferentes tipos de clientes S3. La siguiente tabla muestra las diferencias y puede ayudarle a decidir qué es lo mejor para sus casos de uso.


**Diferentes tipos de clientes de Amazon S3**  

| Cliente de S3 | Descripción breve | Cuándo se debe usar | Limitación o inconveniente | 
| --- | --- | --- | --- | 
|  **AWS Cliente S3 basado en CRT** [Interfaz: S3 AsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html) Constructor: [S3 CrtAsyncClientBuilder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/examples-s3.html) Consulte [Utilice un cliente S3 de alto rendimiento: cliente S3 basado en AWS CRT](crt-based-s3-client.md).  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/examples-s3.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/examples-s3.html)  | 
|  **Cliente asincrónico S3 basado en Java *con* multiparte habilitada** Interfaz: [S3 AsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html) Constructor: [S3 AsyncClientBuilder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClientBuilder.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/examples-s3.html) Consulte [Configuración del cliente asincrónico S3 basado en Java para usar transferencias paralelas](s3-async-client-multipart.md).  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/examples-s3.html)  | Tiene menos rendimiento que el cliente S3 basado en AWS CRT. | 
|  **Cliente asincrónico de S3 basado en Java *sin* multiparte habilitada** [Interfaz: S3 AsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html) Constructor: [S3 AsyncClientBuilder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClientBuilder.html) |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/examples-s3.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/examples-s3.html)  |  Sin optimización del rendimiento  | 
|  **Cliente asincrónico de S3 basado en Java** Interfaz: [S3Client](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html) Constructor: [S3 ClientBuilder](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3ClientBuilder.html) |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/examples-s3.html)  |  [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/examples-s3.html)  |  Sin optimización del rendimiento  | 

**nota**  
A partir de la versión 2.18.x, AWS SDK for Java 2.x utiliza un [direccionamiento de tipo alojamiento virtual al incluir una anulación de punto final](https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html#virtual-hosted-style-access). Esto se aplica siempre que el nombre del bucket sea una etiqueta DNS válida.   
Llame al método [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3BaseClientBuilder.html#forcePathStyle(java.lang.Boolean](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3BaseClientBuilder.html#forcePathStyle(java.lang.Boolean) con `true` en su constructor de clientes para forzar al cliente a utilizar el direccionamiento estilo ruta para los buckets.  
En el siguiente ejemplo se muestra un cliente de servicio configurado con una anulación de punto de conexión y utilizando direccionamiento estilo ruta.  

```
S3Client client = S3Client.builder()
                          .region(Region.US_WEST_2)
                          .endpointOverride(URI.create("https://s3.us-west-2.amazonaws.com"))
                          .forcePathStyle(true)
                          .build();
```

**Topics**
+ [Clientes de S3 en el SDK](#s3-clients)
+ [Carga de flujos en S3](best-practices-s3-uploads.md)
+ [Prefirmado URLs](examples-s3-presign.md)
+ [Acceso entre regiones](s3-cross-region.md)
+ [Protección de la integridad de los datos con sumas de comprobación](s3-checksums.md)
+ [Utilizar un cliente S3 de alto rendimiento](crt-based-s3-client.md)
+ [Configuración de la compatibilidad con la transferencia paralela](s3-async-client-multipart.md)
+ [Transferir archivos y directorios](transfer-manager.md)
+ [Notificaciones de eventos de S3](examples-s3-event-notifications.md)

# Carga de transmisiones a Amazon S3 mediante AWS SDK for Java 2.x
<a name="best-practices-s3-uploads"></a>

Cuando se utiliza un flujo para cargar contenido en S3 utilizando [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#putObject(software.amazon.awssdk.services.s3.model.PutObjectRequest,software.amazon.awssdk.core.sync.RequestBody)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#putObject(software.amazon.awssdk.services.s3.model.PutObjectRequest,software.amazon.awssdk.core.sync.RequestBody)) o [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#uploadPart(software.amazon.awssdk.services.s3.model.UploadPartRequest,software.amazon.awssdk.core.sync.RequestBody)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#uploadPart(software.amazon.awssdk.services.s3.model.UploadPartRequest,software.amazon.awssdk.core.sync.RequestBody)), se utiliza una clase de fábrica `RequestBody` para que la API sincrónica suministre el flujo. Para la API asincrónica, `AsyncRequestBody` es la clase de fábrica equivalente.

## ¿Qué métodos cargan flujos?
<a name="s3-stream-upload-methods"></a>

En la API sincrónica, puede usar los siguientes métodos de fábrica de `RequestBody` para suministrar el flujo:
+ `[fromInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/sync/RequestBody.html#fromInputStream(java.io.InputStream,long))(InputStream inputStream, long contentLength)`

  `[fromContentProvider](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/sync/RequestBody.html#fromContentProvider(software.amazon.awssdk.http.ContentStreamProvider,long,java.lang.String))(ContentStreamProvider provider, long contentLength, String mimeType)`
  + El `ContentStreamProvider` tiene el método de fábrica `fromInputStream(InputStream inputStream)`
+ `[fromContentProvider](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/sync/RequestBody.html#fromContentProvider(software.amazon.awssdk.http.ContentStreamProvider,java.lang.String))(ContentStreamProvider provider, String mimeType)`

En la API asincrónica, puede usar los siguientes métodos de fábrica de `AsyncRequestBody`.
+ `[fromInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncRequestBody.html#fromInputStream(java.io.InputStream,java.lang.Long,java.util.concurrent.ExecutorService))(InputStream inputStream, Long contentLength, ExecutorService executor)` 
+ `[fromInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncRequestBody.html#fromInputStream(software.amazon.awssdk.core.async.AsyncRequestBodyFromInputStreamConfiguration))(AsyncRequestBodyFromInputStreamConfiguration configuration)`
  + Utiliza el AsyncRequestBodyFromInputStreamConfiguration .Builder para suministrar la transmisión
+ `[fromInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncRequestBody.html#fromInputStream(java.util.function.Consumer))(Consumer<AsyncRequestBodyFromInputStreamConfiguration.Builder> configuration)`
+ `[forBlockingInputStream](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/AsyncRequestBody.html#forBlockingInputStream(java.lang.Long))(Long contentLength)`
  + El `[BlockingInputStreamAsyncRequestBody](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/async/BlockingInputStreamAsyncRequestBody.html)` resultante contiene el método `writeInputStream(InputStream inputStream)` que puede utilizar para proporcionar el flujo

## Realización de la carga
<a name="s3-upload-stream-perform"></a>

### Si conoce la longitud del flujo
<a name="s3-stream-upload-supply-content-length"></a>

Como puede ver en la firma de los métodos mostrados anteriormente, la mayoría de los métodos aceptan un parámetro de longitud del contenido. 

Si conoce la longitud del contenido en bytes, proporcione el valor exacto:

```
// Always provide the exact content length when it's available.
long contentLength = 1024; // Exact size in bytes.
s3Client.putObject(req -> req
    .bucket("amzn-s3-demo-bucket")
    .key("my-key"),
RequestBody.fromInputStream(inputStream, contentLength));
```

**aviso**  
 Al cargar desde un flujo de entrada, si la longitud del contenido especificada no coincide con el recuento real de bytes, es posible que se produzca lo siguiente:  
Objetos truncados si la longitud especificada es demasiado pequeña
Errores de carga o conexiones bloqueadas si la longitud especificada es demasiado grande

### Si no conoce la longitud del flujo
<a name="s3-stream-upload-unknown-length"></a>

#### Uso de la API sincrónica
<a name="s3-upload-unknown-sync-client"></a>

Usa: `fromContentProvider(ContentStreamProvider provider, String mimeType)`

```
public PutObjectResponse syncClient_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3Client s3Client = S3Client.create();

    RequestBody body = RequestBody.fromContentProvider(ContentStreamProvider.fromInputStream(inputStream), "text/plain");
    PutObjectResponse putObjectResponse = s3Client.putObject(b -> b.bucket(BUCKET_NAME).key(KEY_NAME), body);
    return putObjectResponse;
}
```

Dado que el SDK almacena en búfer de memoria todo el flujo para calcular la longitud del contenido, pueden producirse problemas de memoria con flujos grandes. Si necesita cargar flujos de gran tamaño con el cliente sincrónico, considere usar la API multiparte:

##### Cargue un flujo mediante la API de cliente sincrónica y la API multiparte
<a name="sync-multipart-upload-stream"></a>

```
public static void uploadStreamToS3(String bucketName, String key, InputStream inputStream) {
    // Create S3 client
    S3Client s3Client = S3Client.create();
    try {
        // Step 1: Initiate the multipart upload
        CreateMultipartUploadRequest createMultipartUploadRequest = CreateMultipartUploadRequest.builder()
                .bucket(bucketName)
                .key(key)
                .build();

        CreateMultipartUploadResponse createResponse = s3Client.createMultipartUpload(createMultipartUploadRequest);
        String uploadId = createResponse.uploadId();
        System.out.println("Started multipart upload with ID: " + uploadId);

        // Step 2: Upload parts
        List<CompletedPart> completedParts = new ArrayList<>();
        int partNumber = 1;
        byte[] buffer = new byte[PART_SIZE];
        int bytesRead;

        try {
            while ((bytesRead = readFullyOrToEnd(inputStream, buffer)) > 0) {
                // Create request to upload a part
                UploadPartRequest uploadPartRequest = UploadPartRequest.builder()
                        .bucket(bucketName)
                        .key(key)
                        .uploadId(uploadId)
                        .partNumber(partNumber)
                        .build();

                // If we didn't read a full buffer, create a properly sized byte array
                RequestBody requestBody;
                if (bytesRead < PART_SIZE) {
                    byte[] lastPartBuffer = new byte[bytesRead];
                    System.arraycopy(buffer, 0, lastPartBuffer, 0, bytesRead);
                    requestBody = RequestBody.fromBytes(lastPartBuffer);
                } else {
                    requestBody = RequestBody.fromBytes(buffer);
                }

                // Upload the part and save the response's ETag
                UploadPartResponse uploadPartResponse = s3Client.uploadPart(uploadPartRequest, requestBody);
                CompletedPart part = CompletedPart.builder()
                        .partNumber(partNumber)
                        .eTag(uploadPartResponse.eTag())
                        .build();
                completedParts.add(part);

                System.out.println("Uploaded part " + partNumber + " with size " + bytesRead + " bytes");
                partNumber++;
            }

            // Step 3: Complete the multipart upload
            CompletedMultipartUpload completedMultipartUpload = CompletedMultipartUpload.builder()
                    .parts(completedParts)
                    .build();

            CompleteMultipartUploadRequest completeRequest = CompleteMultipartUploadRequest.builder()
                    .bucket(bucketName)
                    .key(key)
                    .uploadId(uploadId)
                    .multipartUpload(completedMultipartUpload)
                    .build();

            CompleteMultipartUploadResponse completeResponse = s3Client.completeMultipartUpload(completeRequest);
            System.out.println("Multipart upload completed. Object URL: " + completeResponse.location());

        } catch (Exception e) {
            // If an error occurs, abort the multipart upload
            System.err.println("Error during multipart upload: " + e.getMessage());
            AbortMultipartUploadRequest abortRequest = AbortMultipartUploadRequest.builder()
                    .bucket(bucketName)
                    .key(key)
                    .uploadId(uploadId)
                    .build();
            s3Client.abortMultipartUpload(abortRequest);
            System.err.println("Multipart upload aborted");
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                System.err.println("Error closing input stream: " + e.getMessage());
            }
        }
    } finally {
        s3Client.close();
    }
}

/**
 * Reads from the input stream into the buffer, attempting to fill the buffer completely
 * or until the end of the stream is reached.
 *
 * @param inputStream the input stream to read from
 * @param buffer      the buffer to fill
 * @return the number of bytes read, or -1 if the end of the stream is reached before any bytes are read
 * @throws IOException if an I/O error occurs
 */
private static int readFullyOrToEnd(InputStream inputStream, byte[] buffer) throws IOException {
    int totalBytesRead = 0;
    int bytesRead;

    while (totalBytesRead < buffer.length) {
        bytesRead = inputStream.read(buffer, totalBytesRead, buffer.length - totalBytesRead);
        if (bytesRead == -1) {
            break; // End of stream
        }
        totalBytesRead += bytesRead;
    }

    return totalBytesRead > 0 ? totalBytesRead : -1;
}
```

**nota**  
Para la mayoría de los casos de uso, recomendamos utilizar la API de cliente asincrónica para los flujos de tamaño desconocido. Este método permite transferencias paralelas y ofrece una interfaz de programación más sencilla, ya que el SDK controla la segmentación de flujos en fragmentos multiparte si el flujo es grande.   
Tanto el cliente asincrónico estándar de S3 con mutiparte habilitada como el cliente de S3 basado en AWS CRT implementan este método. Mostramos ejemplos de este método en la siguiente sección.

#### Uso de la API asincrónica
<a name="s3-stream-upload-unknown-async-client"></a>

Puede proporcionar `null` para el argumento `contentLength` a `fromInputStream(InputStream inputStream, Long contentLength, ExecutorService executor)`

**Example utilizando el cliente asíncrono AWS basado en CRT:**  

```
public PutObjectResponse crtClient_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3AsyncClient s3AsyncClient = S3AsyncClient.crtCreate();
    ExecutorService executor = Executors.newSingleThreadExecutor();
    AsyncRequestBody body = AsyncRequestBody.fromInputStream(inputStream, null, executor);  // 'null' indicates that the
                                                                                            // content length is unknown.
    CompletableFuture<PutObjectResponse> responseFuture =
            s3AsyncClient.putObject(r -> r.bucket(bucketName).key(key), body)
                    .exceptionally(e -> {
                        if (e != null){
                            logger.error(e.getMessage(), e);
                        }
                        return null;
                    });

    PutObjectResponse response = responseFuture.join(); // Wait for the response.
    executor.shutdown();
    return response;
}
```

**Example utilizando el cliente asincrónico estándar con multiparte habilitada:**  

```
public PutObjectResponse asyncClient_multipart_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3AsyncClient s3AsyncClient = S3AsyncClient.builder().multipartEnabled(true).build();
    ExecutorService executor = Executors.newSingleThreadExecutor();
    AsyncRequestBody body = AsyncRequestBody.fromInputStream(inputStream, null, executor); // 'null' indicates that the
                                                                                           // content length is unknown.
    CompletableFuture<PutObjectResponse> responseFuture =
            s3AsyncClient.putObject(r -> r.bucket(bucketName).key(key), body)
                    .exceptionally(e -> {
                        if (e != null) {
                            logger.error(e.getMessage(), e);
                        }
                        return null;
                    });

    PutObjectResponse response = responseFuture.join(); // Wait for the response.
    executor.shutdown();
    return response;
}
```

# Trabaja con Amazon S3 prefirmado URLs
<a name="examples-s3-presign"></a>

Los prefirmados URLs proporcionan acceso temporal a objetos privados de S3 sin necesidad de que los usuarios tengan AWS credenciales o permisos. 

Por ejemplo, supongamos que Alice tiene acceso a un objeto S3 y desea compartir temporalmente el acceso a ese objeto con Bob. Alice puede generar una solicitud GET prefirmada para compartir con Bob de forma que pueda descargar el objeto sin requerir acceso a las credenciales de Alice. Puede generar solicitudes prefirmadas URLs para HTTP GET y HTTP PUT.

## Genere una URL prefirmada para un objeto y, a continuación, descárguela (solicitud GET)
<a name="get-presignedobject"></a>

El siguiente ejemplo consta de dos partes.
+ Parte 1: Alice genera la URL prefirmada de un objeto.
+ Parte 2: Bob descarga el objeto mediante la URL prefirmada.

### Parte 1: Generar la URL
<a name="get-presigned-object-part1"></a>

Alice ya tiene un objeto en un bucket S3. Usa el siguiente código para generar una cadena URL que Bob puede usar en una solicitud GET posterior.

#### Importaciones
<a name="get-presigned-example-imports"></a>

```
import com.example.s3.util.PresignUrlUtils;
import org.slf4j.Logger;
import software.amazon.awssdk.http.HttpExecuteRequest;
import software.amazon.awssdk.http.HttpExecuteResponse;
import software.amazon.awssdk.http.SdkHttpClient;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.apache.ApacheHttpClient;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Exception;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest;
import software.amazon.awssdk.services.s3.presigner.model.PresignedGetObjectRequest;
import software.amazon.awssdk.utils.IoUtils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.UUID;
```

```
    /* Create a pre-signed URL to download an object in a subsequent GET request. */
    public String createPresignedGetUrl(String bucketName, String keyName) {
        try (S3Presigner presigner = S3Presigner.create()) {

            GetObjectRequest objectRequest = GetObjectRequest.builder()
                    .bucket(bucketName)
                    .key(keyName)
                    .build();

            GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder()
                    .signatureDuration(Duration.ofMinutes(10))  // The URL will expire in 10 minutes.
                    .getObjectRequest(objectRequest)
                    .build();

            PresignedGetObjectRequest presignedRequest = presigner.presignGetObject(presignRequest);
            logger.info("Presigned URL: [{}]", presignedRequest.url().toString());
            logger.info("HTTP method: [{}]", presignedRequest.httpRequest().method());

            return presignedRequest.url().toExternalForm();
        }
    }
```

### Parte 2: Descargar el objeto
<a name="get-presigned-object-part2"></a>

Bob usa una de las siguientes tres opciones de código para descargar el objeto. Como alternativa, podría usar un navegador para realizar la solicitud GET.

#### Usar JDK `HttpURLConnection` (desde la versión 1.1)
<a name="get-presigned-example-useHttpUrlConnection"></a>

```
    /* Use the JDK HttpURLConnection (since v1.1) class to do the download. */
    public byte[] useHttpUrlConnectionToGet(String presignedUrlString) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // Capture the response body to a byte array.

        try {
            URL presignedUrl = new URL(presignedUrlString);
            HttpURLConnection connection = (HttpURLConnection) presignedUrl.openConnection();
            connection.setRequestMethod("GET");
            // Download the result of executing the request.
            try (InputStream content = connection.getInputStream()) {
                IoUtils.copy(content, byteArrayOutputStream);
            }
            logger.info("HTTP response code is " + connection.getResponseCode());

        } catch (S3Exception | IOException e) {
            logger.error(e.getMessage(), e);
        }
        return byteArrayOutputStream.toByteArray();
    }
```

#### Usar JDK `HttpClient` (desde la v11)
<a name="get-presigned-example-useHttpClient"></a>

```
    /* Use the JDK HttpClient (since v11) class to do the download. */
    public byte[] useHttpClientToGet(String presignedUrlString) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // Capture the response body to a byte array.

        HttpRequest.Builder requestBuilder = HttpRequest.newBuilder();
        HttpClient httpClient = HttpClient.newHttpClient();
        try {
            URL presignedUrl = new URL(presignedUrlString);
            HttpResponse<InputStream> response = httpClient.send(requestBuilder
                            .uri(presignedUrl.toURI())
                            .GET()
                            .build(),
                    HttpResponse.BodyHandlers.ofInputStream());

            IoUtils.copy(response.body(), byteArrayOutputStream);

            logger.info("HTTP response code is " + response.statusCode());

        } catch (URISyntaxException | InterruptedException | IOException e) {
            logger.error(e.getMessage(), e);
        }
        return byteArrayOutputStream.toByteArray();
    }
```

#### Usar `SdkHttpClient` desde el SDK para Java
<a name="get-presigned-example-useSdkHttpClient"></a>

```
    /* Use the AWS SDK for Java SdkHttpClient class to do the download. */
    public byte[] useSdkHttpClientToGet(String presignedUrlString) {

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); // Capture the response body to a byte array.
        try {
            URL presignedUrl = new URL(presignedUrlString);
            SdkHttpRequest request = SdkHttpRequest.builder()
                    .method(SdkHttpMethod.GET)
                    .uri(presignedUrl.toURI())
                    .build();

            HttpExecuteRequest executeRequest = HttpExecuteRequest.builder()
                    .request(request)
                    .build();

            try (SdkHttpClient sdkHttpClient = ApacheHttpClient.create()) {
                HttpExecuteResponse response = sdkHttpClient.prepareRequest(executeRequest).call();
                response.responseBody().ifPresentOrElse(
                        abortableInputStream -> {
                            try {
                                IoUtils.copy(abortableInputStream, byteArrayOutputStream);
                            } catch (IOException e) {
                                throw new RuntimeException(e);
                            }
                        },
                        () -> logger.error("No response body."));

                logger.info("HTTP Response code is {}", response.httpResponse().statusCode());
            }
        } catch (URISyntaxException | IOException e) {
            logger.error(e.getMessage(), e);
        }
        return byteArrayOutputStream.toByteArray();
    }
```

Consulta el [ejemplo completo y [pruébalo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/test/java/com/example/s3/presignurl/GeneratePresignedGetUrlTests.java)](https://github.com/awsdocs/aws-doc-sdk-examples/blob/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/main/java/com/example/s3/GeneratePresignedGetUrlAndRetrieve.java). GitHub

## Genere una URL prefirmada para una carga y, a continuación, cargue un archivo (solicitud PUT)
<a name="put-presignedobject"></a>

El siguiente ejemplo consta de dos partes.
+ Parte 1: Alice genera la URL prefirmada para cargar un objeto.
+ Parte 2: Bob carga un archivo mediante la URL prefirmada.

### Parte 1: Generar la URL
<a name="put-presigned-object-part1"></a>

Alice ya tiene un bucket de S3. Usa el siguiente código para generar una cadena URL que Bob puede usar en una solicitud PUT posterior.

#### Importaciones
<a name="put-presigned-example-imports"></a>

```
import com.example.s3.util.PresignUrlUtils;
import org.slf4j.Logger;
import software.amazon.awssdk.core.internal.sync.FileContentStreamProvider;
import software.amazon.awssdk.http.HttpExecuteRequest;
import software.amazon.awssdk.http.HttpExecuteResponse;
import software.amazon.awssdk.http.SdkHttpClient;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.apache.ApacheHttpClient;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.S3Exception;
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
import software.amazon.awssdk.services.s3.presigner.model.PresignedPutObjectRequest;
import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.util.Map;
import java.util.UUID;
```

```
    /* Create a presigned URL to use in a subsequent PUT request */
    public String createPresignedUrl(String bucketName, String keyName, Map<String, String> metadata) {
        try (S3Presigner presigner = S3Presigner.create()) {

            PutObjectRequest objectRequest = PutObjectRequest.builder()
                    .bucket(bucketName)
                    .key(keyName)
                    .metadata(metadata)
                    .build();

            PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder()
                    .signatureDuration(Duration.ofMinutes(10))  // The URL expires in 10 minutes.
                    .putObjectRequest(objectRequest)
                    .build();


            PresignedPutObjectRequest presignedRequest = presigner.presignPutObject(presignRequest);
            String myURL = presignedRequest.url().toString();
            logger.info("Presigned URL to upload a file to: [{}]", myURL);
            logger.info("HTTP method: [{}]", presignedRequest.httpRequest().method());

            return presignedRequest.url().toExternalForm();
        }
    }
```

### Parte 2: Cargar un objeto de archivo
<a name="put-presigned-object-part2"></a>

Bob usa una de las siguientes tres opciones de código para cargar un archivo.

#### Usar JDK `HttpURLConnection` (desde la versión 1.1)
<a name="put-presigned-example-useHttpUrlConnection"></a>

```
    /* Use the JDK HttpURLConnection (since v1.1) class to do the upload. */
    public void useHttpUrlConnectionToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) {
        logger.info("Begin [{}] upload", fileToPut.toString());
        try {
            URL presignedUrl = new URL(presignedUrlString);
            HttpURLConnection connection = (HttpURLConnection) presignedUrl.openConnection();
            connection.setDoOutput(true);
            metadata.forEach((k, v) -> connection.setRequestProperty("x-amz-meta-" + k, v));
            connection.setRequestMethod("PUT");
            OutputStream out = connection.getOutputStream();

            try (RandomAccessFile file = new RandomAccessFile(fileToPut, "r");
                 FileChannel inChannel = file.getChannel()) {
                ByteBuffer buffer = ByteBuffer.allocate(8192); //Buffer size is 8k

                while (inChannel.read(buffer) > 0) {
                    buffer.flip();
                    for (int i = 0; i < buffer.limit(); i++) {
                        out.write(buffer.get());
                    }
                    buffer.clear();
                }
            } catch (IOException e) {
                logger.error(e.getMessage(), e);
            }

            out.close();
            connection.getResponseCode();
            logger.info("HTTP response code is " + connection.getResponseCode());

        } catch (S3Exception | IOException e) {
            logger.error(e.getMessage(), e);
        }
    }
```

#### Usar JDK `HttpClient` (desde la v11)
<a name="put-presigned-example-useHttpClient"></a>

```
    /* Use the JDK HttpClient (since v11) class to do the upload. */
    public void useHttpClientToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) {
        logger.info("Begin [{}] upload", fileToPut.toString());

        HttpRequest.Builder requestBuilder = HttpRequest.newBuilder();
        metadata.forEach((k, v) -> requestBuilder.header("x-amz-meta-" + k, v));

        HttpClient httpClient = HttpClient.newHttpClient();
        try {
            final HttpResponse<Void> response = httpClient.send(requestBuilder
                            .uri(new URL(presignedUrlString).toURI())
                            .PUT(HttpRequest.BodyPublishers.ofFile(Path.of(fileToPut.toURI())))
                            .build(),
                    HttpResponse.BodyHandlers.discarding());

            logger.info("HTTP response code is " + response.statusCode());

        } catch (URISyntaxException | InterruptedException | IOException e) {
            logger.error(e.getMessage(), e);
        }
    }
```

#### Usar `SdkHttpClient` desde el SDK para Java
<a name="put-presigned-example-useSdkHttpClient"></a>

```
    /* Use the AWS SDK for Java V2 SdkHttpClient class to do the upload. */
    public void useSdkHttpClientToPut(String presignedUrlString, File fileToPut, Map<String, String> metadata) {
        logger.info("Begin [{}] upload", fileToPut.toString());

        try {
            URL presignedUrl = new URL(presignedUrlString);

            SdkHttpRequest.Builder requestBuilder = SdkHttpRequest.builder()
                    .method(SdkHttpMethod.PUT)
                    .uri(presignedUrl.toURI());
            // Add headers
            metadata.forEach((k, v) -> requestBuilder.putHeader("x-amz-meta-" + k, v));
            // Finish building the request.
            SdkHttpRequest request = requestBuilder.build();

            HttpExecuteRequest executeRequest = HttpExecuteRequest.builder()
                    .request(request)
                    .contentStreamProvider(new FileContentStreamProvider(fileToPut.toPath()))
                    .build();

            try (SdkHttpClient sdkHttpClient = ApacheHttpClient.create()) {
                HttpExecuteResponse response = sdkHttpClient.prepareRequest(executeRequest).call();
                logger.info("Response code: {}", response.httpResponse().statusCode());
            }
        } catch (URISyntaxException | IOException e) {
            logger.error(e.getMessage(), e);
        }
    }
```

Consulta el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/main/java/com/example/s3/GeneratePresignedUrlAndPutFileWithMetadata.java) y [sigue probando](https://github.com/awsdocs/aws-doc-sdk-examples/blob/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/test/java/com/example/s3/presignurl/GeneratePresignedPutUrlTests.java) GitHub.

# Acceso entre regiones de Amazon S3
<a name="s3-cross-region"></a>

Cuando trabaja con buckets de Amazon Simple Storage Service (Amazon S3), normalmente sabe cuál es la Región de AWS del bucket. La región con la que trabaja se determina al crear el cliente S3. 

Sin embargo, a veces necesitará trabajar con un bucket específico, pero no sabe si se encuentra en la misma región que está configurada para el cliente S3. 

En lugar de hacer más llamadas para determinar la región del bucket, puede usar el SDK para habilitar el acceso a los buckets de S3 en distintas regiones.

## Configuración
<a name="s3-cross-region-setup"></a>

El acceso entre regiones comenzó a estar disponible con la versión `2.20.111` del SDK. Use esta versión o una posterior en su archivo de compilación de Maven para la dependencia `s3`, como se muestra en el siguiente fragmento.

```
<dependency>
  <groupId>software.amazon.awssdk</groupId>
  <artifactId>s3</artifactId>
  <version>2.27.21</version>
</dependency>
```

Después, cuando cree su cliente S3, habilite el acceso entre regiones, como se muestra en el fragmento. El acceso no está habilitado de forma predeterminada.

```
S3AsyncClient client = S3AsyncClient.builder()
                                    .crossRegionAccessEnabled(true)
                                    .build();
```

## Cómo proporciona el SDK acceso entre regiones
<a name="s3-cross-region-routing"></a>

Al hacer referencia a un bucket existente en una solicitud, como cuando utiliza el método `putObject`, el SDK inicia una solicitud a la Región configurada para el cliente. 

Si el bucket no existe en esa región específica, la respuesta al error incluye la región real en la que reside el bucket. A continuación, el SDK utiliza la región correcta en una segunda solicitud.

Para optimizar las futuras solicitudes al mismo bucket, el SDK almacena en caché esta asignación de regiones en el cliente.

## Consideraciones
<a name="s3-cross-region-considerations"></a>

Cuando active el acceso a los buckets entre regiones, tenga en cuenta que la primera llamada a la API puede provocar un aumento de la latencia si el bucket no se encuentra en la región configurada del cliente. Sin embargo, las llamadas posteriores se benefician de la información regional almacenada en caché, lo que mejora el rendimiento.

Al habilitar el acceso entre regiones, el acceso al bucket no se ve afectado. El usuario debe estar autorizado a acceder al bucket en cualquier región en la que resida.

# Protección de la integridad de los datos con sumas de comprobación
<a name="s3-checksums"></a>

Amazon Simple Storage Service (Amazon S3) permite especificar una suma de comprobación al cargar un objeto. Cuando se especifica una suma de comprobación, esta se almacena con el objeto y se puede validar cuando se descarga el objeto.

Las sumas de comprobación proporcionan un nivel adicional de integridad de los datos al transferir archivos. Con las sumas de comprobación, puede comprobar la coherencia de datos verificando que el archivo recibido coincide con el archivo original. Para obtener más información sobre las sumas de comprobación con Amazon S3, consulte la [Guía del usuario de Amazon Simple Storage Service](https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html), que incluye los [algoritmos compatibles](https://docs.aws.amazon.com/AmazonS3/latest/userguide/checking-object-integrity.html#using-additional-checksums).

Puede elegir el algoritmo que mejor se adapte a sus necesidades y dejar que el SDK calcule la suma de comprobación. También puede especificar un valor de suma de comprobación precalculada mediante uno de los algoritmos compatibles. 

**nota**  
A partir de la versión 2.30.0 de AWS SDK for Java 2.x, el SDK proporciona protecciones de integridad predeterminadas mediante el cálculo automático de una suma de comprobación de `CRC32` para las cargas. El SDK calcula esta suma de comprobación si no se proporciona un valor de suma de comprobación precalculado o si no se especifica un algoritmo que el SDK deba utilizar para calcular una suma de comprobación.   
El SDK también proporciona una configuración global para las protecciones de la integridad de los datos que puede configurar de forma externa, sobre la que puede leer en la Guía de referencia de [herramientas AWS SDKs y herramientas](https://docs.aws.amazon.com/sdkref/latest/guide/feature-dataintegrity.html).

Analizaremos las sumas de comprobación en dos fases de solicitud: carga del objeto y descarga del objeto. 

## Cargar un objeto
<a name="use-service-S3-checksum-upload"></a>

Al cargar un objeto con el método `putObject` y proporcionar un algoritmo de suma de comprobación, el SDK calcula la suma de comprobación del algoritmo especificado.

El siguiente fragmento de código muestra una solicitud para cargar un objeto con una suma de comprobación `SHA256`. Cuando el SDK envía la solicitud, calcula la suma de comprobación de `SHA256` y carga el objeto. Amazon S3 valida la integridad del contenido calculando la suma de comprobación y comparándola con la suma de comprobación proporcionada por el SDK. A continuación, Amazon S3 almacena la suma de comprobación con el objeto.

```
public void putObjectWithChecksum() {
        s3Client.putObject(b -> b
                .bucket(bucketName)
                .key(key)
                .checksumAlgorithm(ChecksumAlgorithm.SHA256),
            RequestBody.fromString("This is a test"));
}
```

Si no proporciona un algoritmo de suma de comprobación con la solicitud, el comportamiento de la suma de comprobación varía en función de la versión del SDK que utilice, tal y como se muestra en la siguiente tabla.

**Comportamiento de la suma de comprobación cuando no se proporciona ningún algoritmo de suma de comprobación**


| Versión del SDK de Java | Comportamiento de la suma de comprobación | 
| --- | --- | 
| anterior a la versión 2.30.0 | El SDK no calcula automáticamente una suma de comprobación basada en CRC ni la proporciona en la solicitud. | 
| 2.30.0 o posterior | El SDK usa el algoritmo `CRC32` para calcular la suma de comprobación y la proporciona en la solicitud. Amazon S3 valida la integridad de la transferencia calculando su propia suma de comprobación `CRC32` y la compara con la suma de comprobación proporcionada por el SDK. Si las sumas de comprobación coinciden, la suma de comprobación se guarda con el objeto. | 

### Utilizar un valor de suma de comprobación calculado previamente
<a name="use-service-S3-checksum-upload-pre"></a>

Un valor de suma de comprobación precalculado proporcionado con la solicitud desactiva el cálculo automático por parte del SDK y utiliza el valor proporcionado en su lugar.

El siguiente ejemplo muestra una solicitud con una suma de control calculada previamente SHA256.

```
    public void putObjectWithPrecalculatedChecksum(String filePath) {
        String checksum = calculateChecksum(filePath, "SHA-256");

        s3Client.putObject((b -> b
                .bucket(bucketName)
                .key(key)
                .checksumSHA256(checksum)),
            RequestBody.fromFile(Paths.get(filePath)));
    }
```

Si Amazon S3 determina que el valor de la suma de comprobación es incorrecto para el algoritmo especificado, el servicio devuelve una respuesta de error.

### Cargas multiparte
<a name="use-service-S3-checksum-upload-multi"></a>

También puede utilizar sumas de comprobación en las cargas multiparte.

 El SDK de Java 2.x ofrece dos opciones para usar sumas de comprobación con cargas multiparte. La primera opción usa `S3TransferManager`.

En el siguiente ejemplo de gestor de transferencias se especifica el SHA1 algoritmo de la carga.

```
    public void multipartUploadWithChecksumTm(String filePath) {
        S3TransferManager transferManager = S3TransferManager.create();
        UploadFileRequest uploadFileRequest = UploadFileRequest.builder()
            .putObjectRequest(b -> b
                .bucket(bucketName)
                .key(key)
                .checksumAlgorithm(ChecksumAlgorithm.SHA1))
            .source(Paths.get(filePath))
            .build();
        FileUpload fileUpload = transferManager.uploadFile(uploadFileRequest);
        fileUpload.completionFuture().join();
        transferManager.close();
    }
```

Si no proporciona un algoritmo de suma de comprobación al utilizar el administrador de transferencias para cargas, el SDK calcula automáticamente una suma de comprobación basada en el algoritmo `CRC32`. El SDK realiza este cálculo para todas las versiones del SDK.

La segunda opción usa la [API `S3Client`](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html) (o la [API `S3AsyncClient`](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html)) para llevar a cabo la carga multiparte. Si especifica una suma de comprobación con este método, debe especificar el algoritmo que se utilizará al iniciar la carga. También debe especificar el algoritmo para cada solicitud de parte y proporcionar la suma de comprobación calculada para cada parte una vez cargada.

```
    public void multipartUploadWithChecksumS3Client(String filePath) {
        ChecksumAlgorithm algorithm = ChecksumAlgorithm.CRC32;

        // Initiate the multipart upload.
        CreateMultipartUploadResponse createMultipartUploadResponse = s3Client.createMultipartUpload(b -> b
            .bucket(bucketName)
            .key(key)
            .checksumAlgorithm(algorithm)); // Checksum specified on initiation.
        String uploadId = createMultipartUploadResponse.uploadId();

        // Upload the parts of the file.
        int partNumber = 1;
        List<CompletedPart> completedParts = new ArrayList<>();
        ByteBuffer bb = ByteBuffer.allocate(1024 * 1024 * 5); // 5 MB byte buffer

        try (RandomAccessFile file = new RandomAccessFile(filePath, "r")) {
            long fileSize = file.length();
            long position = 0;
            while (position < fileSize) {
                file.seek(position);
                long read = file.getChannel().read(bb);

                bb.flip(); // Swap position and limit before reading from the buffer.
                UploadPartRequest uploadPartRequest = UploadPartRequest.builder()
                    .bucket(bucketName)
                    .key(key)
                    .uploadId(uploadId)
                    .checksumAlgorithm(algorithm) // Checksum specified on each part.
                    .partNumber(partNumber)
                    .build();

                UploadPartResponse partResponse = s3Client.uploadPart(
                    uploadPartRequest,
                    RequestBody.fromByteBuffer(bb));

                CompletedPart part = CompletedPart.builder()
                    .partNumber(partNumber)
                    .checksumCRC32(partResponse.checksumCRC32()) // Provide the calculated checksum.
                    .eTag(partResponse.eTag())
                    .build();
                completedParts.add(part);

                bb.clear();
                position += read;
                partNumber++;
            }
        } catch (IOException e) {
            System.err.println(e.getMessage());
        }

        // Complete the multipart upload.
        s3Client.completeMultipartUpload(b -> b
            .bucket(bucketName)
            .key(key)
            .uploadId(uploadId)
            .multipartUpload(CompletedMultipartUpload.builder().parts(completedParts).build()));
    }
```

El [código de los ejemplos y [las pruebas](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/test/java/com/example/s3/PerformMultiPartUploadTests.java) completos](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/main/java/com/example/s3/PerformMultiPartUpload.java) se encuentra en el repositorio GitHub de ejemplos de código.

## Descargar un objeto
<a name="use-service-S3-checksum-download"></a>

Cuando se utiliza el método [getObject](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3Client.html#getObject(software.amazon.awssdk.services.s3.model.GetObjectRequest)) para descargar un objeto, el SDK valida automáticamente la suma de comprobación  cuando el método `checksumMode` del compilador de `GetObjectRequest` se establece como `ChecksumMode.ENABLED`.  

La solicitud del siguiente fragmento indica al SDK que valide la suma de comprobación de la respuesta calculándola y comparando los valores.

```
    public GetObjectResponse getObjectWithChecksum() {
        return s3Client.getObject(b -> b
                        .bucket(bucketName)
                        .key(key)
                        .checksumMode(ChecksumMode.ENABLED))
                .response();
    }
```

**nota**  
Si el objeto no se cargó con una suma de comprobación, no se realizará ninguna validación. 

## Otras opciones de cálculo de la suma de comprobación
<a name="S3-checsum-calculation-options"></a>

**nota**  
Para verificar la integridad de los datos transmitidos e identificar errores de transmisión, recomendamos a los usuarios que mantengan la configuración predeterminada del SDK para las opciones de cálculo de la suma de comprobación. De forma predeterminada, el SDK agrega esta importante comprobación a muchas operaciones de S3, incluidas `PutObject` y `GetObject`.

Sin embargo, si el uso de Amazon S3 requiere una validación mínima de la suma de comprobación, puede deshabilitar muchas comprobaciones cambiando los ajustes de configuración predeterminados. 

### Deshabilite el cálculo automático de la suma de comprobación a menos que sea necesario
<a name="S3-minimize-checksum-calc-global"></a>

Puede deshabilitar el cálculo automático de la suma de comprobación mediante el SDK para las operaciones que lo admiten, por ejemplo, `PutObject` y `GetObject`. Sin embargo, algunas operaciones de S3 requieren un cálculo de la suma de comprobación; no se puede deshabilitar el cálculo de la suma de comprobación para estas operaciones.

El SDK proporciona ajustes independientes para el cálculo de una suma de comprobación de la carga útil de una solicitud y de la carga útil de una respuesta.

En la siguiente lista se describen las configuraciones que puede usar para minimizar los cálculos de la suma de comprobación en los distintos ámbitos.
+ **Alcance de todas las aplicaciones**: al cambiar la configuración de las variables de entorno o de un perfil en los `credentials` archivos AWS `config` AND compartidos, todas las aplicaciones pueden usar esta configuración. Esta configuración afecta a todos los clientes de servicio de todas las aplicaciones AWS del SDK, a menos que se anule en el ámbito del cliente de la aplicación o del servicio.
  + Adición de la configuración a un perfil:

    ```
    [default]
    request_checksum_calculation = WHEN_REQUIRED
    response_checksum_validation = WHEN_REQUIRED
    ```
  + Adición de variables de entorno:

    ```
    AWS_REQUEST_CHECKSUM_CALCULATION=WHEN_REQUIRED
    AWS_RESPONSE_CHECKSUM_VALIDATION=WHEN_REQUIRED
    ```
+ **Ámbito actual de la aplicación**: puede establecer la propiedad del sistema Java en `aws.requestChecksumCalculation` a `WHEN_REQUIRED` para limitar el cálculo de la suma de comprobación. La propiedad del sistema correspondiente para respuestas es `aws.responseChecksumValidation`.

  Esta configuración afecta a todos los clientes de servicio del SDK en la aplicación, a menos que se anulen durante la creación del cliente de servicio.

  Defina la propiedad del sistema en el inicio de la aplicación:

  ```
  import software.amazon.awssdk.core.SdkSystemSetting;
  import software.amazon.awssdk.core.checksums.RequestChecksumCalculation;
  import software.amazon.awssdk.core.checksums.ResponseChecksumValidation;
  import software.amazon.awssdk.services.s3.S3Client;
  
  class DemoClass {
      public static void main(String[] args) {
  
          System.setProperty(SdkSystemSetting.AWS_REQUEST_CHECKSUM_CALCULATION.property(), // Resolves to "aws.requestChecksumCalculation".
                  "WHEN_REQUIRED");
          System.setProperty(SdkSystemSetting.AWS_RESPONSE_CHECKSUM_VALIDATION.property(), // Resolves to "aws.responseChecksumValidation".
                  "WHEN_REQUIRED");
  
          S3Client s3Client = S3Client.builder().build();
  
          // Use s3Client.
      }
  }
  ```
+ **Ámbito de cliente de servicio individual de S3**: puede configurar un cliente de servicio individual de S3 para calcular la cantidad mínima de sumas de comprobación mediante métodos de creación:

  ```
  import software.amazon.awssdk.core.checksums.RequestChecksumCalculation;
  import software.amazon.awssdk.services.s3.S3Client;
  
  public class RequiredChecksums {
      public static void main(String[] args) {
          S3Client s3 = S3Client.builder()
                  .requestChecksumCalculation(RequestChecksumCalculation.WHEN_REQUIRED)
                  .responseChecksumValidation(ResponseChecksumValidation.WHEN_REQUIRED)
                  .build();
  
          // Use s3Client. 
      }
  // ...
  }
  ```

### Utilícela `LegacyMd5Plugin` para simplificar la compatibilidad MD5
<a name="S3-checksum-legacy-md5"></a>

Junto con el lanzamiento del comportamiento de las CRC32 sumas de verificación en la versión 2.30.0, el SDK dejó de calcular las MD5 sumas de verificación en las operaciones requeridas.

Si necesitas un comportamiento de MD5 suma de comprobación tradicional para las operaciones de S3, puedes usar el`LegacyMd5Plugin`, que se publicó con la versión 2.31.32 del SDK.

Esto `LegacyMd5Plugin` resulta especialmente útil cuando se necesita mantener la compatibilidad con aplicaciones que dependen del comportamiento anterior de las MD5 sumatorias de comprobación, especialmente cuando se trabaja con proveedores de almacenamiento de terceros compatibles con S3, como los que se utilizan con los conectores de sistemas de archivos S3A (Apache Spark, Iceberg).

Para usar el `LegacyMd5Plugin`, agréguelo al compilador de clientes de S3:

```
// For synchronous S3 client.
S3Client s3Client = S3Client.builder()
                           .addPlugin(LegacyMd5Plugin.create())
                           .build();

// For asynchronous S3 client.
S3AsyncClient asyncClient = S3AsyncClient.builder()
                                       .addPlugin(LegacyMd5Plugin.create())
                                       .build();
```

Si desea añadir sumas de MD5 comprobación a las operaciones que requieren sumas de comprobación y no quiere añadir las sumas de comprobación predeterminadas del SDK para las operaciones que admiten sumas de comprobación pero que no son obligatorias, puede activar las opciones y como. `ClientBuilder` `requestChecksumCalculation` `responseChecksumValidation` `WHEN_REQUIRED` De este modo se agregarán sumas de comprobación predeterminadas del SDK solo a las operaciones que requieran sumas de comprobación:

```
// Use the `LegacyMd5Plugin` with `requestChecksumCalculation` and `responseChecksumValidation` set to WHEN_REQUIRED.
S3AsyncClient asyncClient = S3AsyncClient.builder()
                                       .addPlugin(LegacyMd5Plugin.create())
                                       .requestChecksumCalculation(RequestChecksumCalculation.WHEN_REQUIRED)
                                       .responseChecksumValidation(ResponseChecksumValidation.WHEN_REQUIRED)
                                       .build();
```

Esta configuración resulta especialmente útil cuando se trabaja con sistemas de almacenamiento de terceros compatibles con S3 que pueden no ser totalmente compatibles con los algoritmos de suma de comprobación más recientes, pero que aun así requieren sumas de comprobación para determinadas operaciones. MD5 

# Utilice un cliente S3 de alto rendimiento: cliente S3 basado en AWS CRT
<a name="crt-based-s3-client"></a>

El cliente S3 AWS basado en CRT, creado sobre el [AWS Common Runtime (](https://docs.aws.amazon.com/sdkref/latest/guide/common-runtime.html)CRT), es un cliente asíncrono S3 alternativo. Transfiere objetos hacia y desde Amazon Simple Storage Service (Amazon S3) con un rendimiento y una fiabilidad mejorados mediante el uso automático de la API de [carga multiparte de Amazon S3 y de](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html) las [recuperaciones por rango de bytes.](https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance-guidelines.html#optimizing-performance-guidelines-get-range) 

El cliente S3 AWS basado en CRT mejora la fiabilidad de la transferencia en caso de que se produzca un fallo en la red. La fiabilidad se mejora reintentando partes individuales fallidas de una transferencia de archivos sin reiniciar la transferencia desde el principio.

Además, el cliente S3 AWS basado en CRT ofrece una mejor agrupación de conexiones y un equilibrio de carga del sistema de nombres de dominio (DNS) mejorados, lo que también mejora el rendimiento.

Puede utilizar el cliente S3 AWS basado en CRT en lugar del cliente asíncrono S3 estándar del SDK y aprovechar su rendimiento mejorado de forma inmediata.

**importante**  
El cliente S3 AWS basado en CRT actualmente no admite la [recopilación de métricas del SDK](metrics.md) a nivel de cliente ni a nivel de solicitud.

**AWS Componentes basados en CRT en el SDK**

El cliente *S3 AWS * basado en CRT, que se describe en este tema, y el cliente *HTTP AWS * basado en CRT son componentes diferentes del SDK. 

El **cliente S3 AWS basado en CRT** es una implementación de la AsyncClient interfaz [S3](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html) y se utiliza para trabajar con el servicio Amazon S3. Es una alternativa a la implementación de la interfaz `S3AsyncClient` basada en Java y ofrece varias ventajas.

El [cliente HTTP AWS basado en CRT](http-configuration-crt.md) es una implementación de la [SdkAsyncHttpClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/async/SdkAsyncHttpClient.html)interfaz y se utiliza para la comunicación HTTP general. Es una alternativa a la implementación Netty de la interfaz `SdkAsyncHttpClient` basada en Java y ofrece varias ventajas.

Si bien ambos componentes utilizan bibliotecas del [AWS Common Runtime](https://docs.aws.amazon.com/sdkref/latest/guide/common-runtime.html), el cliente S3 AWS basado en CRT utiliza la [biblioteca aws-c-s 3](https://github.com/awslabs/aws-c-s3) y admite las funciones de la API de carga [multiparte de S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html). Como el cliente HTTP AWS basado en CRT está diseñado para un uso general, no admite las funciones de la API de carga multiparte de S3.

## Agregue dependencias para usar el cliente S3 basado en CRT AWS
<a name="crt-based-s3-client-depend"></a>

Para usar el cliente S3 AWS basado en CRT, añada las dos dependencias siguientes al archivo de proyecto de Maven. En el ejemplo siguiente se muestran las versiones mínimas que se utilizarán. Busque en el repositorio central de Maven las versiones más recientes de los artefactos [s3](https://central.sonatype.com/artifact/software.amazon.awssdk/s3) y [aws-crt](https://central.sonatype.com/artifact/software.amazon.awssdk.crt/aws-crt).

```
<dependency>
  <groupId>software.amazon.awssdk</groupId>
  <artifactId>s3</artifactId>
  <version>2.27.21</version>
</dependency>
<dependency>
  <groupId>software.amazon.awssdk.crt</groupId>
  <artifactId>aws-crt</artifactId>
  <version>0.30.11</version>
</dependency>
```

## Cree una instancia del cliente S3 basado en CRT AWS
<a name="crt-based-s3-client-create"></a>

 Cree una instancia del cliente S3 AWS basado en CRT con la configuración predeterminada, como se muestra en el siguiente fragmento de código.

```
S3AsyncClient s3AsyncClient = S3AsyncClient.crtCreate();
```

Para configurar el cliente, utilice el generador de clientes CRT. AWS Puede cambiar del cliente asíncrono S3 estándar al cliente AWS basado en CRT cambiando el método de creación.

```
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;


S3AsyncClient s3AsyncClient = 
        S3AsyncClient.crtBuilder()
                     .credentialsProvider(DefaultCredentialsProvider.create())
                     .region(Region.US_WEST_2)
                     .targetThroughputInGbps(20.0)
                     .minimumPartSizeInBytes(8 * 1025 * 1024L)
                     .build();
```

**nota**  
Es posible que algunas de las configuraciones del generador estándar no sean compatibles actualmente con el generador de clientes AWS CRT. Obtener el constructor estándar llamando a `S3AsyncClient#builder()`.

## Utilice el cliente S3 basado en CRT AWS
<a name="crt-based-s3-client-use"></a>

Utilice el cliente S3 AWS basado en CRT para llamar a las operaciones de la API de Amazon S3. En el siguiente ejemplo, se muestran las [GetObject](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html#getObject(java.util.function.Consumer,software.amazon.awssdk.core.async.AsyncResponseTransformer))operaciones [PutObject](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClient.html#putObject(java.util.function.Consumer,software.amazon.awssdk.core.async.AsyncRequestBody))y disponibles a través de. AWS SDK para Java

```
import software.amazon.awssdk.core.async.AsyncRequestBody;
import software.amazon.awssdk.core.async.AsyncResponseTransformer;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;


S3AsyncClient s3Client = S3AsyncClient.crtCreate();

// Upload a local file to Amazon S3.
PutObjectResponse putObjectResponse = 
      s3Client.putObject(req -> req.bucket(<BUCKET_NAME>)
                                   .key(<KEY_NAME>),
                        AsyncRequestBody.fromFile(Paths.get(<FILE_NAME>)))
              .join();

// Download an object from Amazon S3 to a local file.
GetObjectResponse getObjectResponse = 
      s3Client.getObject(req -> req.bucket(<BUCKET_NAME>)
                                   .key(<KEY_NAME>),
                        AsyncResponseTransformer.toFile(Paths.get(<FILE_NAME>)))
              .join();
```

## Carga de flujos de tamaño desconocido
<a name="crt-stream-unknown-size"></a>

Una ventaja importante del cliente S3 AWS AWS basado en CRT es su capacidad para gestionar flujos de entrada de tamaño desconocido de manera eficiente. Esto resulta especialmente útil cuando se deben cargar datos desde una fuente en la que el tamaño total no se puede determinar de antemano.

```
public PutObjectResponse crtClient_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3AsyncClient s3AsyncClient = S3AsyncClient.crtCreate();
    ExecutorService executor = Executors.newSingleThreadExecutor();
    AsyncRequestBody body = AsyncRequestBody.fromInputStream(inputStream, null, executor);  // 'null' indicates that the
                                                                                            // content length is unknown.
    CompletableFuture<PutObjectResponse> responseFuture =
            s3AsyncClient.putObject(r -> r.bucket(bucketName).key(key), body)
                    .exceptionally(e -> {
                        if (e != null){
                            logger.error(e.getMessage(), e);
                        }
                        return null;
                    });

    PutObjectResponse response = responseFuture.join(); // Wait for the response.
    executor.shutdown();
    return response;
}
```

Esta capacidad ayuda a evitar problemas comunes de las cargas tradicionales, en los que una especificación incorrecta de la longitud del contenido puede provocar que los objetos se trunquen o fallen las cargas.

## Limitaciones de la configuración
<a name="crt-based-s3-client-limitations"></a>

El cliente S3 AWS basado en CRT y el cliente asíncrono S3 basado en Java [ofrecen funciones comparables, mientras que el cliente S3 basado en CRT ofrece](examples-s3.md#s3-clients) una ventaja de AWS rendimiento. Sin embargo, el cliente S3 basado en AWS CRT carece de los ajustes de configuración que tiene el cliente asíncrono S3 basado en Java. Esta configuración incluye:
+ *Configuración de nivel de cliente:* tiempo de espera de intentos de llamada a la API, interceptores de ejecución de compresión, publicadores de métricas, atributos de ejecución personalizados, opciones avanzadas personalizadas, servicio de ejecutor programado personalizado, encabezados personalizados
+ *Configuración a nivel de solicitud: firmantes* personalizados, tiempo de espera del intento de llamada a la API

Para obtener una lista completa de las diferencias de configuración, consulte la referencia de la API.


| Cliente asincrónico de S3 basado en Java | AWS Cliente S3 basado en CRT | 
| --- | --- | 
| Configuraciones de nivel de cliente[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/crt-based-s3-client.html)Configuraciones de nivel de solicitud[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/crt-based-s3-client.html) | Configuraciones de nivel de cliente[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/crt-based-s3-client.html)Configuraciones de nivel de solicitud[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/es_es/sdk-for-java/latest/developer-guide/crt-based-s3-client.html) | 

# Configuración del cliente asincrónico S3 basado en Java para usar transferencias paralelas
<a name="s3-async-client-multipart"></a>

Desde la versión 2.27.5, el cliente asincrónico estándar de S3 basado en Java admite transferencias paralelas automáticas (cargas y descargas multiparte). La compatibilidad con las transferencias paralelas se configura al crear el cliente asincrónico de S3 basado en Java. 

En esta sección se muestra cómo habilitar las transferencias paralelas y cómo personalizar la configuración.

## Cree una instancia de `S3AsyncClient`
<a name="s3-async-client-multipart-create"></a>

Cuando se crea una instancia de `S3AsyncClient` sin llamar a ninguno de los métodos `multipart*` en el [generador](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/S3AsyncClientBuilder.html), no se habilitan las transferencias paralelas. Cada una de las siguientes instrucciones crea un cliente asincrónico de S3 basado en Java sin compatibilidad con cargas y descargas multiparte.

### Creación *sin* compatibilidad multiparte
<a name="s3-async-client-mp-off"></a>

**Example**  

```
import software.amazon.awssdk.auth.credentials.ProcessCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;


S3AsyncClient s3Client = S3AsyncClient.create();

S3AsyncClient s3Client2 = S3AsyncClient.builder().build();

S3AsyncClient s3Client3 = S3AsyncClient.builder()
        .credentialsProvider(ProcessCredentialsProvider.builder().build())
        .region(Region.EU_NORTH_1)
        .build();
```

### Creación *con* compatibilidad multiparte
<a name="s3-async-client-mp-on"></a>

Para habilitar transferencias paralelas con configuración predeterminada, llame al `multipartEnabled` en el generador y pase `true` como se muestra en el siguiente ejemplo.

**Example**  

```
S3AsyncClient s3AsyncClient2 = S3AsyncClient.builder()
        .multipartEnabled(true)
        .build();
```

El valor predeterminado es 8 MiB para la configuración de `thresholdInBytes` y `minimumPartSizeInBytes`.

Si personaliza la configuración multiparte, las transferencias paralelas se habilitan automáticamente, como se muestra a continuación.

**Example**  

```
import software.amazon.awssdk.services.s3.S3AsyncClient;
import static software.amazon.awssdk.transfer.s3.SizeConstant.MB;


S3AsyncClient s3AsyncClient2 = S3AsyncClient.builder()
        .multipartConfiguration(b -> b
                .thresholdInBytes(16 * MB)
                .minimumPartSizeInBytes(10 * MB))
        .build();
```

## Carga de flujos de tamaño desconocido
<a name="java-async-client-stream-unknown-size"></a>

El cliente asincrónico de S3 basado en Java con multiparte habilitada puede controlar eficientemente flujos de entrada cuando el tamaño total no se conozca de antemano:

```
public PutObjectResponse asyncClient_multipart_stream_unknown_size(String bucketName, String key, InputStream inputStream) {

    S3AsyncClient s3AsyncClient = S3AsyncClient.builder().multipartEnabled(true).build();
    ExecutorService executor = Executors.newSingleThreadExecutor();
    AsyncRequestBody body = AsyncRequestBody.fromInputStream(inputStream, null, executor); // 'null' indicates that the
                                                                                           // content length is unknown.
    CompletableFuture<PutObjectResponse> responseFuture =
            s3AsyncClient.putObject(r -> r.bucket(bucketName).key(key), body)
                    .exceptionally(e -> {
                        if (e != null) {
                            logger.error(e.getMessage(), e);
                        }
                        return null;
                    });

    PutObjectResponse response = responseFuture.join(); // Wait for the response.
    executor.shutdown();
    return response;
}
```

Este método evita los problemas que pueden surgir al especificar manualmente una longitud de contenido incorrecta, como objetos truncados o cargas fallidas.

# Transferir archivos y directorios con Amazon S3 Transfer Manager
<a name="transfer-manager"></a>

Amazon S3 Transfer Manager es una utilidad de transferencia de archivos de alto nivel y de código abierto para el AWS SDK for Java 2.x. Úselo para transferir archivos y directorios desde y hacia Amazon Simple Storage Service (Amazon S3). 

Cuando crea sobre el [cliente de S3 basado en CRT de AWS](crt-based-s3-client.md) o el [cliente asincrónico de S3 basado en Java estándar con multiparte habilitada](s3-async-client-multipart.md), S3 Transfer Manager puede aprovechar mejoras del rendimiento, como la [API de carga multiparte](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html) y las [recuperaciones de rango de byte](https://docs.aws.amazon.com/AmazonS3/latest/userguide/optimizing-performance-guidelines.html#optimizing-performance-guidelines-get-range). 

Con el S3 Transfer Manager, también puede supervisar el progreso de una transferencia en tiempo real y pausarla para su posterior ejecución.

## Introducción
<a name="transfer-manager-prerequisites"></a>

### Añadir dependencias a su archivo de compilación
<a name="transfer-manager-add-dependency"></a>

Para utilizar el S3 Transfer Manager con rendimiento multiparte mejorado, configure el archivo de compilación con las dependencias necesarias.

------
#### [ Use the AWS CRT-based S3 client ]

```
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>2.27.211</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3-transfer-manager</artifactId>
    </dependency>
    <dependency>
        <groupId>software.amazon.awssdk.crt</groupId>
        <artifactId>aws-crt</artifactId>
        <version>0.29.1432</version>
    </dependency>
</dependencies>
```

1 [Última versión](https://central.sonatype.com/artifact/software.amazon.awssdk/bom). 2[Última versión](https://central.sonatype.com/artifact/software.amazon.awssdk.crt/aws-crt).

------
#### [ Use the Java-based S3 async client ]

```
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>2.27.211</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3-transfer-manager</artifactId>
    </dependency>
</dependencies>
```

1 [Última versión](https://central.sonatype.com/artifact/software.amazon.awssdk/bom).

------

### Crear una instancia del S3 Transfer Manager
<a name="transfer-manager-create"></a>

Para habilitar la transferencia paralela, debe pasar un cliente S3 basado en AWS CRT O un cliente asíncrono S3 basado en Java con la función multiparte habilitada. El ejemplo siguiente muestra cómo configurar un S3 Transfer Manager con ajustes personalizados. 

------
#### [ Use the AWS CRT-based S3 client ]

```
        S3AsyncClient s3AsyncClient = S3AsyncClient.crtBuilder()
                .credentialsProvider(DefaultCredentialsProvider.create())
                .region(Region.US_EAST_1)
                .targetThroughputInGbps(20.0)
                .minimumPartSizeInBytes(8 * MB)
                .build();

        S3TransferManager transferManager = S3TransferManager.builder()
                .s3Client(s3AsyncClient)
                .build();
```

------
#### [ Use the Java-based S3 async client ]

Si la dependencia `aws-crt` no está incluida en el archivo de compilación, S3 Transfer Manager se crea sobre el cliente asincrónico de S3 estándar que se utiliza en el SDK para Java 2.x. 

**Configuración personalizada del cliente de S3: requiere multiparte habilitada**

```
        S3AsyncClient s3AsyncClient = S3AsyncClient.builder()
                .multipartEnabled(true)
                .credentialsProvider(DefaultCredentialsProvider.create())
                .region(Region.US_EAST_1)
                .build();

        S3TransferManager transferManager = S3TransferManager.builder()
                .s3Client(s3AsyncClient)
                .build();
```

**Sin configuración del cliente de S3: compatibilidad multiparte habilitada automáticamente**

```
S3TransferManager transferManager = S3TransferManager.create();
```

------

## Cargar un archivo en un bucket de S3
<a name="transfer-manager-upload"></a>

El siguiente ejemplo muestra un ejemplo de carga de archivos junto con el uso opcional de a [LoggingTransferListener](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/progress/LoggingTransferListener.html), que registra el progreso de la carga.

Para cargar un archivo en Amazon S3 mediante el S3 Transfer Manager, pase un objeto [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/UploadFileRequest.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/UploadFileRequest.html) al método [uploadFile](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/S3TransferManager.html#uploadFile(software.amazon.awssdk.transfer.s3.model.UploadFileRequest)) del `S3TransferManager`.

El [FileUpload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/FileUpload.html)objeto devuelto por el `uploadFile` método representa el proceso de carga. Una vez finalizada la solicitud, el [CompletedFileUpload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedFileUpload.html)objeto contiene información sobre la carga.

```
    public void trackUploadFile(S3TransferManager transferManager, String bucketName,
                             String key, URI filePathURI) {
        UploadFileRequest uploadFileRequest = UploadFileRequest.builder()
                .putObjectRequest(b -> b.bucket(bucketName).key(key))
                .addTransferListener(LoggingTransferListener.create())  // Add listener.
                .source(Paths.get(filePathURI))
                .build();

        FileUpload fileUpload = transferManager.uploadFile(uploadFileRequest);

        fileUpload.completionFuture().join();
        /*
            The SDK provides a LoggingTransferListener implementation of the TransferListener interface.
            You can also implement the interface to provide your own logic.

            Configure log4J2 with settings such as the following.
                <Configuration status="WARN">
                    <Appenders>
                        <Console name="AlignedConsoleAppender" target="SYSTEM_OUT">
                            <PatternLayout pattern="%m%n"/>
                        </Console>
                    </Appenders>

                    <Loggers>
                        <logger name="software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener" level="INFO" additivity="false">
                            <AppenderRef ref="AlignedConsoleAppender"/>
                        </logger>
                    </Loggers>
                </Configuration>

            Log4J2 logs the progress. The following is example output for a 21.3 MB file upload.
                Transfer initiated...
                |                    | 0.0%
                |====                | 21.1%
                |============        | 60.5%
                |====================| 100.0%
                Transfer complete!
        */
    }
```

### Importaciones
<a name="transfer-manager-upload-imports"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedFileUpload;
import software.amazon.awssdk.transfer.s3.model.FileUpload;
import software.amazon.awssdk.transfer.s3.model.UploadFileRequest;
import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.UUID;
```

## Descargar un archivo de un bucket de S3
<a name="transfer-manager-download"></a>

El siguiente ejemplo muestra un ejemplo de descarga junto con el uso opcional de un [LoggingTransferListener](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/progress/LoggingTransferListener.html), que registra el progreso de la descarga.

Para descargar un objeto de un bucket de S3 mediante el administrador de transferencias de S3, cree un [DownloadFileRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/DownloadFileRequest.html)objeto y páselo al método [downloadFile](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/S3TransferManager.html#downloadFile(software.amazon.awssdk.transfer.s3.model.DownloadFileRequest)).

El [FileDownload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/FileDownload.html)objeto devuelto por el `downloadFile` método `S3TransferManager`'s representa la transferencia de archivos. Una vez completada la descarga, [CompletedFileDownload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedFileDownload.html)contiene el acceso a la información sobre la descarga.

```
    public void trackDownloadFile(S3TransferManager transferManager, String bucketName,
                             String key, String downloadedFileWithPath) {
        DownloadFileRequest downloadFileRequest = DownloadFileRequest.builder()
                .getObjectRequest(b -> b.bucket(bucketName).key(key))
                .addTransferListener(LoggingTransferListener.create())  // Add listener.
                .destination(Paths.get(downloadedFileWithPath))
                .build();

        FileDownload downloadFile = transferManager.downloadFile(downloadFileRequest);

        CompletedFileDownload downloadResult = downloadFile.completionFuture().join();
        /*
            The SDK provides a LoggingTransferListener implementation of the TransferListener interface.
            You can also implement the interface to provide your own logic.

            Configure log4J2 with settings such as the following.
                <Configuration status="WARN">
                    <Appenders>
                        <Console name="AlignedConsoleAppender" target="SYSTEM_OUT">
                            <PatternLayout pattern="%m%n"/>
                        </Console>
                    </Appenders>

                    <Loggers>
                        <logger name="software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener" level="INFO" additivity="false">
                            <AppenderRef ref="AlignedConsoleAppender"/>
                        </logger>
                    </Loggers>
                </Configuration>

            Log4J2 logs the progress. The following is example output for a 21.3 MB file download.
                Transfer initiated...
                |=======             | 39.4%
                |===============     | 78.8%
                |====================| 100.0%
                Transfer complete!
        */
    }
```

### Importaciones
<a name="transfer-manager-download-import"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.core.exception.SdkException;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedFileDownload;
import software.amazon.awssdk.transfer.s3.model.DownloadFileRequest;
import software.amazon.awssdk.transfer.s3.model.FileDownload;
import software.amazon.awssdk.transfer.s3.progress.LoggingTransferListener;

import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
```

## Copiar un objeto de Amazon S3 a otro bucket
<a name="transfer-manager-copy"></a>

En el siguiente ejemplo se muestra cómo copiar un objeto con S3 Transfer Manager.

Para empezar a copiar un objeto de un bucket de S3 a otro bucket, cree una [CopyObjectRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/model/CopyObjectRequest.html)instancia básica.

A continuación, incluya la básica `CopyObjectRequest` en una [CopyRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CopyRequest.html)que pueda utilizar el S3 Transfer Manager. 

El objeto `Copy` devuelto por el método `copy` de `S3TransferManager` representa el proceso de copia. Una vez finalizado el proceso de copia, el [CompletedCopy](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedCopy.html)objeto contiene detalles sobre la respuesta.

```
    public String copyObject(S3TransferManager transferManager, String bucketName,
            String key, String destinationBucket, String destinationKey) {
        CopyObjectRequest copyObjectRequest = CopyObjectRequest.builder()
                .sourceBucket(bucketName)
                .sourceKey(key)
                .destinationBucket(destinationBucket)
                .destinationKey(destinationKey)
                .build();

        CopyRequest copyRequest = CopyRequest.builder()
                .copyObjectRequest(copyObjectRequest)
                .build();

        Copy copy = transferManager.copy(copyRequest);

        CompletedCopy completedCopy = copy.completionFuture().join();
        return completedCopy.response().copyObjectResult().eTag();
    }
```

**nota**  
Para realizar una copia entre regiones con el administrador de transferencias de S3, habilítelo `crossRegionAccessEnabled` en el generador de clientes S3 AWS basado en CRT, tal y como se muestra en el siguiente fragmento.  

```
S3AsyncClient s3AsyncClient = S3AsyncClient.crtBuilder()
                .crossRegionAccessEnabled(true)
                .build();

S3TransferManager transferManager = S3TransferManager.builder()
                .s3Client(s3AsyncClient)
                .build();
```

### Importaciones
<a name="transfer-manager-copy-import"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.model.CopyObjectRequest;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedCopy;
import software.amazon.awssdk.transfer.s3.model.Copy;
import software.amazon.awssdk.transfer.s3.model.CopyRequest;

import java.util.UUID;
```

## Cargar un directorio local en un bucket de S3
<a name="transfer-manager-upload_directory"></a>

En el siguiente ejemplo se muestra cómo se puede cargar un directorio local en S3.

Comience por llamar al método [UploadDirectory](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/S3TransferManager.html#uploadDirectory(software.amazon.awssdk.transfer.s3.model.UploadDirectoryRequest)) de la instancia e introduzca un. `S3TransferManager` [UploadDirectoryRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/UploadDirectoryRequest.html)

El [DirectoryUpload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/DirectoryUpload.html)objeto representa el proceso de carga, que genera un [CompletedDirectoryUpload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedDirectoryUpload.html)cuando se completa la solicitud. El objeto `CompleteDirectoryUpload` contiene información sobre los resultados de la transferencia, incluidos los archivos que no se pudieron transferir.

```
    public Integer uploadDirectory(S3TransferManager transferManager,
            URI sourceDirectory, String bucketName) {
        DirectoryUpload directoryUpload = transferManager.uploadDirectory(UploadDirectoryRequest.builder()
                .source(Paths.get(sourceDirectory))
                .bucket(bucketName)
                .build());

        CompletedDirectoryUpload completedDirectoryUpload = directoryUpload.completionFuture().join();
        completedDirectoryUpload.failedTransfers()
                .forEach(fail -> logger.warn("Object [{}] failed to transfer", fail.toString()));
        return completedDirectoryUpload.failedTransfers().size();
    }
```

### Importaciones
<a name="transfer-manager-upload_directory-import"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.services.s3.model.ObjectIdentifier;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedDirectoryUpload;
import software.amazon.awssdk.transfer.s3.model.DirectoryUpload;
import software.amazon.awssdk.transfer.s3.model.UploadDirectoryRequest;

import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Paths;
import java.util.UUID;
```

## Descargar objetos de bucket S3 en un directorio local
<a name="transfer-manager-download_directory"></a>

Puede descargar los objetos de un bucket S3 en un directorio local tal y como se muestra en el siguiente ejemplo.

Para descargar los objetos de un bucket de S3 a un directorio local, comience por llamar al método [downloadDirectory](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/S3TransferManager.html#downloadDirectory(software.amazon.awssdk.transfer.s3.model.DownloadDirectoryRequest)) del Transfer Manager e introduzca un [DownloadDirectoryRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/DownloadDirectoryRequest.html).

El [DirectoryDownload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/DirectoryDownload.html)objeto representa el proceso de descarga, que genera un [CompletedDirectoryDownload](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/transfer/s3/model/CompletedDirectoryDownload.html)cuando se completa la solicitud. El objeto `CompleteDirectoryDownload` contiene información sobre los resultados de la transferencia, incluidos los archivos que no se pudieron transferir.

```
    public Integer downloadObjectsToDirectory(S3TransferManager transferManager,
            URI destinationPathURI, String bucketName) {
        DirectoryDownload directoryDownload = transferManager.downloadDirectory(DownloadDirectoryRequest.builder()
                .destination(Paths.get(destinationPathURI))
                .bucket(bucketName)
                .build());
        CompletedDirectoryDownload completedDirectoryDownload = directoryDownload.completionFuture().join();

        completedDirectoryDownload.failedTransfers()
                .forEach(fail -> logger.warn("Object [{}] failed to transfer", fail.toString()));
        return completedDirectoryDownload.failedTransfers().size();
    }
```

### Importaciones
<a name="transfer-manager-download_directory-import"></a>

```
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.model.ObjectIdentifier;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.CompletedDirectoryDownload;
import software.amazon.awssdk.transfer.s3.model.DirectoryDownload;
import software.amazon.awssdk.transfer.s3.model.DownloadDirectoryRequest;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
```

## Vea ejemplos completos
<a name="transfer-manager-example-location"></a>

[GitHub contiene el código completo](https://github.com/awsdocs/aws-doc-sdk-examples/tree/d73001daea05266eaa9e074ccb71b9383832369a/javav2/example_code/s3/src/main/java/com/example/s3/transfermanager) de todos los ejemplos de esta página.

# Uso de notificaciones de eventos de S3
<a name="examples-s3-event-notifications"></a>

Para ayudarle a monitorizar la actividad de sus buckets, Amazon S3 puede enviar notificaciones cuando se produzcan determinados eventos. La Guía del usuario de Amazon S3 proporciona información sobre las [notificaciones que puede enviar un bucket](https://docs.aws.amazon.com/AmazonS3/latest/userguide/EventNotifications.html#notification-how-to-overview). 

Puede configurar un bucket para enviar eventos a cuatro destinos posibles mediante el SDK para Java: 
+ Temas de Amazon Simple Notification Service
+ Colas de Amazon Simple Queue Service
+ AWS Lambda funciones
+ Amazon EventBridge

Cuando configuras un depósito al que enviar eventos EventBridge, tienes la posibilidad de configurar una EventBridge regla para distribuir el mismo evento a varios destinos. Al configurar el bucket para que se envíe directamente a uno de los tres primeros destinos, solo se puede especificar un tipo de destino para cada evento.

En la siguiente sección, verá cómo configurar un bucket mediante el SDK para Java para enviar notificaciones de eventos de S3 de dos maneras: directamente a una cola de Amazon SQS y a. EventBridge

En la última sección se muestra cómo utilizar la API Notificaciones de eventos de S3 para trabajar con notificaciones de una forma orientada a objetos.

## Configuración de un bucket para enviarlo directamente a un destino
<a name="s3-event-conf-bucket-direct"></a>

En el siguiente ejemplo se configura un bucket para enviar notificaciones cuando se produzcan eventos de *creación de objetos* o de *etiquetado de objetos* en un bucket.

```
static void processS3Events(String bucketName, String queueArn) {
    // Configure the bucket to send Object Created and Object Tagging notifications to an existing SQS queue.
    s3Client.putBucketNotificationConfiguration(b -> b
            .notificationConfiguration(ncb -> ncb
                    .queueConfigurations(qcb -> qcb
                            .events(Event.S3_OBJECT_CREATED, Event.S3_OBJECT_TAGGING)
                            .queueArn(queueArn)))
                    .bucket(bucketName)
    );
}
```

El código mostrado configura una cola para recibir dos tipos de eventos. Convenientemente, el método `queueConfigurations` permite configurar varios destinos de cola si es necesario. Además, en el método `notificationConfiguration` puede establecer destinos adicionales, como uno o más temas de Amazon SNS o una o más funciones de Lambda. El siguiente fragmento muestra un ejemplo con dos colas y tres tipos de destinos.

```
s3Client.putBucketNotificationConfiguration(b -> b
                .notificationConfiguration(ncb -> ncb
                        .queueConfigurations(qcb -> qcb
                                .events(Event.S3_OBJECT_CREATED, Event.S3_OBJECT_TAGGING)
                                .queueArn(queueArn), 
                                qcb2 -> qcb2.<...>)
                        .topicConfigurations(tcb -> tcb.<...>)
                        .lambdaFunctionConfigurations(lfcb -> lfcb.<...>))
                        .bucket(bucketName)
        );
```

El GitHub repositorio de ejemplos de código contiene el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/main/java/com/example/s3/ProcessS3EventNotification.java) para enviar notificaciones de eventos de S3 directamente a una cola.

## Configure un bucket al que enviar EventBridge
<a name="s3-event-conf-bucket-eventbridge"></a>

En el siguiente ejemplo, se configura un depósito al que enviar EventBridge notificaciones.

```
public static String setBucketNotificationToEventBridge(String bucketName) {
    // Enable bucket to emit S3 Event notifications to EventBridge.
    s3Client.putBucketNotificationConfiguration(b -> b
            .bucket(bucketName)
            .notificationConfiguration(b1 -> b1
                    .eventBridgeConfiguration(SdkBuilder::build))
    .build());
```

Al configurar un depósito al que enviar eventos EventBridge, solo tiene que indicar el EventBridge destino, no los tipos de eventos ni el destino final al que EventBridge se enviarán. Los objetivos y los tipos de eventos finales se configuran mediante el EventBridge cliente del SDK de Java.

En el siguiente código se muestra cómo configurar EventBridge para distribuir los eventos *creados por objetos* en un tema y una cola.

```
   public static String configureEventBridge(String topicArn, String queueArn) {
        try {
            // Create an EventBridge rule to route Object Created notifications.
            PutRuleRequest putRuleRequest = PutRuleRequest.builder()
                    .name(RULE_NAME)
                    .eventPattern("""
                            {
                              "source": ["aws.s3"],
                              "detail-type": ["Object Created"],
                              "detail": {
                                "bucket": {
                                  "name": ["%s"]
                                }
                              }
                            }
                            """.formatted(bucketName))
                    .build();

            // Add the rule to the default event bus.
            PutRuleResponse putRuleResponse = eventBridgeClient.putRule(putRuleRequest)
                    .whenComplete((r, t) -> {
                        if (t != null) {
                            logger.error("Error creating event bus rule: " + t.getMessage(), t);
                            throw new RuntimeException(t.getCause().getMessage(), t);
                        }
                        logger.info("Event bus rule creation request sent successfully. ARN is: {}", r.ruleArn());
                    }).join();

            // Add the existing SNS topic and SQS queue as targets to the rule.
            eventBridgeClient.putTargets(b -> b
                    .eventBusName("default")
                    .rule(RULE_NAME)
                    .targets(List.of (
                            Target.builder()
                                    .arn(queueArn)
                                    .id("Queue")
                                    .build(),
                            Target.builder()
                                    .arn(topicArn)
                                    .id("Topic")
                                    .build())
                            )
                    ).join();
            return putRuleResponse.ruleArn();
        } catch (S3Exception e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        return null;
    }
```

Para trabajar con él EventBridge en tu código Java, añade una dependencia del `eventbridge` artefacto a tu archivo `pom.xml` Maven.

```
<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>eventbridge</artifactId>
</dependency>
```

El GitHub repositorio de ejemplos de código contiene el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/main/javav2/example_code/s3/src/main/java/com/example/s3/PutBucketS3EventNotificationEventBridge.java) para enviar notificaciones de eventos de S3 a, EventBridge y luego, a un tema y una cola.

## Uso de la API Notificaciones de eventos de S3 para procesar eventos
<a name="s3-event-notification-read"></a>

Una vez que un destino recibe eventos de notificación de S3, puede procesarlos de forma orientada a objetos mediante la API Notificaciones de eventos de S3. Puedes usar la API de notificaciones de eventos de S3 para trabajar con las notificaciones de eventos que se envían directamente a un destino (como se muestra en el [primer ejemplo](#s3-event-conf-bucket-direct)), pero no con las notificaciones que se envían directamente. EventBridge Las notificaciones de eventos de S3 se envían mediante buckets para EventBridge contener una [estructura diferente](https://docs.aws.amazon.com/AmazonS3/latest/userguide/ev-events.html#ev-events-list) que la API de notificaciones de eventos de S3 no gestiona actualmente.

### Adición de dependencia
<a name="s3-event-notifications-dep"></a>

La API Notificaciones de eventos de S3 se publicó con la versión 2.25.11 del SDK para Java 2.x.

Para usar la API Notificaciones de eventos de S3, agregue el elemento de dependencia necesario al `pom.xml` de Maven, como se muestra en el siguiente fragmento.

```
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>2.X.X1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>s3-event-notifications</artifactId>
    </dependency>
</dependencies>
```

1 [Última versión](https://central.sonatype.com/artifact/software.amazon.awssdk/bom).

### Uso de la clase `S3EventNotification`
<a name="s3-event-notifications-use"></a>

#### Creación de una instancia de `S3EventNotification` a partir de una cadena JSON
<a name="s3-event-notifications-use-from-json"></a>

Para convertir una cadena JSON en un objeto `S3EventNotification`, use los métodos estáticos de la clase `S3EventNotification`, como se muestra en el siguiente ejemplo.

```
import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotification
import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotificationRecord
import software.amazon.awssdk.services.sqs.model.Message; 

public class S3EventNotificationExample {
    ...
    
    void receiveMessage(Message message) {
       // Message received from SQSClient.
       String sqsEventBody = message.body();
       S3EventNotification s3EventNotification = S3EventNotification.fromJson(sqsEventBody);
    
       // Use getRecords() to access all the records in the notification.                                                                                                       
       List<S3EventNotificationRecord> records = s3EventNotification.getRecords();   
    
        S3EventNotificationRecord record = records.stream().findFirst();
        // Use getters on the record to access individual attributes.
        String awsRegion = record.getAwsRegion();
        String eventName = record.getEventName();
        String eventSource = record.getEventSource();                                                                                                   
    }
}
```

En este ejemplo, el método `fromJson` convierte la cadena JSON en un objeto `S3EventNotification`. La falta de campos en la cadena JSON generará valores de `null` en los campos de objetos de Java correspondientes y se ignorará cualquier campo adicional del JSON.

Puedes encontrar otras opciones APIs para un registro de notificaciones de eventos en la referencia de la API. `[S3EventNotificationRecord](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/eventnotifications/s3/model/S3EventNotificationRecord.html)`

#### Conversión de una instancia de `S3EventNotification` en una cadena JSON
<a name="s3-event-notifications-use-to-json"></a>

Utilice el método `toJson` (o `toJsonPretty`) para convertir un objeto `S3EventNotification` en una cadena JSON, como se muestra en el siguiente ejemplo.

```
import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotification

public class S3EventNotificationExample {
    ...

    void toJsonString(S3EventNotification event) {

        String json = event.toJson();
        String jsonPretty = event.toJsonPretty();

        System.out.println("JSON: " + json);
        System.out.println("Pretty JSON: " + jsonPretty);
    }
}
```

Los campos para `GlacierEventData`, `ReplicationEventData`, `IntelligentTieringEventData` y `LifecycleEventData` se excluyen del JSON si son `null`. Los demás campos `null` se serializarán como `null`.

A continuación, se muestra un ejemplo de la salida del método `toJsonPretty` para un evento de etiquetado de objetos de S3.

```
{
  "Records" : [ {
    "eventVersion" : "2.3",
    "eventSource" : "aws:s3",
    "awsRegion" : "us-east-1",
    "eventTime" : "2024-07-19T20:09:18.551Z",
    "eventName" : "ObjectTagging:Put",
    "userIdentity" : {
      "principalId" : "AWS:XXXXXXXXXXX"
    },
    "requestParameters" : {
      "sourceIPAddress" : "XXX.XX.XX.XX"
    },
    "responseElements" : {
      "x-amz-request-id" : "XXXXXXXXXXXX",
      "x-amz-id-2" : "XXXXXXXXXXXXX"
    },
    "s3" : {
      "s3SchemaVersion" : "1.0",
      "configurationId" : "XXXXXXXXXXXXX",
      "bucket" : {
        "name" : "amzn-s3-demo-bucket",
        "ownerIdentity" : {
          "principalId" : "XXXXXXXXXXX"
        },
        "arn" : "arn:aws:s3:::XXXXXXXXXX"
      },
      "object" : {
        "key" : "akey",
        "size" : null,
        "eTag" : "XXXXXXXXXX",
        "versionId" : null,
        "sequencer" : null
      }
    }
  } ]
}
```

Hay disponible un [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/75c3daadf750406156fc87fa30ee499a206b4a36/javav2/example_code/s3/src/main/java/com/example/s3/ProcessS3EventNotification.java#L117) en el GitHub que se muestra cómo utilizar la API para trabajar con las notificaciones recibidas por una cola de Amazon SQS.

## Procese eventos de S3 en Lambda con bibliotecas Java: y AWS SDK for Java 2.x `aws-lambda-java-events`
<a name="s3-event-notif-processing-options"></a>

En lugar de usar el SDK para Java 2.x para procesar las notificaciones de eventos de Amazon S3 en una función Lambda, puede usar `[aws-lambda-java-events](https://github.com/aws/aws-lambda-java-libs/tree/main/aws-lambda-java-events)` la biblioteca de la versión 3.x.x. AWS mantiene la `aws-lambda-java-events` biblioteca de forma independiente y tiene sus propios requisitos de dependencia. La biblioteca de `aws-lambda-java-events` solo funciona con eventos de S3 en funciones de Lambda, mientras que el SDK para Java 2.x funciona con eventos de S3 en funciones de Lambda, Amazon SNS y Amazon SQS.

Ambos enfoques modelan la carga útil de notificación de eventos de JSON de una manera orientada a objetos con similares. APIs En la tabla siguiente se muestran las notables diferencias entre el uso de los dos métodos.


****  

|  | AWS SDK para Java | aws-lambda-java-events biblioteca | 
| --- | --- | --- | 
| Nomenclatura del paquete |  `software.amazon.awssdk.eventnotifications.s3.model.S3EventNotification`  | com.amazonaws.services.lambda.runtime.events.models.s3.S3EventNotification | 
| Parámetro RequestHandler |  Escriba la implementación `RequestHandler` de la función de Lambda para recibir una cadena JSON: <pre>import com.amazonaws.services.lambda.runtime.Context;<br />import com.amazonaws.services.lambda.runtime.RequestHandler;<br />import software.amazon.awssdk.eventnotifications.s3.model.S3EventNotification;<br /><br />public class Handler implements RequestHandler<String, String> {<br /><br />    @Override<br />        public String handleRequest(String jsonS3Event, Context context) {<br />            S3EventNotification s3Event = S3EventNotification<br />                                             .fromJson(jsonS3Event);<br />            // Work with the s3Event object.        <br />            ...<br />    }<br />}</pre>  | Escriba la implementación RequestHandler de la función de Lambda para recibir un objeto S3Event:<pre>import com.amazonaws.services.lambda.runtime.Context;<br />import com.amazonaws.services.lambda.runtime.RequestHandler;<br />import com.amazonaws.services.lambda.runtime.events.S3Event;<br /><br />public class Handler implements RequestHandler<S3Event, String> {<br /><br />    @Override<br />        public String handleRequest(S3Event s3event, Context context) {<br />            // Work with the s3Event object.        <br />            ...<br />    }<br />}</pre> | 
| dependencias Maven |  <pre><dependencyManagement><br />    <dependencies><br />        <dependency><br />            <groupId>software.amazon.awssdk</groupId><br />            <artifactId>bom</artifactId><br />            <version>2.X.X</version><br />            <type>pom</type><br />            <scope>import</scope><br />        </dependency><br />    </dependencies><br /></dependencyManagement><br /><dependencies><br />    <dependency><br />        <groupId>software.amazon.awssdk</groupId><br />        <artifactId>s3-event-notifications</artifactId><br />    </dependency><br />    <!-- Add other SDK dependencies that you need. --><br /></dependencies></pre>  |  <pre><dependencyManagement><br />    <dependencies><br />        <dependency><br />            <groupId>software.amazon.awssdk</groupId><br />            <artifactId>bom</artifactId><br />            <version>2.X.X</version><br />            <type>pom</type><br />            <scope>import</scope><br />        </dependency><br />    </dependencies><br /></dependencyManagement><br /><dependencies><br />    <!-- The following two dependencies are for the <br />         aws-lambda-java-events library. --><br />    <dependency><br />        <groupId>com.amazonaws</groupId><br />        <artifactId>aws-lambda-java-core</artifactId><br />        <version>1.2.3</version>     <br />    </dependency><br />    <dependency><br />        <groupId>com.amazonaws</groupId><br />        <artifactId>aws-lambda-java-events</artifactId><br />        <version>3.15.0</version><br />    </dependency><br />    <!-- Add other SDK dependencies that you need. --><br /></dependencies></pre>  | 

# Trabajar con de Amazon Simple Notification Service
<a name="examples-simple-notification-service"></a>

Con Amazon Simple Notification Service, puede enviar fácilmente mensajes de notificación push en tiempo real desde sus aplicaciones a los suscriptores a través de distintos canales de comunicación. En este tema se describe cómo ejecutar algunas de las funciones básicas de Amazon SNS.

## Crear un tema
<a name="sns-create-topic"></a>

Un **tema** es una agrupación lógica de canales de comunicación que define a qué sistemas se envía un mensaje (por ejemplo, la difusión de un mensaje a AWS Lambda y un webhook HTTP). Se envían mensajes a Amazon SNS y, a continuación, se distribuyen a los canales definidos en el tema. Esto permite que los mensajes estén disponibles para los suscriptores.

Para crear un tema, primero cree un objeto [CreateTopicRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/CreateTopicRequest.html) con el nombre del conjunto de temas utilizando el método `name()` en el creador. A continuación, envíe el objeto de solicitud a Amazon SNS mediante el método `createTopic()` de [SnsClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/SnsClient.html). Puede capturar el resultado de esta solicitud como un objeto [CreateTopicResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/CreateTopicResponse.html), como se muestra en el siguiente fragmento de código.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.CreateTopicRequest;
import software.amazon.awssdk.services.sns.model.CreateTopicResponse;
import software.amazon.awssdk.services.sns.model.SnsException;
```

 **Código de** 

```
    public static String createSNSTopic(SnsClient snsClient, String topicName ) {

        CreateTopicResponse result = null;
        try {
            CreateTopicRequest request = CreateTopicRequest.builder()
                    .name(topicName)
                    .build();

            result = snsClient.createTopic(request);
            return result.topicArn();
        } catch (SnsException e) {

            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        return "";
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/da520cb4436f8567a90b6f73f77232fd590a50bf/javav2/example_code/sns/src/main/java/com/example/sns/CreateTopic.java) en GitHub.

## Enumerar sus temas de Amazon SNS
<a name="sns-crelistate-topics"></a>

Para recuperar una lista de los temas de Amazon SNS existentes, cree un objeto [ListTopicsRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/ListTopicsRequest.html). A continuación, envíe el objeto de solicitud a Amazon SNS mediante el método `listTopics()` de `SnsClient`. Puede capturar el resultado de esta solicitud como un objeto [ListTopicsResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/ListTopicsResponse.html).

El siguiente fragmento de código imprime el código de estado HTTP de la solicitud y una lista de nombres de recursos de Amazon (ARN) para los temas de Amazon SNS.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.ListTopicsRequest;
import software.amazon.awssdk.services.sns.model.ListTopicsResponse;
import software.amazon.awssdk.services.sns.model.SnsException;
```

 **Código de** 

```
    public static void listSNSTopics(SnsClient snsClient) {

        try {
            ListTopicsRequest request = ListTopicsRequest.builder()
                   .build();

            ListTopicsResponse result = snsClient.listTopics(request);
            System.out.println("Status was " + result.sdkHttpResponse().statusCode() + "\n\nTopics\n\n" + result.topics());

        } catch (SnsException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/da520cb4436f8567a90b6f73f77232fd590a50bf/javav2/example_code/sns/src/main/java/com/example/sns/ListTopics.java) en GitHub.

## Suscribir un punto de enlace a un tema
<a name="sns-subscribe-endpoint-topic"></a>

Después de crear un tema, puede configurar los canales de comunicación que serán los puntos de enlace de ese tema. Los mensajes se distribuyen a estos puntos de enlace una vez que Amazon SNS los recibe.

Para configurar un canal de comunicación como punto de enlace de un tema, suscriba ese punto de enlace al tema. Para empezar, cree un objeto [SubscribeRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/SubscribeRequest.html). Especifique el canal de comunicación (por ejemplo, `lambda` o `email`) como `protocol()`. Establezca `endpoint()` en la ubicación de salida correspondiente (por ejemplo, el ARN de una función Lambda o una dirección de correo electrónico) y, a continuación, el ARN del tema al que desea suscribirse como `topicArn()`. Envíe el objeto de solicitud a Amazon SNS mediante el método `subscribe()` del `SnsClient`. Puede capturar el resultado de esta solicitud como un objeto [SubscribeResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/SubscribeResponse.html).

El siguiente fragmento de código muestra cómo suscribir una dirección de correo electrónico a un tema.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.SnsException;
import software.amazon.awssdk.services.sns.model.SubscribeRequest;
import software.amazon.awssdk.services.sns.model.SubscribeResponse;
```

 **Código de** 

```
    public static void subEmail(SnsClient snsClient, String topicArn, String email) {

        try {
            SubscribeRequest request = SubscribeRequest.builder()
                .protocol("email")
                .endpoint(email)
                .returnSubscriptionArn(true)
                .topicArn(topicArn)
                .build();

            SubscribeResponse result = snsClient.subscribe(request);
            System.out.println("Subscription ARN: " + result.subscriptionArn() + "\n\n Status is " + result.sdkHttpResponse().statusCode());

        } catch (SnsException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/da520cb4436f8567a90b6f73f77232fd590a50bf/javav2/example_code/sns/src/main/java/com/example/sns/SubscribeEmail.java) en GitHub.

## Publicar un mensaje en un tema
<a name="sns-publish-message-topic"></a>

Después de haber configurado un tema y uno o varios puntos de enlace para él, puede publicar un mensaje en el tema. Para empezar, cree un objeto [PublishRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/PublishRequest.html). Especifique el `message()` que desea enviar y el ARN del tema (`topicArn()`) al que desea enviarlo. A continuación, envíe el objeto de solicitud a Amazon SNS mediante el método `publish()` de `SnsClient`. Puede capturar el resultado de esta solicitud como un objeto [PublishResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/PublishResponse.html).

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.PublishRequest;
import software.amazon.awssdk.services.sns.model.PublishResponse;
import software.amazon.awssdk.services.sns.model.SnsException;
```

 **Código de** 

```
    public static void pubTopic(SnsClient snsClient, String message, String topicArn) {

        try {
            PublishRequest request = PublishRequest.builder()
                .message(message)
                .topicArn(topicArn)
                .build();

            PublishResponse result = snsClient.publish(request);
            System.out.println(result.messageId() + " Message sent. Status is " + result.sdkHttpResponse().statusCode());

         } catch (SnsException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
         }
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/da520cb4436f8567a90b6f73f77232fd590a50bf/javav2/example_code/sns/src/main/java/com/example/sns/PublishTopic.java) en GitHub.

## Cancelar la suscripción de un punto de enlace de un tema
<a name="sns-unsubscribe-endpoint-topic"></a>

Puede quitar los canales de comunicación configurados como puntos de enlace de un tema. Una vez hecho esto, el tema sigue existiendo y distribuye mensajes a cualquier otro punto de enlace configurado para ese tema.

Para quitar un canal de comunicación como punto de enlace de un tema, cancele la suscripción de dicho punto de enlace del tema. Para empezar, cree un objeto [UnsubscribeRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/UnsubscribeRequest.html) y establezca el ARN del tema del que desea cancelar la suscripción como `subscriptionArn()`. A continuación, envíe el objeto de solicitud a SNS mediante el método `unsubscribe()` de `SnsClient`. Puede capturar el resultado de esta solicitud como un objeto [UnsubscribeResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/UnsubscribeResponse.html).

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.SnsException;
import software.amazon.awssdk.services.sns.model.UnsubscribeRequest;
import software.amazon.awssdk.services.sns.model.UnsubscribeResponse;
```

 **Código de** 

```
    public static void unSub(SnsClient snsClient, String subscriptionArn) {

        try {
            UnsubscribeRequest request = UnsubscribeRequest.builder()
                .subscriptionArn(subscriptionArn)
                .build();

            UnsubscribeResponse result = snsClient.unsubscribe(request);

            System.out.println("\n\nStatus was " + result.sdkHttpResponse().statusCode()
                + "\n\nSubscription was removed for " + request.subscriptionArn());

        } catch (SnsException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/da520cb4436f8567a90b6f73f77232fd590a50bf/javav2/example_code/sns/src/main/java/com/example/sns/Unsubscribe.java) en GitHub.

## Eliminación de un tema
<a name="sns-delete-topic"></a>

Para eliminar un tema de Amazon SNS, primero cree un objeto [DeleteTopicRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/DeleteTopicRequest.html) con el ARN del conjunto de temas como el método `topicArn()` en el creador. A continuación, envíe el objeto de solicitud a Amazon SNS mediante el método `deleteTopic()` de `SnsClient`. Puede capturar el resultado de esta solicitud como un objeto [DeleteTopicResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sns/model/DeleteTopicResponse.html), como se muestra en el siguiente fragmento de código.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sns.SnsClient;
import software.amazon.awssdk.services.sns.model.DeleteTopicRequest;
import software.amazon.awssdk.services.sns.model.DeleteTopicResponse;
import software.amazon.awssdk.services.sns.model.SnsException;
```

 **Código de** 

```
    public static void deleteSNSTopic(SnsClient snsClient, String topicArn ) {

        try {
            DeleteTopicRequest request = DeleteTopicRequest.builder()
                .topicArn(topicArn)
                .build();

            DeleteTopicResponse result = snsClient.deleteTopic(request);
            System.out.println("\n\nStatus was " + result.sdkHttpResponse().statusCode());

        } catch (SnsException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/da520cb4436f8567a90b6f73f77232fd590a50bf/javav2/example_code/sns/src/main/java/com/example/sns/DeleteTopic.java) en GitHub.

Para obtener más información, consulte [Amazon Simple Notification Service Developer Guide](https://docs.aws.amazon.com/sns/latest/dg/).

# Trabajar con de Amazon Simple Queue Service
<a name="examples-sqs"></a>

En esta sección se proporcionan ejemplos de programación [Amazon Simple Queue Service](https://docs.aws.amazon.com/sqs/) mediante AWS SDK para Java 2.x.

Los siguientes ejemplos incluyen únicamente el código necesario para demostrar cada técnica. El [código de ejemplo completo está disponible en GitHub](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2). Desde allí, puede descargar un único archivo de código fuente o clonar el repositorio localmente para obtener todos los ejemplos para compilarlos y ejecutarlos.

**Topics**
+ [Uso de procesamiento automático de solicitudes por lotes](sqs-auto-batch.md)
+ [Operaciones de cola](examples-sqs-message-queues.md)
+ [Operaciones con mensajes](examples-sqs-messages.md)

# Utilice el procesamiento automático por lotes de solicitudes para Amazon SQS con AWS SDK for Java 2.x
<a name="sqs-auto-batch"></a>

La API Procesamiento automático de solicitudes por lotes para Amazon SQS es una biblioteca de alto nivel que proporciona una forma eficaz de procesar por lotes y almacenar en búfer las solicitudes de operaciones de SQS. Al utilizar la API de procesamiento por lotes, reduce el número de solicitudes a SQS, lo que mejora el rendimiento y minimiza los costos. 

Dado que los métodos de la API por lotes coinciden con los métodos de `[SqsAsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/SqsAsyncClient.html)` (`sendMessage`, `changeMessageVisibility`, `deleteMessage`, `receiveMessage`), puede utilizar la API por lotes como sustitución directa con cambios mínimos. 

En este tema se ofrece información general sobre cómo configurar y trabajar con la API Procesamiento automático de solicitudes por lotes para Amazon SQS.

## Requisitos previos
<a name="sqs-auto-batch-requirements"></a>

Debe usar la versión *2.28.0* o posterior del SDK para Java 2.x para tener acceso a la API de procesamiento por lotes. Su `pom.xml` de Maven debe contener al menos los siguientes elementos.

```
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>software.amazon.awssdk</groupId>
            <artifactId>bom</artifactId>
            <version>2.28.231</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<dependencies>
    <dependency>
        <groupId>software.amazon.awssdk</groupId>
        <artifactId>sqs</artifactId>
    </dependency>
</dependencies>
```

1 [Última versión](https://central.sonatype.com/artifact/software.amazon.awssdk/bom)

## Creación de un administrador de lotes
<a name="sqs-auto-batch-create"></a>

La [SqsAsyncBatchManager](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/SqsAsyncBatchManager.html)interfaz implementa la API automática de procesamiento por lotes de solicitudes. Puede crear una instancia del administrador de dos formas.

### Configuración predeterminada mediante `SqsAsyncClient`
<a name="sqs-batch-manager-create-default"></a>

La forma más sencilla de crear un administrador de lotes es llamar al método de `batchManager` fábrica en una [SqsAsyncClient](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/SqsAsyncClient.html)instancia existente. Este método se muestra en el fragmento de código siguiente. 

```
SqsAsyncClient asyncClient = SqsAsyncClient.create();
SqsAsyncBatchManager sqsAsyncBatchManager = asyncClient.batchManager();
```

Al usar este método, la instancia `SqsAsyncBatchManager` usa los valores predeterminados que se muestran en la tabla de la sección [Anulación de los ajustes de configuración de `SqsAsyncBatchManager`](#sqs-auto-batch-config-settings). Además, la instancia `SqsAsyncBatchManager` usa el `ExecutorService` de la instancia de `SqsAsyncClient` desde la que se creó.

### Configuración personalizada mediante `SqsAsyncBatchManager.Builder`
<a name="sqs-batch-manager-create-custom"></a>

Para casos de uso más avanzados, puede personalizar el administrador de lotes mediante [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/SqsAsyncBatchManager.Builder.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/SqsAsyncBatchManager.Builder.html). Si usa este método para crear una instancia de `SqsAsyncBatchManager`, puede afinar el comportamiento del procesamiento por lotes. El siguiente fragmento de código ofrece un ejemplo de cómo utilizar el compilador para personalizar el comportamiento de procesamiento por lotes.

```
SqsAsyncBatchManager batchManager = SqsAsyncBatchManager.builder()
    .client(SqsAsyncClient.create())
    .scheduledExecutor(Executors.newScheduledThreadPool(5))
    .overrideConfiguration(b -> b
        .receiveMessageMinWaitDuration(Duration.ofSeconds(10))
        .receiveMessageVisibilityTimeout(Duration.ofSeconds(1))
        .receiveMessageAttributeNames(Collections.singletonList("*"))
        .receiveMessageSystemAttributeNames(Collections.singletonList(MessageSystemAttributeName.ALL)))
    .build();
```

Si utiliza este método, puede ajustar la configuración del objeto `BatchOverrideConfiguration` que se muestra en la tabla de la sección [Anulación de los ajustes de configuración de `SqsAsyncBatchManager`](#sqs-auto-batch-config-settings). También puede proporcionar un [https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ScheduledExecutorService.html](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/concurrent/ScheduledExecutorService.html) personalizado para el administrador de lotes mediante este método.

## Envío de mensajes
<a name="sqs-auto-batch-send"></a>

Para enviar mensajes con el administrador de lotes, utilice el método `[SqsAsyncBatchManager\$1sendMessage](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/SqsAsyncBatchManager.html#sendMessage(software.amazon.awssdk.services.sqs.model.SendMessageRequest))`. El SDK almacena en búfer las solicitudes y las envía como un lote cuando se alcanzan los valores `maxBatchSize` o `sendRequestFrequency`.

En el siguiente ejemplo, se muestra una solicitud `sendMessage` seguida inmediatamente de otra solicitud. En este caso, el SDK envía ambos mensajes en un único lote.

```
// Sending the first message
CompletableFuture<SendMessageResponse> futureOne = 
    sqsAsyncBatchManager.sendMessage(r -> r.messageBody("One").queueUrl("queue"));

// Sending the second message
CompletableFuture<SendMessageResponse> futureTwo = 
    sqsAsyncBatchManager.sendMessage(r -> r.messageBody("Two").queueUrl("queue"));

// Waiting for both futures to complete and retrieving the responses
SendMessageResponse messageOne = futureOne.join();
SendMessageResponse messageTwo = futureTwo.join();
```

## Cambio del tiempo de espera de visibilidad de mensajes
<a name="sqs-auto-batch-change-vis"></a>

Puede cambiar el tiempo de espera de visibilidad de los mensajes de un lote utilizando el método [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/SqsAsyncBatchManager.html#changeMessageVisibility(java.util.function.Consumer)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/SqsAsyncBatchManager.html#changeMessageVisibility(java.util.function.Consumer)). El SDK almacena en búfer las solicitudes y las envía como un lote cuando se alcanzan los valores `maxBatchSize` o `sendRequestFrequency`.

En el siguiente ejemplo, se muestra cómo llamar al método `changeMessageVisibility`.

```
CompletableFuture<ChangeMessageVisibilityResponse> futureOne =
    sqsAsyncBatchManager.changeMessageVisibility(r -> 
        r.receiptHandle("receiptHandle")
         .queueUrl("queue"));
ChangeMessageVisibilityResponse response = futureOne.join();
```

## Cómo eliminar mensajes
<a name="sqs-auto-batch-delete"></a>

Puede eliminar los mensajes de un lote con el método [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/SqsAsyncBatchManager.html#deleteMessage(java.util.function.Consumer)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/SqsAsyncBatchManager.html#deleteMessage(java.util.function.Consumer)). El SDK almacena en búfer las solicitudes y las envía como un lote cuando se alcanzan los valores `maxBatchSize` o `sendRequestFrequency`.

En el siguiente ejemplo se muestra cómo llamar al método `deleteMessage`.

```
CompletableFuture<DeleteMessageResponse> futureOne = 
    sqsAsyncBatchManager.deleteMessage(r -> 
        r.receiptHandle("receiptHandle")
         .queueUrl("queue"));
DeleteMessageResponse response = futureOne.join();
```

## Recepción de mensajes
<a name="sqs-auto-batch-receive"></a>

### Uso de la configuración predeterminada
<a name="sqs-auto-batch-receive-default-settings"></a>

Al sondear el método `[SqsAsyncBatchManager\$1receiveMessage](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/SqsAsyncBatchManager.html#receiveMessage(java.util.function.Consumer))` en la aplicación, el administrador de lotes extrae los mensajes de su búfer interno, que el SDK actualiza automáticamente en segundo plano.

En el siguiente ejemplo, se muestra cómo llamar al método `receiveMessage`.

```
CompletableFuture<ReceiveMessageResponse> responseFuture = 
    sqsAsyncBatchManager.receiveMessage(r -> r.queueUrl("queueUrl"));
```

### Uso de la configuración personalizada
<a name="sqs-auto-batch-receive-custom-settings"></a>

Si quiere personalizar aún más la solicitud, por ejemplo, configurando tiempos de espera personalizados y especificando el número de mensajes que se van a recuperar, puede personalizar la solicitud como se muestra en el siguiente ejemplo.

```
CompletableFuture<ReceiveMessageResponse> response = 
    sqsAsyncBatchManager.receiveMessage(r -> 
        r.queueUrl("queueUrl")
         .waitTimeSeconds(5)
         .visibilityTimeout(20));
```

**nota**  
Si llama a `receiveMessage` con un parámetro `[ReceiveMessageRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/ReceiveMessageRequest.html)` que incluya alguno de los siguientes parámetros, el SDK omite el administrador de lotes y envía una solicitud asincrónica `receiveMessage` normal:  
`messageAttributeNames`
`messageSystemAttributeNames`
`messageSystemAttributeNamesWithStrings`
`overrideConfiguration`

## Anulación de los ajustes de configuración de `SqsAsyncBatchManager`
<a name="sqs-auto-batch-config-settings"></a>

Puede ajustar la configuración siguiente al crear una instancia `SqsAsyncBatchManager`. La siguiente lista de ajustes está disponible en [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/BatchOverrideConfiguration.Builder.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/batchmanager/BatchOverrideConfiguration.Builder.html).


| Opción | Description (Descripción) | Predeterminado | 
| --- | --- | --- | 
| maxBatchSize | Número máximo de solicitudes por lote para cada SendMessageBatchRequest, ChangeMessageVisibilityBatchRequest o DeleteMessageBatchRequest. El valor máximo es 10. | 10 | 
| sendRequestFrequency |  Tiempo antes de enviar un lote, a menos que se alcance antes `maxBatchSize`. Los valores más altos pueden reducir las solicitudes pero aumentar la latencia.  | 200 ms | 
| receiveMessageVisibilityTimeout | Tiempo de espera de visibilidad para los mensajes. Si no se especifica, se utiliza la configuración predeterminada de la cola. | Configuración predeterminada de la cola | 
| receiveMessageMinWaitDuration | Tiempo mínimo de espera para solicitudes receiveMessage. Evite establecer el valor en 0 para evitar un uso excesivo de la CPU. | 50 ms | 
| receiveMessageSystemAttributeNames | Lista de [nombres de atributos del sistema](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/MessageSystemAttributeName.html) para solicitar para llamadas receiveMessage. | Ninguno | 
| receiveMessageAttributeNames | Lista de [nombres de atributos](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-message-metadata.html#sqs-message-attributes) para solicitar para llamadas receiveMessage. | Ninguno | 

# Trabaje con colas de Amazon Simple Queue Service mensajes
<a name="examples-sqs-message-queues"></a>

Una *cola de mensajes* es el contenedor lógico que se utiliza para enviar mensajes de forma fiable. Amazon Simple Queue Service Existen dos tipos de colas: *estándar* y *primero en entrar, primero en salir* (FIFO). Para obtener más información sobre las colas y las diferencias entre estos tipos, consulte la [Guía para desarrolladores de Amazon Simple Queue Service](https://docs.aws.amazon.com//AWSSimpleQueueService/latest/SQSDeveloperGuide/welcome.html).

En este tema se describe cómo crear, enumerar, eliminar y obtener la URL de una Amazon Simple Queue Service cola mediante el. AWS SDK para Java

La variable `sqsClient` que se utiliza en los ejemplos a continuación se puede crear a partir del siguiente fragmento.

```
SqsClient sqsClient = SqsClient.create();
```

Al crear un `SqsClient` utilizando el método `create()` estático, el SDK configura la región mediante la [cadena de proveedores de regiones predeterminada](region-selection.md#default-region-provider-chain) y las credenciales mediante la [cadena de proveedores de credenciales predeterminada](credentials-chain.md).

## Creación de una cola
<a name="sqs-create-queue"></a>

Use el `createQueue` método de `SqsClient’s` y proporcione un objeto `[CreateQueueRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/CreateQueueRequest.html)` que describa los parámetros de la cola, como se muestra en el siguiente fragmento de código.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.*;
import java.util.List;
```

 **Código** 

```
            CreateQueueRequest createQueueRequest = CreateQueueRequest.builder()
                .queueName(queueName)
                .build();

            sqsClient.createQueue(createQueueRequest);
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/sqs/src/main/java/com/example/sqs/SQSExample.java#L52) en. GitHub

## Lista de colas
<a name="sqs-list-queues"></a>

Para ver las Amazon Simple Queue Service colas de tu cuenta, llama al `SqsClient’s` `listQueues` método con un `[ListQueuesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/ListQueuesRequest.html)` objeto.

Si utiliza la forma del método [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/SqsClient.html#listQueues()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/SqsClient.html#listQueues()) que no acepta parámetros, el servicio devuelve *todas las colas* hasta un máximo de 1000. 

Puede proporcionar un prefijo de nombre de cola al objeto `[ListQueuesRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/ListQueuesRequest.html)` para limitar los resultados a las colas que coinciden con ese prefijo, como se muestra en el código siguiente.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.*;
import java.util.List;
```

 **Código** 

```
        String prefix = "que";

        try {
            ListQueuesRequest listQueuesRequest = ListQueuesRequest.builder().queueNamePrefix(prefix).build();
            ListQueuesResponse listQueuesResponse = sqsClient.listQueues(listQueuesRequest);

            for (String url : listQueuesResponse.queueUrls()) {
                System.out.println(url);
            }

        } catch (SqsException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
```

Consulta el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/sqs/src/main/java/com/example/sqs/SQSExample.java#L79) en. GitHub

## Obtener la URL de una cola
<a name="sqs-get-queue-url"></a>

El código siguiente muestra cómo obtener la URL de una cola llamando al `getQueueUrl` método de `SqsClient’s` con un objeto `[GetQueueUrlRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/GetQueueUrlRequest.html)`.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.*;
import java.util.List;
```

 **Código** 

```
            GetQueueUrlResponse getQueueUrlResponse =
                sqsClient.getQueueUrl(GetQueueUrlRequest.builder().queueName(queueName).build());
            String queueUrl = getQueueUrlResponse.queueUrl();
            return queueUrl;
```

Vea el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/7486a1a092aa8e16a21698ef26f9d524fef62e55/javav2/example_code/sqs/src/main/java/com/example/sqs/SQSExample.java#L70) en GitHub.

## Eliminar una cola
<a name="sqs-delete-queue"></a>

Proporcione la [URL](#sqs-get-queue-url) de la cola al objeto `[DeleteQueueRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/DeleteQueueRequest.html)`. A continuación, llame al método `deleteQueue` de `SqsClient’s` para eliminar una cola, como se muestra en el código siguiente.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.*;
import java.util.List;
```

 **Código** 

```
    public static void deleteSQSQueue(SqsClient sqsClient, String queueName) {

        try {

            GetQueueUrlRequest getQueueRequest = GetQueueUrlRequest.builder()
                    .queueName(queueName)
                    .build();

            String queueUrl = sqsClient.getQueueUrl(getQueueRequest).queueUrl();

            DeleteQueueRequest deleteQueueRequest = DeleteQueueRequest.builder()
                    .queueUrl(queueUrl)
                    .build();

            sqsClient.deleteQueue(deleteQueueRequest);

        } catch (SqsException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
    }
```

Vea el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/6240df86c5f17eae1e23d1139d1435c7dc4b2a11/javav2/example_code/sqs/src/main/java/com/example/sqs/DeleteQueue.java#L48) en GitHub.

## Más información
<a name="more-information"></a>
+  [CreateQueue](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_CreateQueue.html)en la referencia Amazon Simple Queue Service de la API
+  [GetQueueUrl](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_GetQueueUrl.html)en la referencia Amazon Simple Queue Service de la API
+  [ListQueues](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_ListQueues.html)en la referencia Amazon Simple Queue Service de la API
+  [DeleteQueue](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_DeleteQueue.html)en la referencia Amazon Simple Queue Service de la API

# Enviar, recibir y eliminar Amazon Simple Queue Service mensajes
<a name="examples-sqs-messages"></a>

Un mensaje es un segmento de datos que los componentes distribuidos pueden enviar y recibir. Los mensajes se envían siempre a través de una [cola de SQS](examples-sqs-message-queues.md).

La variable `sqsClient` que se utiliza en los ejemplos a continuación se puede crear a partir del siguiente fragmento.

```
SqsClient sqsClient = SqsClient.create();
```

Al crear un `SqsClient` utilizando el método `create()` estático, el SDK configura la región mediante la [cadena de proveedores de regiones predeterminada](region-selection.md#default-region-provider-chain) y las credenciales mediante la [cadena de proveedores de credenciales predeterminada](credentials-chain.md).

## Enviar un mensaje
<a name="sqs-message-send"></a>

Agrega un solo mensaje a una Amazon Simple Queue Service cola llamando al `sendMessage` método SqsClient cliente. Proporcione un [SendMessageRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/SendMessageRequest.html)objeto que contenga la [URL](examples-sqs-message-queues.md#sqs-get-queue-url) de la cola, el cuerpo del mensaje y el valor de retraso opcional (en segundos).

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.*;
import java.util.List;
```

 **Código** 

```
            sqsClient.sendMessage(SendMessageRequest.builder()
                .queueUrl(queueUrl)
                .messageBody("Hello world!")
                .delaySeconds(10)
                .build());

            sqsClient.sendMessage(sendMsgRequest);
```

## Enviar varios mensajes en una solicitud
<a name="sqs-messages-send-multiple"></a>

Envíe varios mensajes en una sola solicitud mediante el método SqsClient `sendMessageBatch`. Este método toma una [SendMessageBatchRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/SendMessageBatchRequest.html)que contiene la URL de la cola y una lista de los mensajes que se van a enviar. (Cada mensaje es un [SendMessageBatchRequestEntry](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/SendMessageBatchRequestEntry.html).) También puede retrasar el envío de un mensaje específico estableciendo un valor de retraso en el mensaje.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.*;
import java.util.List;
```

 **Código** 

```
            SendMessageBatchRequest sendMessageBatchRequest = SendMessageBatchRequest.builder()
                .queueUrl(queueUrl)
                .entries(SendMessageBatchRequestEntry.builder().id("id1").messageBody("Hello from msg 1").build(),
                        SendMessageBatchRequestEntry.builder().id("id2").messageBody("msg 2").delaySeconds(10).build())
                .build();
            sqsClient.sendMessageBatch(sendMessageBatchRequest);
```

Vea el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/sqs/src/main/java/com/example/sqs/SQSExample.java#L133) en GitHub.

## Recuperar mensajes
<a name="sqs-messages-receive"></a>

Recupere todos los mensajes que se encuentran actualmente en la cola llamando al método SqsClient `receiveMessage`. Este método toma una [ReceiveMessageRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/ReceiveMessageRequest.html)que contiene la URL de la cola. También puede especificar el número máximo de mensajes que se devuelven. Los mensajes se devuelven como una lista de objetos [Message](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/Message.html).

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.*;
import java.util.List;
```

 **Código** 

```
        try {
            ReceiveMessageRequest receiveMessageRequest = ReceiveMessageRequest.builder()
                .queueUrl(queueUrl)
                .maxNumberOfMessages(5)
                .build();
            List<Message> messages = sqsClient.receiveMessage(receiveMessageRequest).messages();
            return messages;
        } catch (SqsException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
        }
        return null;
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/sqs/src/main/java/com/example/sqs/SQSExample.java#L148) en. GitHub

## Eliminar un mensaje después de su recepción
<a name="sqs-messages-delete"></a>

Tras recibir un mensaje y procesar su contenido, elimine el mensaje de la cola enviando el identificador de recepción y la URL de la cola del mensaje al método [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/SqsClient.html#deleteMessage(software.amazon.awssdk.services.sqs.model.DeleteMessageRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/SqsClient.html#deleteMessage(software.amazon.awssdk.services.sqs.model.DeleteMessageRequest)) de `SqsClient's`.

 **Importaciones** 

```
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.*;
import java.util.List;
```

 **Código** 

```
        try {
            for (Message message : messages) {
                DeleteMessageRequest deleteMessageRequest = DeleteMessageRequest.builder()
                        .queueUrl(queueUrl)
                        .receiptHandle(message.receiptHandle())
                        .build();
                sqsClient.deleteMessage(deleteMessageRequest);
            }
```

Vea el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/cf25559da654a7b74bec039c0ab9397dc5951dd4/javav2/example_code/sqs/src/main/java/com/example/sqs/SQSExample.java#L187) en GitHub.

## Más información
<a name="more-info"></a>
+  [Cómo funcionan Amazon Simple Queue Service las colas](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-basic-architecture.html) en la guía Amazon Simple Queue Service para desarrolladores
+  [SendMessage](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessage.html)en la referencia de la Amazon Simple Queue Service API
+  [SendMessageBatch](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_SendMessageBatch.html)en la referencia Amazon Simple Queue Service de la API
+  [ReceiveMessage](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_ReceiveMessage.html)en la referencia Amazon Simple Queue Service de la API
+  [DeleteMessage](https://docs.aws.amazon.com/AWSSimpleQueueService/latest/APIReference/API_DeleteMessage.html)en la referencia Amazon Simple Queue Service de la API

# Trabaja con Amazon Transcribe
<a name="examples-transcribe"></a>

En el siguiente ejemplo se muestra cómo funciona el streaming bidireccional con Amazon Transcribe. El streaming bidireccional implica que hay una secuencia de datos que se envía al servicio y que se recibe en tiempo real. El ejemplo utiliza la transcripción Amazon Transcribe en streaming para enviar una transmisión de audio y recibir una secuencia de texto transcrito en tiempo real.

Consulta la [transcripción en streaming](https://docs.aws.amazon.com//transcribe/latest/dg/streaming.html) en la guía para Amazon Transcribe desarrolladores para obtener más información sobre esta función.

Consulte [Primeros pasos](https://docs.aws.amazon.com//transcribe/latest/dg/getting-started.html) en la Guía para Amazon Transcribe desarrolladores para empezar a utilizarla Amazon Transcribe.

## Configurar el micrófono
<a name="set-up-the-microphone"></a>

Este código utiliza el paquete javax.sound.sampled para transmitir audio desde un dispositivo de entrada.

 **Código** 

```
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.TargetDataLine;

public class Microphone {

    public static TargetDataLine get() throws Exception {
        AudioFormat format = new AudioFormat(16000, 16, 1, true, false);
        DataLine.Info datalineInfo = new DataLine.Info(TargetDataLine.class, format);

        TargetDataLine dataLine = (TargetDataLine) AudioSystem.getLine(datalineInfo);
        dataLine.open(format);

        return dataLine;
    }
}
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/transcribe/src/main/java/com/amazonaws/transcribe/Microphone.java) en GitHub.

## Crear un editor
<a name="create-a-publisher"></a>

Este código implementa un editor que publica los datos de audio de la transmisión Amazon Transcribe de audio.

 **Código** 

```
package com.amazonaws.transcribe;

import java.io.IOException;
import java.io.InputStream;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.services.transcribestreaming.model.AudioEvent;
import software.amazon.awssdk.services.transcribestreaming.model.AudioStream;
import software.amazon.awssdk.services.transcribestreaming.model.TranscribeStreamingException;


public class AudioStreamPublisher implements Publisher<AudioStream> {
    private final InputStream inputStream;

    public AudioStreamPublisher(InputStream inputStream) {
        this.inputStream = inputStream;
    }

    @Override
    public void subscribe(Subscriber<? super AudioStream> s) {
        s.onSubscribe(new SubscriptionImpl(s, inputStream));
    }

    private class SubscriptionImpl implements Subscription {
        private static final int CHUNK_SIZE_IN_BYTES = 1024 * 1;
        private ExecutorService executor = Executors.newFixedThreadPool(1);
        private AtomicLong demand = new AtomicLong(0);

        private final Subscriber<? super AudioStream> subscriber;
        private final InputStream inputStream;

        private SubscriptionImpl(Subscriber<? super AudioStream> s, InputStream inputStream) {
            this.subscriber = s;
            this.inputStream = inputStream;
        }

        @Override
        public void request(long n) {
            if (n <= 0) {
                subscriber.onError(new IllegalArgumentException("Demand must be positive"));
            }

            demand.getAndAdd(n);

            executor.submit(() -> {
                try {
                    do {
                        ByteBuffer audioBuffer = getNextEvent();
                        if (audioBuffer.remaining() > 0) {
                            AudioEvent audioEvent = audioEventFromBuffer(audioBuffer);
                            subscriber.onNext(audioEvent);
                        } else {
                            subscriber.onComplete();
                            break;
                        }
                    } while (demand.decrementAndGet() > 0);
                } catch (TranscribeStreamingException e) {
                    subscriber.onError(e);
                }
            });
        }

        @Override
        public void cancel() {

        }

        private ByteBuffer getNextEvent() {
            ByteBuffer audioBuffer;
            byte[] audioBytes = new byte[CHUNK_SIZE_IN_BYTES];

            int len = 0;
            try {
                len = inputStream.read(audioBytes);

                if (len <= 0) {
                    audioBuffer = ByteBuffer.allocate(0);
                } else {
                    audioBuffer = ByteBuffer.wrap(audioBytes, 0, len);
                }
            } catch (IOException e) {
                throw new UncheckedIOException(e);
            }

            return audioBuffer;
        }

        private AudioEvent audioEventFromBuffer(ByteBuffer bb) {
            return AudioEvent.builder()
                    .audioChunk(SdkBytes.fromByteBuffer(bb))
                    .build();
        }
    }
}
```

Consulte el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/transcribe/src/main/java/com/amazonaws/transcribe/AudioStreamPublisher.java) en GitHub.

## Cree el cliente e inicie la secuencia
<a name="create-the-client-and-start-the-stream"></a>

En el método principal, cree un objeto de solicitud, inicie la secuencia de entrada de audio y cree instancias del editor con la entrada de audio.

También debe crear un [StartStreamTranscriptionResponseHandler](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/transcribestreaming/model/StartStreamTranscriptionResponseHandler.html)para especificar cómo gestionar la respuesta desde Amazon Transcribe.

A continuación, utilice el `startStreamTranscription` método TranscribeStreamingAsyncClient's para iniciar la transmisión bidireccional.

 **Importaciones** 

```
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.TargetDataLine;
import javax.sound.sampled.AudioInputStream;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.transcribestreaming.TranscribeStreamingAsyncClient;
import software.amazon.awssdk.services.transcribestreaming.model.TranscribeStreamingException ;
import software.amazon.awssdk.services.transcribestreaming.model.StartStreamTranscriptionRequest;
import software.amazon.awssdk.services.transcribestreaming.model.MediaEncoding;
import software.amazon.awssdk.services.transcribestreaming.model.LanguageCode;
import software.amazon.awssdk.services.transcribestreaming.model.StartStreamTranscriptionResponseHandler;
import software.amazon.awssdk.services.transcribestreaming.model.TranscriptEvent;
```

 **Código** 

```
    public static void convertAudio(TranscribeStreamingAsyncClient client) throws Exception {

        try {

            StartStreamTranscriptionRequest request = StartStreamTranscriptionRequest.builder()
                    .mediaEncoding(MediaEncoding.PCM)
                    .languageCode(LanguageCode.EN_US)
                    .mediaSampleRateHertz(16_000).build();

            TargetDataLine mic = Microphone.get();
            mic.start();

            AudioStreamPublisher publisher = new AudioStreamPublisher(new AudioInputStream(mic));

            StartStreamTranscriptionResponseHandler response =
                    StartStreamTranscriptionResponseHandler.builder().subscriber(e -> {
                        TranscriptEvent event = (TranscriptEvent) e;
                        event.transcript().results().forEach(r -> r.alternatives().forEach(a -> System.out.println(a.transcript())));
                    }).build();

            // Keeps Streaming until you end the Java program
            client.startStreamTranscription(request, publisher, response);

        } catch (TranscribeStreamingException e) {
            System.err.println(e.awsErrorDetails().errorMessage());
            System.exit(1);
         }
    }
```

Consulta el [ejemplo completo](https://github.com/awsdocs/aws-doc-sdk-examples/blob/ac748d8ef99cd17e297cb74fe13aa671e2679088/javav2/example_code/transcribe/src/main/java/com/amazonaws/transcribe/BidirectionalStreaming.java) en. GitHub

## Más información
<a name="more-info"></a>
+  [Cómo funciona](https://docs.aws.amazon.com//transcribe/latest/dg/how-it-works.html) en la guía para Amazon Transcribe desarrolladores.
+  [Introducción al audio en streaming](https://docs.aws.amazon.com//transcribe/latest/dg/getting-started.html) en la Guía para desarrolladores de Amazon Transcribe .