

# 使用 AWS SDK for Java 2.x 对 DynamoDB 进行编程
<a name="ProgrammingWithJava"></a>

本指南为想要将 Amazon DynamoDB 与 Java 结合使用的程序员提供了指导。本指南涵盖不同的概念，例如抽象层、配置管理、错误处理、控制重试策略和管理 Keep-Alive。

**Topics**
+ [关于 AWS SDK for Java 2.x](#AboutProgrammingWithJavaSDK)
+ [开始使用](#GetStartedProgrammingWithJavaSDK)
+ [适用于 Java 的 SDK 2.x 文档](#ProgrammingWithJavaUseDoc)
+ [支持的接口](#JavaInterfaces)
+ [其他代码示例](#AdditionalCodeEx)
+ [同步和异步编程](#SyncAsyncProgramming)
+ [HTTP 客户端](#HttpClients)
+ [Config](#ConfigHttpClient)
+ [错误处理](#JavaErrorHandling)
+ [AWS 请求 ID](#JavaRequestID)
+ [日志记录](#JavaLogging)
+ [分页](#JavaPagination)
+ [数据类注释](#JavaDataClassAnnotation)

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

您可以使用官方的 适用于 Java 的 AWS SDK 从 Java 访问 DynamoDB。适用于 Java 的 SDK 有两个版本：1.x 和 2.x。我们已于 2024 年 1 月 12 日[宣布](https://aws.amazon.com/blogs/developer/announcing-end-of-support-for-aws-sdk-for-java-v1-x-on-december-31-2025/)终止支持 1.x 版本。该版本将于 2024 年 7 月 31 日进入维护模式，其终止支持的截至日期为 2025 年 12 月 31 日。对于新开发，强烈建议您使用 2018 年首次发布的 2.x。本指南专门针对 2.x，仅重点介绍 SDK 中与 DynamoDB 相关的部分。

有关 AWS SDK 维护和支持的更多信息，请参阅《AWS SDK 和工具参考指南》**中的 [AWS SDK 和工具维护策略](https://docs.aws.amazon.com/sdkref/latest/guide/maint-policy.html)以及 [AWS SDK 和工具版本支持矩阵](https://docs.aws.amazon.com/sdkref/latest/guide/version-support-matrix.html)。

AWS SDK for Java 2.x 是对 1.x 代码库的重大重写。适用于 Java 的 SDK 2.x 支持现代 Java 功能，例如 Java 8 中引入的非阻塞 I/O。适用于 Java 的 SDK 2.x 还增加了对可插拔 HTTP 客户端实现的支持，从而提高了网络连接灵活性，并提供更多配置选项。

从适用于 Java 的 SDK 1.x 到适用于 Java 的 SDK 2.x 的一个明显变化是使用了新的软件包名称。Java 1.x SDK 使用 `com.amazonaws` 软件包名称，而 Java 2.x SDK 使用 `software.amazon.awssdk`。同样，Java 1.x SDK 的 Maven 构件使用 `com.amazonaws` `groupId`，而 Java 2.x SDK 构件使用 `software.amazon.awssdk` `groupId`。

**重要**  
适用于 Java 的 AWS SDK 1.x 有一个名为 `com.amazonaws.dynamodbv2` 的 DynamoDB 软件包。该软件包名称中的“v2”并不表示它适用于 Java 2（J2SE）。相反，“v2”表示该软件包支持 DynamoDB 低级 API 的[第二个版本](CurrentAPI.md)，而不支持低级 API 的[原始版本](Appendix.APIv20111205.md)。

### Java 版本支持
<a name="SupportedJavaVersions"></a>

AWS SDK for Java 2.x 为长期支持（LTS）[Java 版本](https://github.com/aws/aws-sdk-java-v2?tab=readme-ov-file#maintenance-and-support-for-java-versions)提供全面支持。

## 开始使用 AWS SDK for Java 2.x
<a name="GetStartedProgrammingWithJavaSDK"></a>

以下教程向您展示如何使用 [Apache Maven](https://maven.apache.org/) 为适用于 Java 的 SDK 2.x 定义依赖项。本教程还向您展示了如何编写连接到 DynamoDB 的代码，以列出可用的 DynamoDB 表。本指南中的教程基于《AWS SDK for Java 2.x 开发人员指南》**中的[开始使用 AWS SDK for Java 2.x](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html)教程。我们编辑本教程是为了调用 DynamoDB 而不是 Amazon S3。

**Topics**
+ [步骤 1：为本教程进行设置](#GetStartedJavaSetup)
+ [步骤 2：创建项目](#GetStartedJavaProjectSetup)
+ [步骤 3：编写代码](#GetStartedJavaCode)
+ [步骤 4：构建并运行应用程序](#GetStartedRunJava)

### 步骤 1：为本教程进行设置
<a name="GetStartedJavaSetup"></a>

在开始本教程之前，您需要满足以下条件：
+ 具有访问 Amazon DynamoDB 的权限。
+ 具有 Java 开发环境，该环境配置为能够使用 AWS 访问门户以单点登录方式访问 AWS 服务

要进行本教程的设置，请按照《AWS SDK for Java 2.x 开发人员指南》**中[安装概述](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html#setup-overview)中的说明操作。在为 Java SDK [将开发环境配置为单点登录访问](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html#setup-credentials)，并且 [AWS 访问门户会话处于活动状态](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html#setup-login-sso)后，请继续本教程的[步骤 2](#GetStartedJavaProjectSetup)。

### 步骤 2：创建项目
<a name="GetStartedJavaProjectSetup"></a>

要为本教程创建项目，您需要运行一条 Maven 命令，该命令会提示您输入有关如何配置项目的信息。完成所有输入并进行确认后，Maven 通过创建 `pom.xml` 文件并创建存根 Java 文件完成项目构建。

1. 打开终端或命令提示符窗口，然后导航到您选择的目录，例如您的 `Desktop` 或 `Home` 文件夹。

1. 在终端输入以下命令，然后按 **Enter**。

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

1. 为每个提示输入第二列中列出的值。    
[\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/ProgrammingWithJava.html)

1. 输入最后一个值后，Maven 会列出您所做的选择。要进行确认，请输入 **Y**。或者输入 **N**，然后重新输入您的选择。

Maven 会根据您输入的 `artifactId` 值创建名为 `getstarted` 的项目文件夹。在 `getstarted` 文件夹中，查找可以查看的、名为 `README.md` 的文件，以及 `pom.xml` 文件和 `src` 目录。

Maven 会构建以下目录树。

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

下面显示的是 `pom.xml` 项目文件的内容。

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

`dependencyManagement` 部分包含 AWS SDK for Java 2.x 的依赖项，而 `dependencies` 部分包含 DynamoDB 的依赖项。指定这些依赖项会强制 Maven 将相关的 `.jar` 文件包含到您的 Java 类路径中。默认情况下，AWS SDK 不包含所有 AWS 服务的所有类。对于 DynamoDB，如果您使用低级别接口，则应依赖 `dynamodb` 构件。或者，如果您使用高级别接口，则应依赖 `dynamodb-enhanced` 构件。如果您不包含相关依赖项，则无法编译您的代码。由于 `maven.compiler.source` 和 `maven.compiler.target` 属性中的值是 `1.8`，所以该项目使用 Java 1.8。

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

### 步骤 3：编写代码
<a name="GetStartedJavaCode"></a>

以下代码显示 Maven 创建的 `App` 类。`main` 方法是应用程序的入口点，它会创建 `Handler` 类的实例，然后调用其 `sendRequest` 方法。

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

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

Maven 创建的 `DependencyFactory` 类包含用于构建和返回 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html) 实例的 `dynamoDbClient` 工厂方法。`DynamoDbClient` 实例使用基于 Apache 的 HTTP 客户端的实例。这是因为您在 Maven 提示您输入使用哪个 HTTP 客户端时指定了 `apache-client`。

以下代码显示的是 `DependencyFactory` 类。

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

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

`Handler` 类包含程序的主要逻辑。在 `App` 类中创建 `Handler` 的实例时，`DependencyFactory` 将提供 `DynamoDbClient` 服务客户端。您的代码使用 `DynamoDbClient` 实例来调用 DynamoDB。

Maven 生成以下带有 `TODO` 注释的 `Handler` 类。本教程的下一步会将 *`TODO`* 注释替换为代码。

#### Maven 生成的 `Handler` 类
<a name="code-collapsible3"></a>

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

要填写逻辑，请将该 `Handler` 类的全部内容替换为以下代码。这将填写 `sendRequest` 方法并添加必要的导入。

#### 实现的 `Handler` 类
<a name="code-collapse4"></a>

以下代码使用 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html) 实例检索现有表的列表。如果给定账户和 AWS 区域存在表，该代码将使用 `Logger` 实例记录这些表的名称。

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

### 步骤 4：构建并运行应用程序
<a name="GetStartedRunJava"></a>

在创建项目并使其包含完整的 `Handler` 类后，构建并运行该应用程序。

1. 确保 AWS IAM Identity Center 会话处于活动状态。要进行确认，请运行 AWS Command Line Interface（AWS CLI）命令 `aws sts get-caller-identity` 并检查响应。如果您没有活动会话，请参阅[使用 AWS CLI 登录](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/setup.html#setup-login-sso)了解相关说明。

1. 打开终端或命令提示符窗口并导航至您的项目目录 `getstarted`。

1. 使用以下命令构件项目：

   ```
   mvn clean package
   ```

1. 使用以下命令运行应用程序：

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

查看文件后，删除对象，然后删除存储桶。

#### 成功
<a name="GetStartedSuccessJava"></a>

如果您的 Maven 项目生成和运行都没有错误，那么恭喜您！您已经使用适用于 Java 的 SDK 2.x. 成功构建了您的第一个 Java 应用程序。

#### 清理
<a name="GetStartedCleanupJava"></a>

要清理您在本教程中创建的资源，请删除项目文件夹 `getstarted`。

## 查看 AWS SDK for Java 2.x 文档
<a name="ProgrammingWithJavaUseDoc"></a>

[AWS SDK for Java 2.x 开发人员指南](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/home.html)中涵盖了所有 AWS 服务中 SDK 的方方面面。建议您查看以下主题：
+ [从版本 1.x 迁移到 2.x](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/migration.html) – 包括对 1.x 和 2.x 之间差异的详细说明。本主题还包含有关如何并行使用两个主要版本的说明。
+ [适用于 Java 2.x SDK 的 DynamoDB 指南](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/examples-dynamodb.html) – 向您展示如何执行基本的 DynamoDB 操作：创建表、操作项目和检索项目。这些示例均使用低级别接口。Java 有几个接口，如以下部分所述：[支持的接口](#JavaInterfaces)。

**提示**  
阅读这些主题后，请将 [AWS SDK for Java 2.x API 参考](https://sdk.amazonaws.com/java/api/latest/)加入书签。该参考涵盖了所有 AWS 服务，建议将其用作主要 API 参考。

## 支持的接口
<a name="JavaInterfaces"></a>

AWS SDK for Java 2.x 支持以下接口，具体取决于您所需的抽象级别。

**Topics**
+ [低级别接口](#LowLevelInterface)
+ [高级别接口](#HighLevelInterface)
+ [文档接口](#DocumentInterface)
+ [将接口与 `Query` 示例进行比较](#CompareJavaInterfacesQueryEx)

### 低级别接口
<a name="LowLevelInterface"></a>

低级别接口提供与底层服务 API 的一对一映射。每个 DynamoDB API 都可通过此接口提供。这意味着低级别接口可以提供完整的功能，但使用起来往往更加冗长且复杂。例如，您必须使用 `.s()` 函数来保存字符串，使用 `.n()` 函数保存数字。以下 [PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html) 示例使用低级别接口插入项目。

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

import java.util.Map;

public class PutItem {

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

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

### 高级别接口
<a name="HighLevelInterface"></a>

AWS SDK for Java 2.x 中的高级别接口称为 DynamoDB 增强型客户端。此接口提供了更为惯用的代码编写体验。

增强型客户端提供了一种在客户端数据类和专为存储该数据而设计的 DynamoDB 表之间进行映射的方法。您可以在代码中定义表与其相应模型类之间的关系。然后，您可以依靠 SDK 来管理数据类型操作。有关增强型客户端的更多信息，请参阅《AWS SDK for Java 2.x 开发人员指南》**中的 [DynamoDB 增强型客户端 API](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/dynamodb-enhanced-client.html)。

以下 [PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html) 示例使用高级别接口。在此示例中，名为 `YourItem` 的 `DynamoDbBean` 创建了一个 `TableSchema`，以将其直接用作 `putItem()` 调用的输入。

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

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

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

    @DynamoDbBean
    public static class YourItem {

        public YourItem() {}

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

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

        private int inventory;

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

        public String getPk() {
            return pk;
        }

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

        public String getSk() {
            return sk;
        }

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

        public String getItemData() {
            return itemData;
        }

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

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

适用于 Java 的 AWS SDK 1.x 有自己的高级别接口，通常由其主类 `DynamoDBMapper` 引用。AWS SDK for Java 2.x 发布在名为 `software.amazon.awssdk.enhanced.dynamodb` 的单独软件包（和 Maven 构件）中。Java 2.x SDK 通常由其主类 `DynamoDbEnhancedClient` 引用。

#### 使用不可变数据类的高级别接口
<a name="HighLevelInterfaceImmutableDataClasses"></a>

DynamoDB 增强型客户端 API 的映射特征也适用于不可变的数据类。不可变类只有 getter，且需要一个生成器类，使 SDK 可用来创建该类的实例。Java 中的不可变性是一种常用风格，开发人员可以使用它来创建没有副作用的类。这些类在复杂的多线程应用程序中的行为更具可预测性。不可变类不使用[High-level interface example](#highleveleg)中所示的 `@DynamoDbBean` 注释，而是使用 `@DynamoDbImmutable` 注释，该注释采用生成器类作为其输入。

以下示例使用生成器类 `DynamoDbEnhancedClientImmutablePutItem` 作为输入来创建表架构。然后，该示例提供架构作为 [PutItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_PutItem.html) API 调用的输入。

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

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

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

以下示例展示了不可变数据类。

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

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

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

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

    public String getItemData() {
        return itemData;
    }

    public int getInventory() {
        return inventory;
    }

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

        private YourImmutableItemBuilder() {}

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

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

#### 使用不可变数据类和第三方样板生成库的高级别接口
<a name="ImmutableDataClassesThirdPartyBoilerplateGenLib"></a>

上个示例中显示的不可变数据类需要一些样板代码。例如，`Builder` 类之外的数据类上的 getter 和 setter 逻辑。第三方库（例如 [Project Lombok](https://projectlombok.org/)）有助于您生成此类样板代码。减少大部分样板代码有助于限制使用不可变数据类和 AWS SDK 所需的代码量。这进一步提高了代码的编写效率和可读性。有关更多信息，请参阅《AWS SDK for Java 2.x 开发人员指南》**中的[使用 Lombok 等第三方库](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-use-immut.html#ddb-en-client-use-immut-lombok)。

以下示例展示了 Project Lombok 如何简化使用 DynamoDB 增强型客户端 API 所需的代码。

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

public class DynamoDbEnhancedClientImmutableLombokPutItem {

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

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

以下示例展示了不可变数据类的不可变数据对象。

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

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

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

`YourImmutableLombokItem` 类使用 Project Lombok 和 AWS SDK 提供的以下注释：
+ [@Builder](https://projectlombok.org/features/Builder) – 为 Project Lombok 提供的数据类生成复杂的生成器 API。
+ [@DynamoDbImmutable](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/mapper/annotations/DynamoDbImmutable.html) – 将 `DynamoDbImmutable` 类标识为 AWS SDK 提供的 DynamoDB 可映射实体注释。
+ [@Value](https://projectlombok.org/features/Value) – `@Data` 的不可变变体。默认情况下，所有字段均为私有和最终字段，并且不会生成 setter。Project Lombok 提供此注释。

### 文档接口
<a name="DocumentInterface"></a>

AWS SDK for Java 2.x 文档接口无需指定数据类型描述符。数据类型由数据本身的语义隐含。此文档接口与 适用于 Java 的 AWS SDK 1.x 的文档接口类似，但经过了重新设计。

下面的[Document interface example](#DocInterfaceEg)显示了使用文档接口表达的 `PutItem` 调用。该示例还使用了 EnhancedDocument。要使用增强型文档 API 对 DynamoDB 表执行命令，必须先将该表与您的文档表架构相关联，以创建 `DynamoDBTable` 资源对象。Document 表架构生成器需要一个主索引键和一个或多个属性转换器提供程序。

您可以使用 `AttributeConverterProvider.defaultProvider()` 转换默认类型的文档属性。您可以使用自定义 `AttributeConverterProvider` 实现来更改整体默认行为。您还可以更改单个属性的转换器。[AWS SDK 和工具参考指南](https://docs.aws.amazon.com/sdkref/latest/guide/version-support-matrix.html)提供了有关如何使用自定义转换器的更多详细信息和示例。它们主要用于没有默认转换器的域类的属性。使用自定义转换器，您可以为 SDK 提供写入或读取 DynamoDB 所需的信息。

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

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

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

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

}
```

要在 JSON 文档与原生 Amazon DynamoDB 数据类型之间相互转换，可以使用以下实用程序方法：
+ [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html#fromJson(java.lang.String)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html#fromJson(java.lang.String)) – 通过 JSON 字符串创建新的 EnhancedDocument 实例。
+ [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html#toJson()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/enhanced/dynamodb/document/EnhancedDocument.html#toJson()) – 创建文档的 JSON 字符串表示形式，以便在应用程序中像使用任何其他 JSON 对象一样使用它。

### 将接口与 `Query` 示例进行比较
<a name="CompareJavaInterfacesQueryEx"></a>

本部分展示了使用各种接口表达的相同 [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html) 调用。要微调这些查询的结果，请注意以下几点：
+ DynamoDB 将针对一个特定的分区键值，因此必须完全指定分区键。
+ 要使查询仅针对购物车商品，排序键必须有一个使用 `begins_with` 的键条件表达式。
+ 我们使用 `limit()` 将查询限制为最多 100 个返回项。
+ 我们将 `scanIndexForward` 设置为 false。结果按照 UTF-8 字节的顺序返回，这通常意味着首先返回编号最小的购物车商品。通过将 `scanIndexForward` 设置为 false，我们可以颠倒顺序，首先返回编号最大的购物车商品。
+ 我们会应用筛选条件来删除任何不符合条件的结果。无论商品是否与筛选条件匹配，筛选的数据都会消耗读取容量。

**Example 使用低级别接口的 `Query`**  
以下示例使用 `keyConditionExpression` 查询名为 `YourTableName` 的表。这会将查询限制为特定的分区键值和以特定前缀值开头的排序键值。这些关键条件限制了从 DynamoDB 读取的数据量。最后，该查询使用 `filterExpression` 筛选从 DynamoDB 检索的数据。  

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

import java.util.Map;

public class Query {

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

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

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

**Example 使用文档接口的 `Query`**  
以下示例使用文档接口查询名为 `YourTableName` 的表。  

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

import java.util.Map;

public class DynamoDbEnhancedDocumentClientQuery {

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

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

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

    }
}
```

**Example 使用高级别接口的 `Query`**  
以下示例使用 DynamoDB 增强型客户端 API 查询名为 `YourTableName` 的表。  

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

import java.util.Map;

public class DynamoDbEnhancedClientQuery {

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

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

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

    @DynamoDbBean
    public static class YourItem {

        public YourItem() {}

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

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

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

        public String getPk() {
            return pk;
        }

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

        public String getSk() {
            return sk;
        }

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

        public String getName() {
            return name;
        }
    }
}
```
**使用不可变数据类的高级别接口**  
使用高级别不可变数据类执行 `Query` 时，除了实体类 `YourItem` 或 `YourImmutableItem` 的构造之外，代码与高级别接口示例相同。有关更多信息，请参阅 [PutItem](#HighLevelImmutableDataClassEg) 示例。
**使用不可变数据类和第三方样板生成库的高级别接口**  
使用高级别不可变数据类执行 `Query` 时，除了实体类 `YourItem` 或 `YourImmutableLombokItem` 的构造之外，代码与高级别接口示例相同。有关更多信息，请参阅 [PutItem](#HighLevelImmutableDataClassEg) 示例。

## 其他代码示例
<a name="AdditionalCodeEx"></a>

有关如何将 DynamoDB 与适用于 Java 的 SDK 2.x 结合使用的其他示例，请参阅以下代码示例存储库：
+ [官方 AWS 单一操作代码示例](https://docs.aws.amazon.com/code-library/latest/ug/java_2_dynamodb_code_examples.html)
+ [社区维护的单一操作代码示例](https://github.com/aws-samples/aws-dynamodb-examples/tree/master/examples/SDK/java)
+ [官方 AWS 面向场景的代码示例](https://github.com/aws-samples/aws-dynamodb-examples/tree/master/examples/SDK/java)

## 同步和异步编程
<a name="SyncAsyncProgramming"></a>

AWS SDK for Java 2.x 为 DynamoDB 等 AWS 服务提供了*同步*和*异步*客户端。

`DynamoDbClient` 和 `DynamoDbEnhancedClient` 类提供了同步方法，这些方法会阻止执行您的线程，直到客户端接收到服务的响应。如果您不需要异步操作，则此客户端是与 DynamoDB 交互的最直接方式。

`DynamoDbAsyncClient` 和 `DynamoDbEnhancedAsyncClient` 类提供了异步方法，这些方法会立即返回，并控制调用的线程，而不必等待响应。非阻塞客户端的优势在于，它允许在几个线程之间实现高并发性，从而以最少的计算资源高效处理 I/O 请求。这会提高吞吐量和响应能力。

AWS SDK for Java 2.x 使用对非阻塞 I/O 的本机支持。适用于 Java 的 AWS SDK 1.x 必须模拟非阻塞 I/O。

由于同步方法在收到响应之前返回，所以需要通过某种方法在响应准备就绪时接收响应。适用于 Java 的 AWS SDK 中的异步方法会返回 [https://docs.oracle.com/javase/8/docs/api/index.html?java/util/concurrent/CompletableFuture.html](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/concurrent/CompletableFuture.html) 对象，其中包含未来的异步操作的结果。当您在这些 `CompletableFuture` 对象上调用 `get()` 或 `join()` 时，您的代码将阻塞，直到结果可用。如果您在发出请求的同时进行此调用，则其行为与普通的同步调用类似。

有关异步编程的更多信息，请参阅《AWS SDK for Java 2.x 开发人员指南》**中的[使用异步编程](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/asynchronous.html)。

## HTTP 客户端
<a name="HttpClients"></a>

为了支持每个客户端，有一个处理与 AWS 服务的通信的 HTTP 客户端。您可以插入备用 HTTP 客户端，选择一个具有最适合您应用程序的特性的客户端。有些更轻量；有些则有更多的配置选项。

有些 HTTP 客户端仅支持同步使用，而另一些则仅支持异步使用。有关可帮助您为工作负载选择最佳 HTTP 客户端的流程图，请参阅《AWS SDK for Java 2.x 开发人员指南》**中的 [HTTP 客户端建议](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration.html#http-clients-recommend)。

以下列表列出了一些可能的 HTTP 客户端：

**Topics**
+ [基于 Apache 的 HTTP 客户端](#ApacheHttpClient)
+ [基于 `URLConnection` 的 HTTP 客户端](#URLConnHttpClient)
+ [基于 Netty 的 HTTP 客户端](#NettyHttpClient)
+ [基于 AWS CRT 的 HTTP 客户端](#AWSCRTHttpClient)

### 基于 Apache 的 HTTP 客户端
<a name="ApacheHttpClient"></a>

[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/apache/ApacheHttpClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/apache/ApacheHttpClient.html) 类支持同步服务客户端。它是实现同步使用的默认 HTTP 客户端。有关配置 `ApacheHttpClient` 类的信息，请参阅《AWS SDK for Java 2.x 开发人员指南》**中的[配置基于 Apache 的 HTTP 客户端](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-apache.html)。

### 基于 `URLConnection` 的 HTTP 客户端
<a name="URLConnHttpClient"></a>

[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/urlconnection/UrlConnectionHttpClient.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/http/urlconnection/UrlConnectionHttpClient.html) 类是同步客户端的另一种选择。其加载速度比基于 Apache 的 HTTP 客户端快，但功能较少。有关配置 `UrlConnectionHttpClient` 类的信息，请参阅《AWS SDK for Java 2.x 开发人员指南》**中的[配置基于 URLConnection 的 HTTP 客户端](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-url.html)。

### 基于 Netty 的 HTTP 客户端
<a name="NettyHttpClient"></a>

`NettyNioAsyncHttpClient` 类支持异步客户端。这是实现异步使用的默认选择。有关配置 `NettyNioAsyncHttpClient` 类的信息，请参阅《AWS SDK for Java 2.x 开发人员指南》**中的[配置基于 Netty 的 HTTP 客户端](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-netty.html)。

### 基于 AWS CRT 的 HTTP 客户端
<a name="AWSCRTHttpClient"></a>

AWS 公共运行时（CRT）库中的较新 `AwsCrtHttpClient` 和 `AwsCrtAsyncHttpClient` 类提供了更多支持同步和异步客户端的选项。与其它 HTTP 客户端相比，AWS CRT 可提供：
+ 更快的 SDK 启动时间
+ 更小的内存占用空间
+ 缩短了延迟时间
+ 连接运行状况管理
+ DNS 负载均衡

有关配置 `AwsCrtHttpClient` 和 `AwsCrtAsyncHttpClient` 类的信息，请参阅《AWS SDK for Java 2.x 开发人员指南》**中的[配置基于 AWS CRT 的 HTTP 客户端](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-crt.html)。

基于 AWS CRT 的 HTTP 客户端不是默认选项，因为它会破坏现有应用程序的向后兼容性。但是，对于 DynamoDB，不管是同步使用还是异步使用，都建议使用基于 AWS CRT 的 HTTP 客户端。

有关基于 AWS CRT 的 HTTP 客户端的介绍，请参阅 AWS 开发人员工具博客**中的[宣布在 AWS SDK for Java 2.x 中推出 AWS CRT HTTP 客户端](https://aws.amazon.com/blogs/developer/announcing-availability-of-the-aws-crt-http-client-in-the-aws-sdk-for-java-2-x/)。

## 配置 HTTP 客户端
<a name="ConfigHttpClient"></a>

配置客户端时，您可以提供各种配置选项，包括：
+ 为 API 调用的不同方面设置超时。
+ 启用 TCP Keep-Alive。
+ 控制遇到错误时的重试策略。
+ 指定[执行拦截器](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/interceptors.html)实例可以修改的执行属性。执行拦截器可以编写代码，拦截 API 请求和响应的执行。这使您能够执行任务，例如发布指标和修改动态请求。
+ 添加或操作 HTTP 标头。
+ 启用对[客户端性能指标](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/metrics.html)的跟踪。使用此特征有助于您收集应用程序中服务客户端的指标，并在 Amazon CloudWatch 中分析输出。
+ 指定用于调度任务（例如异步重试尝试和超时任务）的备用执行器服务。

您可以通过向服务客户端 `Builder` 类提供 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/client/config/ClientOverrideConfiguration.html) 对象来控制配置。您将在以下部分的一些代码示例中看到这一点。

`ClientOverrideConfiguration` 提供了标准配置选项。不同的可插拔 HTTP 客户端也有实现特定的配置可能性。

**Topics**
+ [超时配置](#TimeoutConfig)
+ [RetryMode](#RetryMode)
+ [DefaultsMode](#DefaultsMode)
+ [Keep-Alive 配置](#KeepAliveConfig)

### 超时配置
<a name="TimeoutConfig"></a>

您可以调整客户端配置，来控制与服务调用相关的各种超时。与其他 AWS 服务相比，DynamoDB 的延迟更低。因此，您可能需要将这些属性调整为较低的超时值，以便在出现网络问题时可以快速失效。

您可以在 DynamoDB 客户端上使用 `ClientOverrideConfiguration` 或通过更改底层 HTTP 客户端实现的详细配置选项来自定义与延迟相关的行为。

您可以使用 `ClientOverrideConfiguration` 配置以下有影响力的属性：
+ `apiCallAttemptTimeout` – 在放弃和超时之前，等待单次 HTTP 请求尝试完成的时间。
+ `apiCallTimeout` – 客户端完全执行 API 调用所需的时间。这包括由所有 HTTP 请求（包括重试）组成的请求处理程序执行。

AWS SDK for Java 2.x 为某些超时选项（例如连接超时和套接字超时等）提供了[默认值](https://github.com/aws/aws-sdk-java-v2/blob/a0c8a0af1fa572b16b5bd78f310594d642324156/http-client-spi/src/main/java/software/amazon/awssdk/http/SdkHttpConfigurationOption.java#L134)。SDK 不为 API 调用超时或单个 API 调用尝试超时提供默认值。如果未在 `ClientOverrideConfiguration` 中设置这些超时值，SDK 将使用套接字超时值，而不是整体 API 调用超时值。套接字超时的默认值为 30 秒。

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

您应该考虑的另一个与超时配置相关的配置是 `RetryMode` 配置对象。此配置对象包含一组重试行为。

适用于 Java 的 SDK 2.x 支持以下重试模式：
+ `legacy` – 默认重试模式，如果您未明确更改。这种重试模式特定于 Java SDK。它的特点是最多重试 3 次，对于 DynamoDB 等服务来说，重试次数更多，最多为 8 次。
+ `standard` – 之所以命名为“标准”，是因为它与其他 AWS SDK 更加一致。对于首次重试，此模式随机等待 0 毫秒到 1000 毫秒不等的时间。如果需要再次重试，此模式会从 0 毫秒到 1000 毫秒之间随机选择另一个时间，然后将其乘以二。如果需要更多重试，它会进行相同的随机选择，然后乘以 4，依此类推。每次等待的上限为 20 秒。与 `legacy` 模式相比，此模式会对检测到的更多故障条件执行重试。对于 DynamoDB，除非您使用 [numRetries](#numRetries) 进行覆盖，否则它最多总共执行三次尝试。
+ `adaptive` – 基于 `standard` 模式构建，动态限制 AWS 请求速率以最大限度提高成功率。这样做可能会以牺牲请求延迟为代价。当可预测的延迟很重要时，不建议使用自适应重试模式。

您可以在**《AWS SDK 和工具参考指南》的[重试行为](https://docs.aws.amazon.com/sdkref/latest/guide/feature-retry-behavior.html)主题中找到这些重试模式的扩展定义。

#### 重试策略
<a name="RetryPolicies"></a>

所有 `RetryMode` 配置都有 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/RetryPolicy.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/RetryPolicy.html)，后者基于一个或多个 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/RetryCondition.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/RetryCondition.html) 配置而构建。[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/TokenBucketRetryCondition.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/TokenBucketRetryCondition.html) 对于 DynamoDB SDK 客户端实现的重试行为尤其重要。此条件限制了 SDK 使用令牌存储桶算法进行的重试次数。根据所选的重试模式，节流异常可能会，也可能不会从 `TokenBucket` 中减去令牌。

当客户端遇到可重试错误（例如节流异常或临时服务器错误）时，SDK 将自动重试请求。您可以控制这些重试发生的次数和频率。

配置客户端时，您可以提供支持以下参数的 `RetryPolicy`：
+ `numRetries` – 在认为请求失败之前应当应用的最多重试次数。无论您使用哪种重试模式，默认值都是 8。
**警告**  
请务必在适当考虑后更改此默认值。
+ `backoffStrategy` – 将 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/backoff/BackoffStrategy.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/backoff/BackoffStrategy.html) 应用于重试，[https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/backoff/FullJitterBackoffStrategy.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/backoff/FullJitterBackoffStrategy.html) 成为默认策略。此策略根据当前的重试次数、基本延迟和最大回退时间，在额外重试之间执行指数延迟。然后它会添加抖动以提供一点随机性。无论重试模式如何，指数延迟中使用的基本延迟均为 25 毫秒。
+ `retryCondition` – [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/RetryCondition.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/retry/conditions/RetryCondition.html) 决定是否完全重试请求。默认情况下，它将重试一组它认为可以重试的特定 HTTP 状态码和异常。在大多数情况下，默认配置应该足够了。

以下代码提供了另一种重试策略。它指定总共五次重试（总共六次请求）。首次重试应在大约 100 毫秒延迟之后进行，每增加一次重试，该时间成倍增加，最多延迟一秒。

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

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

通常通过指定 `DefaultsMode` 来隐式配置 `ClientOverrideConfiguration` 和 `RetryMode` 不管理的超时属性。

AWS SDK for Java 2.x（2.17.102 或更高版本）引入了对 `DefaultsMode` 的支持。此功能为常见的可配置设置（例如 HTTP 通信设置、重试行为、服务区域端点设置，可能还包括任何与 SDK 相关的配置）提供一组默认值。使用此功能时，您可以获得针对常见使用场景量身定制的新配置默认值。

所有 AWS SDK 的默认模式均已标准化。适用于 Java 的 SDK 2.x 支持以下默认模式：
+ `legacy` – 提供默认设置，这些设置因 AWS SDK 而异，并且在建立 `DefaultsMode` 之前就已存在。
+ `standard` – 为大多数场景提供默认的非优化设置。
+ `in-region` – 基于标准模式构建，包括为从同一 AWS 区域 内部调用 AWS 服务 的应用程序量身定制的设置。
+ `cross-region` – 基于标准模式构建，包括为调用不同区域中 AWS 服务的应用程序量身定制的高超时设置。
+ `mobile` – 基于标准模式构建，包括为延迟较高的移动应用程序量身定制的高超时设置。
+ `auto` – 基于标准模式构建，包括实验功能。SDK 会尝试发现运行时系统环境以自动确定适当的设置。自动检测是基于启发式的，无法提供 100% 的准确性。如果无法确定运行时环境，则使用标准模式。自动检测可能会查询[实例元数据和用户数据](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html)，这可能会带来延迟。如果启动延迟对您的应用程序而言至关重要，建议您改为选择显式 `DefaultsMode` 延迟。

您可以通过以下方式配置默认模式：
+ 直接通过 `AwsClientBuilder.Builder#defaultsMode(DefaultsMode)` 在客户端上配置。
+ 通过 `defaults_mode` 配置文件属性在配置文件上配置。
+ 通过 `aws.defaultsMode` 系统属性进行全局配置。
+ 通过 `AWS_DEFAULTS_MODE` 环境变量进行全局配置。

**注意**  
对于除 `legacy` 之外的任何模式，随着最佳实践不断改进，提供的默认值可能会发生变化。因此，如果您使用的是 `legacy` 以外的模式，建议您在升级 SDK 时进行测试。

《AWS SDK 和工具参考指南》**中的[智能配置默认值](https://docs.aws.amazon.com/sdkref/latest/guide/feature-smart-config-defaults.html)提供了不同默认模式下的配置属性及其默认值的列表。

您可以根据应用程序的特性以及与之互动的 AWS 服务选择默认模式值。

配置这些值时应考虑广泛的 AWS 服务选择。对于将 DynamoDB 表和应用程序部署在一个区域中的典型 DynamoDB 部署，`in-region` 默认模式在 `standard` 默认模式中最相关。

**Example 针对低延迟调用调整的示例 DynamoDB SDK 客户端配置**  
以下示例将预期的低延迟 DynamoDB 调用的超时调整为较低的值。  

```
DynamoDbAsyncClient asyncClient = DynamoDbAsyncClient.builder()
    .defaultsMode(DefaultsMode.IN_REGION)
    .httpClientBuilder(AwsCrtAsyncHttpClient.builder())
    .overrideConfiguration(ClientOverrideConfiguration.builder()
        .apiCallTimeout(Duration.ofSeconds(3))
        .apiCallAttemptTimeout(Duration.ofMillis(500))
        .build())
    .build();
```
单独的 HTTP 客户端实现让您可以对超时和连接使用行为进行更精细的控制。例如，对于基于 AWS CRT 的客户端，可以启用 `ConnectionHealthConfiguration`，以便该客户端能够主动监控所用连接的运行状况。有关更多信息，请参阅《AWS SDK for Java 2.x 开发人员指南》**中的[基于 AWS CRT 的 HTTP 客户端的高级配置](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration-crt.html#configuring-the-crt-based-http-client)。

### Keep-Alive 配置
<a name="KeepAliveConfig"></a>

启用 keep-alive 可以通过重复使用连接来减少延迟。有两种不同的 keep-alive：HTTP Keep-Alive 和 TCP Keep-Alive。
+ HTTP Keep-Alive 尝试维护客户端和服务器之间的 HTTPS 连接，以便以后的请求可以重复使用该连接。这会跳过对以后请求进行重量级 HTTPS 身份验证。默认情况下，在所有客户端上启用 HTTP Keep-Alive。
+ TCP Keep-Alive 请求底层操作系统通过套接字连接发送小数据包，以进一步保证套接字保持活动状态并立即检测任何丢包。这将确保后续请求不会花时间尝试使用丢失的套接字。默认情况下，在所有客户端上禁用 TCP Keep-Alive。以下代码示例演示了如何在每个 HTTP 客户端上启用该功能。当为所有不是基于 CRT 的 HTTP 客户端启用时，实际的 Keep-Alive 机制取决于操作系统。因此，您必须通过操作系统配置其他 TCP Keep-Alive 值，例如超时和数据包数量。您可以在 Linux 或 Mac 计算机上使用 `sysctl` 来执行此操作，也可以在 Windows 计算机上使用注册表值来执行此操作。

**Example 在基于 Apache 的 HTTP 客户端上启用 TCP Keep-Alive**  

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

**基于 `URLConnection` 的 HTTP 客户端**  
任何使用基于 `URLConnection` 的 HTTP 客户端 [https://docs.oracle.com/javase/8/docs/api/java/net/HttpURLConnection.html](https://docs.oracle.com/javase/8/docs/api/java/net/HttpURLConnection.html) 的同步客户端都没有启用 Keep-Alive 的[机制](https://docs.oracle.com/javase/8/docs/api/java/net/doc-files/net-properties.html)。

**Example 在基于 Netty 的 HTTP 客户端上启用 TCP Keep-Alive**  

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

**Example 在基于 AWS CRT 的 HTTP 客户端上启用 TCP Keep-Alive**  
对于基于 AWS CRT 的 HTTP 客户端，您可以启用 TCP Keep-Alive 并控制持续时间。  

```
DynamoDbClient client = DynamoDbClient.builder()
    .httpClientBuilder(AwsCrtHttpClient.builder()
    .tcpKeepAliveConfiguration(TcpKeepAliveConfiguration.builder()
        .keepAliveInterval(Duration.ofSeconds(50))
        .keepAliveTimeout(Duration.ofSeconds(5))
        .build()))
    .build();
```
使用异步 DynamoDB 客户端时，您可以启用 TCP Keep-Alive，如以下代码所示。  

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

## 错误处理
<a name="JavaErrorHandling"></a>

在异常处理方面，AWS SDK for Java 2.x 使用运行时（未经核查的）异常。

涵盖所有 SDK 异常的基本异常是 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkServiceException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkServiceException.html)，它扩展自 Java 未经核查的 `RuntimeException`。如果您捕获此异常，就会捕获 SDK 引发的所有异常。

`SdkServiceException` 有一个名为 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/exception/AwsServiceException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/awscore/exception/AwsServiceException.html) 的子类。此子类表示与 AWS 服务通信时出现的任何问题。它有一个名为 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/DynamoDbException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/DynamoDbException.html) 的子类，这表示在与 DynamoDB 通信时出现问题。如果您捕获此异常，就会捕获与 DynamoDB 相关的所有异常，但不会捕获其他 SDK 异常。

`DynamoDbException` 下有更具体的[异常类型](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/DynamoDbException.html)。其中一些异常类型适用于控制面板操作，例如 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/TableAlreadyExistsException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/TableAlreadyExistsException.html)。其他异常适用于数据面板操作。以下是常见的数据面板异常的示例：
+ [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ConditionalCheckFailedException.html](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/model/ConditionalCheckFailedException.html) – 在请求中指定计算结果为 false 的条件。例如，您可能已尝试对项目执行有条件更新，但属性的实际值与条件预期值不匹配。不会重试以这种方式失败的请求。

其他情况没有定义特定的异常。例如，当您的请求受到限制时，可能会引发特定 `ProvisionedThroughputExceededException`，而在其他情况下，会引发更通用的 `DynamoDbException`。无论哪种情况，都可以通过检查 `isThrottlingException()` 是否返回 `true` 来确定异常是否由节流引起。

根据您的应用程序需求，您可以捕获所有 `AwsServiceException` 或 `DynamoDbException` 实例。但是，您通常需要在不同的情况下采取不同的行为。处理条件检查失败的逻辑与处理节流不同。定义要处理的异常路径，并确保测试替代路径。这有助于确保您能够处理所有相关场景。

有关您可能遇到的常见错误的列表，请参阅 [DynamoDB 错误处理](Programming.Errors.md)。另请参阅《Amazon DynamoDB API 参考》**中的[常见错误](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/CommonErrors.html)。API 参考还提供每个 API 操作（例如 [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html) 操作）可能发生的确切错误。有关处理异常的信息，请参阅《AWS SDK for Java 2.x 开发人员指南》**中的[AWS SDK for Java 2.x 异常处理](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/handling-exceptions.html)。

## AWS 请求 ID
<a name="JavaRequestID"></a>

每个请求都包含一个请求 ID，如果您正在与 AWS 支持 部门合作诊断问题，则该 ID 会非常有用。派生自 `SdkServiceException` 的每个异常都有一个可用于检索请求 ID 的 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkServiceException.html#requestId()](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/exception/SdkServiceException.html#requestId()) 方法。

## 日志记录
<a name="JavaLogging"></a>

使用 SDK 提供的日志记录既可以捕获客户端库中的任何重要消息，也可以帮助您进行更深入的调试。记录器是分层的，SDK 将 `software.amazon.awssdk` 用作其根记录器。可以使用 `TRACE`、`DEBUG`、`INFO`、`WARN`、`ERROR`、`ALL` 或 `OFF` 中的一个设置来配置记录器级别。所配置的级别将应用于该记录器并向下应用到记录器层次结构。

AWS SDK for Java 2.x 使用 Simple Logging Façade for Java（SLF4J）进行日志记录。这充当其他记录器周围的抽象层，您可以用它来插入自己喜欢的记录器。有关插入记录器的说明，请参阅 [SLF4J 用户手册](https://www.slf4j.org/manual.html)。

每个记录器都有特定的行为。默认情况下，Log4j 2.x 记录器会创建一个 `ConsoleAppender`，后者将日志事件附加到 `System.out`，并默认处于 `ERROR` 日志级别。

SLF4J 输出中包含的 SimpleLogger 记录器默认为 `System.err`，并默认处于 `INFO` 日志级别。

建议将任何生产部署的 `software.amazon.awssdk` 的级别设置为 `WARN`，以捕获来自 SDK 客户端库的任何重要消息，同时限制输出数量。

如果 SLF4J 在类路径上找不到支持的记录器（没有 SLF4J 绑定），它将默认为[无操作实现](https://www.slf4j.org/codes.html#noProviders)。此实现会导致将消息记录到 `System.err`，解释 SLF4J 在类路径上找不到记录器实现。为了防止出现这种情况，您必须添加记录器实现。为此，您可以在 Apache Maven `pom.xml` 中添加构件依赖项，例如 `org.slf4j.slf4j-simple` 或 `org.apache.logging.log4j.log4j-slf4j2-imp`。

有关如何在 SDK 中配置日志记录，包括向应用程序配置中添加日志记录依赖项的信息，请参阅《适用于 Java 的 AWS SDK 开发人员指南》**中的[适用于 Java 的 SDK 2.x 日志记录](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/logging-slf4j.html)。

`Log4j2.xml` 文件中的以下配置显示了在使用 Apache Log4j 2 记录器时如何调整日志记录行为。此配置将根记录器级别设置为 `WARN`。层次结构中的所有记录器（包括 `software.amazon.awssdk` 记录器）将继承此日志级别。

默认情况下，输出将转到 `System.out`。在以下示例中，我们仍然覆盖默认的输出 Log4j Appender 以应用定制的 Log4j `PatternLayout`。

**`Log4j2.xml` 配置文件示例**  
以下配置在所有日志记录器层次结构的 `ERROR` 和 `WARN` 级别，将消息记录到控制台。

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

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

### AWS 请求 ID 日志记录
<a name="JavaReqIDLogging"></a>

当出现问题时，您可以在异常中找到请求 ID。但是，如果您想要未生成异常的请求的请求 ID，可以使用日志记录。

`software.amazon.awssdk.request` 记录器在 `DEBUG` 级别输出请求 ID。以下示例扩展了前面的[configuration example](#Log4j2ConfigEg)，将根记录器级别保持在 `ERROR`、将 `software.amazon.awssdk` 保持在级别 `WARN`，将 `software.amazon.awssdk.request` 保持在级别 `DEBUG`。设置这些级别有助于捕获请求 ID 和其他与请求相关的详细信息，例如端点和状态码。

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

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

以下是日志输出的示例：

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

## 分页
<a name="JavaPagination"></a>

某些请求（例如 [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html) 和 [https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Scan.html)）会限制针对单个请求返回的数据大小，并要求您重复请求才能显示后续页面。

您可以使用 `Limit` 参数控制每页可读取的最大项目数。例如，您可以使用 `Limit` 参数仅检索最后 10 个项目。此限制指定在应用任何筛选条件之前应从表中读取多少项目。如果您希望筛选后正好有 10 个项目，则没有办法指定。只有在实际检索到 10 个项目后，才能控制预先筛选的数量并检查客户端。不管限制如何，每个响应最多允许 1 MB 的大小。

`LastEvaluatedKey` 可能包含在 API 响应中。这表示响应因达到数量限制或大小限制而结束。此密钥是针对该响应评估的最后一个密钥。通过直接与 API 交互，您可以检索此 `LastEvaluatedKey` 并将其作为 `ExclusiveStartKey` 传递给后续调用，以便从该起点读取下一个数据块。如果未返回 `LastEvaluatedKey`，则表示没有更多与 `Query` 或 `Scan` API 调用匹配的项目。

以下示例使用低级别接口根据 `keyConditionExpression` 参数将项目限制为 100。

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

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

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

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

AWS SDK for Java 2.x 可通过提供自动分页方法（这些方法可进行多个服务调用以自动为您获取后续页面的结果）来简化与 DynamoDB 的交互。这简化了您的代码，但会消除对资源使用的一些控制，而手动读取页面可以保持这些控制。

通过使用 DynamoDB 客户端中提供的 `Iterable` 方法（例如 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html#queryPaginator(software.amazon.awssdk.services.dynamodb.model.QueryRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html#queryPaginator(software.amazon.awssdk.services.dynamodb.model.QueryRequest)) 和 [https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html#scanPaginator(software.amazon.awssdk.services.dynamodb.model.ScanRequest)](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/dynamodb/DynamoDbClient.html#scanPaginator(software.amazon.awssdk.services.dynamodb.model.ScanRequest))），SDK 可以负责分页。这些方法的返回类型是一个自定义的可迭代对象，您可以用它来遍历所有页面。SDK 在内部处理服务调用。使用 Java Stream API 可以处理 `QueryPaginator` 的结果，如以下示例所示。

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

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

## 数据类注释
<a name="JavaDataClassAnnotation"></a>

Java SDK 提供了几个注释，您可以将这些注释放在数据类的属性上。这些注释影响 SDK 与属性的交互方式。通过添加注释，您可以让属性充当隐式原子计数器，维护自动生成的时间戳值，或跟踪项目版本号。有关更多信息，请参阅[数据类注释](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/ddb-en-client-anno-index.html)。