

# 使用 DynamoDB 和 AWS SDK 编程
<a name="Programming"></a>

本部分介绍开发人员相关主题。如果要改为运行代码示例，请参阅[运行本开发人员指南中的代码示例](CodeSamples.md)。

**注意**  
2017 年 12 月，AWS开始迁移所有 Amazon DynamoDB 终端节点以使用由 Amazon Trust Services (ATS) 颁发的安全证书。有关更多信息，请参阅 [使用 DynamoDB 解决 SSL/TLS 连接建立问题](ats-certs.md)。

**Topics**
+ [DynamoDB 的 AWS SDK 支持概述](Programming.SDKOverview.md)
+ [使用 Python 和 Boto3 对 Amazon DynamoDB 进行编程](programming-with-python.md)
+ [使用 JavaScript 对 Amazon DynamoDB 进行编程](programming-with-javascript.md)
+ [使用 AWS SDK for Java 2.x 对 DynamoDB 进行编程](ProgrammingWithJava.md)
+ [DynamoDB 错误处理](Programming.Errors.md)
+ [结合使用 DynamoDB 与 AWS SDK](sdk-general-information-section.md)

# DynamoDB 的 AWS SDK 支持概述
<a name="Programming.SDKOverview"></a>

下图高度概述了使用 AWS SDK 编程 Amazon DynamoDB 应用程序。

![\[将 DynamoDB 与 AWS SDK 结合使用的编程模型。\]](http://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/images/SDKSupport.png)


1. 使用适用于您的编程语言的 AWS SDK 编写一个应用程序。

1. 每个 AWS 开发工具包提供一个或多个编程接口，用于使用 DynamoDB。可用的具体接口取决于您使用的编程语言和 AWS SDK。选项包括：
   + [与 DynamoDB 配合使用的低级别接口](Programming.SDKs.Interfaces.md#Programming.SDKs.Interfaces.LowLevel)
   + [与 DynamoDB 配合使用的文档接口](Programming.SDKs.Interfaces.md#Programming.SDKs.Interfaces.Document)
   + [与 DynamoDB 配合使用的对象持久化接口](Programming.SDKs.Interfaces.md#Programming.SDKs.Interfaces.Mapper)
   + [高级别接口](HigherLevelInterfaces.md)

1. AWS SDK 构造 HTTP(S) 请求，以便与低级 DynamoDB API 一起使用。

1. AWS SDK 将请求发送到 DynamoDB 终端节点。

1. DynamoDB 运行请求。如果请求成功，则 DynamoDB 将返回 HTTP 200 响应代码（确定）。如果请求不成功，DynamoDB 将返回 HTTP 错误代码和错误消息。

1. AWS SDK 处理响应并将其传播回您的应用程序。

每个 AWS SDK 为您的应用程序提供重要服务，包括以下内容：
+ 设置 HTTP(S) 请求格式和序列化请求参数。
+ 为每个请求生成加密签名。
+ 将请求转发到 DynamoDB 端点并接收来自 DynamoDB 的响应。
+ 从这些响应中提取结果。
+ 在出现错误时实现基本重试逻辑。

您无需为上述任何任务编写代码。

**注意**  
有关 AWS SDK 的更多信息（包括安装说明和文档），请参阅[用于 Amazon Web Services 的工具](https://aws.amazon.com/tools)。

## SDK 对基于 AWS 账户的端点的支持
<a name="Programming.SDKs.endpoints"></a>

AWS 正在为 DynamoDB 推出 SDK 对基于 AWS 账户的端点的支持，以 2024 年 9 月 4 日推出的适用于 Java 的 AWS SDK V1 开始。这些新端点可协助 AWS 确保高性能和可扩展性。更新后的 SDK 将自动使用新端点，其格式为 `https://(account-id).ddb.(region).amazonaws.com`。

如果您使用 SDK 客户端的单个实例向多个账户发出请求，则应用程序重用连接的机会就会减少。AWS 建议修改应用程序，以便在每个 SDK 客户端实例上连接到更少的账户。另一种方法是使用 `ACCOUNT_ID_ENDPOINT_MODE` 设置将 SDK 客户端设置为继续使用区域端点，如 [https://docs.aws.amazon.com/sdkref/latest/guide/feature-account-endpoints.html](https://docs.aws.amazon.com/sdkref/latest/guide/feature-account-endpoints.html) 中所述。

# 与 DynamoDB 配合使用的编程接口
<a name="Programming.SDKs.Interfaces"></a>

每个 [AWS SDK](https://aws.amazon.com/tools) 提供了一个或多个用于使用 Amazon DynamoDB 的编程接口。这些接口范围从简单的低级 DynamoDB 包装到面向对象的持久层。可用接口因您使用的 AWS SDK 和编程语言而不同。

![\[不同 AWS SDK 中提供了用于 DynamoDB 的编程接口。\]](http://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/images/SDKSupport.SDKInterfaces.png)


以下部分重点介绍了一些可用的接口，使用适用于 Java 的 AWS SDK作为一个示例。（并非所有接口都可用于所有 AWS SDK。）

**Topics**
+ [与 DynamoDB 配合使用的低级别接口](#Programming.SDKs.Interfaces.LowLevel)
+ [与 DynamoDB 配合使用的文档接口](#Programming.SDKs.Interfaces.Document)
+ [与 DynamoDB 配合使用的对象持久化接口](#Programming.SDKs.Interfaces.Mapper)

## 与 DynamoDB 配合使用的低级别接口
<a name="Programming.SDKs.Interfaces.LowLevel"></a>

每个语言特定 AWS SDK 为 Amazon DynamoDB 提供了一个低级别接口，其方法与低级别 DynamoDB API 请求非常相似。

在某些情况下，您需要使用 [数据类型描述符](Programming.LowLevelAPI.md#Programming.LowLevelAPI.DataTypeDescriptors) 识别数据类型，例如 `S` 对于字符串，`N` 对于数字。

**注意**  
一个低级别接口可用于每种特定语言 AWS SDK。

下面的 Java 程序使用低级别 适用于 Java 的 AWS SDK 接口。

### 低级别接口示例
<a name="low-level-example"></a>

```
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;

/**
 * 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
 *
 * To get an item from an Amazon DynamoDB table using the AWS SDK for Java V2,
 * its better practice to use the
 * Enhanced Client, see the EnhancedGetItem example.
 */
public class GetItem {
    public static void main(String[] args) {
        final String usage = """

                Usage:
                    <tableName> <key> <keyVal>

                Where:
                    tableName - The Amazon DynamoDB table from which an item is retrieved (for example, Music3).\s
                    key - The key used in the Amazon DynamoDB table (for example, Artist).\s
                    keyval - The key value that represents the item to get (for example, Famous Band).
                """;

        if (args.length != 3) {
            System.out.println(usage);
            System.exit(1);
        }

        String tableName = args[0];
        String key = args[1];
        String keyVal = args[2];
        System.out.format("Retrieving item \"%s\" from \"%s\"\n", keyVal, tableName);
        Region region = Region.US_EAST_1;
        DynamoDbClient ddb = DynamoDbClient.builder()
                .region(region)
                .build();

        getDynamoDBItem(ddb, tableName, key, keyVal);
        ddb.close();
    }

    public static void getDynamoDBItem(DynamoDbClient ddb, String tableName, String key, String keyVal) {
        HashMap<String, AttributeValue> keyToGet = new HashMap<>();
        keyToGet.put(key, AttributeValue.builder()
                .s(keyVal)
                .build());

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

        try {
            // If there is no matching item, GetItem does not return any data.
            Map<String, AttributeValue> returnedItem = ddb.getItem(request).item();
            if (returnedItem.isEmpty())
                System.out.format("No item found with the key %s!\n", key);
            else {
                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());
                }
            }

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

## 与 DynamoDB 配合使用的文档接口
<a name="Programming.SDKs.Interfaces.Document"></a>

许多 AWS SDK 提供文档接口，允许您对表和索引执行数据层面操作（创建、读取、更新、删除）。对于文档接口，无需指定 [数据类型描述符](Programming.LowLevelAPI.md#Programming.LowLevelAPI.DataTypeDescriptors)。数据类型由数据本身的语义隐含。这些 AWS SDK 还提供了一些方法，可以在 JSON 文档转换为 Amazon DynamoDB 本机数据类型之间轻松转换。

**注意**  
适用于 [Java](https://aws.amazon.com/sdk-for-java)、[.NET](https://aws.amazon.com/sdk-for-net)、[Node.js](https://aws.amazon.com/sdk-for-node-js) 的 AWS SDK 和 [JavaScript SDK](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/) 中可用的文档接口。

以下 Java 程序使用 适用于 Java 的 AWS SDK 的文档接口。程序创建一个 `Table` 对象，表示 `Music` 表，然后要求该对象使用 `GetItem` 检索歌曲。然后程序打印该歌曲的发行年份。

`software.amazon.dynamodb.document.DynamoDB` 类实施 DynamoDB 文档接口。注意`DynamoDB`充当一个围绕低级别客户端的包装程序 (`AmazonDynamoDB`)。

### 文档接口示例
<a name="document-level-example"></a>

```
package com.amazonaws.codesamples.gsg;

import software.amazon.dynamodb.AmazonDynamoDB;
import software.amazon.dynamodb.AmazonDynamoDBClientBuilder;
import software.amazon.dynamodb.document.DynamoDB;
import software.amazon.dynamodb.document.GetItemOutcome;
import software.amazon.dynamodb.document.Table;

public class MusicDocumentDemo {

    public static void main(String[] args) {

        AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();
        DynamoDB docClient = new DynamoDB(client);

        Table table = docClient.getTable("Music");
        GetItemOutcome outcome = table.getItemOutcome(
                "Artist", "No One You Know",
                "SongTitle", "Call Me Today");

        int year = outcome.getItem().getInt("Year");
        System.out.println("The song was released in " + year);

    }
}
```

## 与 DynamoDB 配合使用的对象持久化接口
<a name="Programming.SDKs.Interfaces.Mapper"></a>

如果不直接执行数据层面操作，一些 AWS SDK 提供对象持久化接口。相反，您可以创建表示 Amazon DynamoDB 表和索引中项目的对象，并且仅与这些对象进行交互。这允许您编写以对象为中心的代码，而不是以数据库为中心的代码。

**注意**  
适用于 Java 和 .NET 的 AWS SDK 提供对象持久化接口。有关更多信息，请参阅 DynamoDB 的[用于 DynamoDB 的更高级别编程接口](HigherLevelInterfaces.md)。

### 对象持久化接口示例
<a name="mapper-level-example"></a>

```
import com.example.dynamodb.Customer;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
import software.amazon.awssdk.enhanced.dynamodb.Key;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.model.GetItemEnhancedRequest;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;
```

```
import com.example.dynamodb.Customer;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient;
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbTable;
import software.amazon.awssdk.enhanced.dynamodb.Key;
import software.amazon.awssdk.enhanced.dynamodb.TableSchema;
import software.amazon.awssdk.enhanced.dynamodb.model.GetItemEnhancedRequest;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbException;

/*
 * Before running this code example, create an Amazon DynamoDB table named Customer with these columns:
 *   - id - the id of the record that is the key. Be sure one of the id values is `id101`
 *   - custName - the customer name
 *   - email - the email value
 *   - registrationDate - an instant value when the item was added to the table. These values
 *                        need to be in the form of `YYYY-MM-DDTHH:mm:ssZ`, such as 2022-07-11T00:00:00Z
 *
 * Also, ensure that you have set up your development environment, including your credentials.
 *
 * For information, see this documentation topic:
 *
 * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
 */

public class EnhancedGetItem {
    public static void main(String[] args) {
        Region region = Region.US_EAST_1;
        DynamoDbClient ddb = DynamoDbClient.builder()
                .region(region)
                .build();

        DynamoDbEnhancedClient enhancedClient = DynamoDbEnhancedClient.builder()
                .dynamoDbClient(ddb)
                .build();

        getItem(enhancedClient);
        ddb.close();
    }

    public static String getItem(DynamoDbEnhancedClient enhancedClient) {
        Customer result = null;
        try {
            DynamoDbTable<Customer> table = enhancedClient.table("Customer", TableSchema.fromBean(Customer.class));
            Key key = Key.builder()
                    .partitionValue("id101").sortValue("tred@noserver.com")
                    .build();

            // Get the item by using the key.
            result = table.getItem(
                    (GetItemEnhancedRequest.Builder requestBuilder) -> requestBuilder.key(key));
            System.out.println("******* The description value is " + result.getCustName());

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

# 用于 DynamoDB 的更高级别编程接口
<a name="HigherLevelInterfaces"></a>

AWS SDK 为应用程序提供了用于使用 Amazon DynamoDB 的低级接口。这些客户端类和方法直接对应于低级 DynamoDB API。然而，需要将复杂的数据类型映射到数据库表中的项目时，许多开发人员感觉脱离或*阻力不匹配*。使用低级数据库接口，开发人员必须编写向数据库表读取或写入对象数据的方法。对象类型和数据库表的每个组合所需的额外代码量非常庞大。

为了简化开发，适用于 Java 和 .NET 的 AWS SDK 提供更高级别抽象。DynamoDB 的更高级别接口允许您定义程序中的对象与存储这些对象数据的数据库表之间的关系。定义此映射后，可以调用简单的对象方法，例如 `save`、`load` 或 `delete`，并且底层的低级 DynamoDB 操作会代表您自动调用。这允许您编写以对象为中心的代码，而不是以数据库为中心的代码。

用于 DynamoDB 的更高级别的编程接口在适用于 Java 和 .NET 的 AWS SDK 中提供。

**Java**
+ [Java 1.x：DynamoDBMapper](DynamoDBMapper.md)
+ [Java 2.x：DynamoDB 增强型客户端](DynamoDBEnhanced.md)

**.NET**
+ [在 DynamoDB 中使用 .NET 文档模型](DotNetSDKMidLevel.md)
+ [结合使用 .NET 对象持久化模型和 DynamoDB](DotNetSDKHighLevel.md)

# Java 1.x：DynamoDBMapper
<a name="DynamoDBMapper"></a>

**注意**  
SDK for Java 有两个版本：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 版本，并且将于 2025 年 12 月 31 日终止支持该版本。为进行新开发，强烈建议您使用 2.x。

适用于 Java 的 AWS SDK 提供了 `DynamoDBMapper` 类，使您能够将客户端类映射到 Amazon DynamoDB 表。要使用 `DynamoDBMapper`，您应在代码中定义 DynamoDB 表中项目与其相应对象实例之间的关系。`DynamoDBMapper` 类让您能够对项目执行各种创建、读取、更新和删除 (CRUD) 操作，并对表运行查询和扫描。

**Topics**
+ [DynamoDBMapper 类](DynamoDBMapper.Methods.md)
+ [DynamoDBMapper for Java 支持的数据类型](DynamoDBMapper.DataTypes.md)
+ [适用于 DynamoDB 的 Java 注释](DynamoDBMapper.Annotations.md)
+ [DynamoDBMapper 的可选配置设置](DynamoDBMapper.OptionalConfig.md)
+ [DynamoDB 和乐观锁（使用版本号）](DynamoDBMapper.OptimisticLocking.md)
+ [在 DynamoDB 中映射任意数据](DynamoDBMapper.ArbitraryDataMapping.md)
+ [DynamoDBMapper 示例](DynamoDBMapper.Examples.md)

**注意**  
`DynamoDBMapper` 类不允许创建、更新或删除表。要执行这些任务，请改用低级别 SDK for Java 接口。

SDK for Java 提供了一组注释类型，可用于将类映射到表。例如，我们来看一个使用 `ProductCatalog` 作为分区键的 `Id` 表。

```
ProductCatalog(Id, ...)
```

您可以将客户端应用程序中的类映射到 `ProductCatalog` 表（如下面的 Java 代码所示）。该代码定义了一个名为 `CatalogItem` 的普通旧 Java 对象 (POJO)，此对象使用注释将对象字段映射到 DynamoDB 属性名称。

**Example**  

```
package com.amazonaws.codesamples;

import java.util.Set;

import software.amazon.dynamodb.datamodeling.DynamoDBAttribute;
import software.amazon.dynamodb.datamodeling.DynamoDBHashKey;
import software.amazon.dynamodb.datamodeling.DynamoDBIgnore;
import software.amazon.dynamodb.datamodeling.DynamoDBTable;

@DynamoDBTable(tableName="ProductCatalog")
public class CatalogItem {

    private Integer id;
    private String title;
    private String ISBN;
    private Set<String> bookAuthors;
    private String someProp;

    @DynamoDBHashKey(attributeName="Id")
    public Integer getId() { return id; }
    public void setId(Integer id) {this.id = id; }

    @DynamoDBAttribute(attributeName="Title")
    public String getTitle() {return title; }
    public void setTitle(String title) { this.title = title; }

    @DynamoDBAttribute(attributeName="ISBN")
    public String getISBN() { return ISBN; }
    public void setISBN(String ISBN) { this.ISBN = ISBN; }

    @DynamoDBAttribute(attributeName="Authors")
    public Set<String> getBookAuthors() { return bookAuthors; }
    public void setBookAuthors(Set<String> bookAuthors) { this.bookAuthors = bookAuthors; }

    @DynamoDBIgnore
    public String getSomeProp() { return someProp; }
    public void setSomeProp(String someProp) { this.someProp = someProp; }
}
```

在上述代码中，`@DynamoDBTable` 注释将 `CatalogItem` 类映射到 `ProductCatalog` 表。您可以将各个类实例存储为表中的项目。在类定义中，`@DynamoDBHashKey` 注释将 `Id` 属性映射到主键。

默认情况下，类属性会映射到表中的同名属性。`Title` 和 `ISBN` 属性会映射到表中的同名属性。

当 DynamoDB 属性的名称与类中声明的属性的名称匹配时，`@DynamoDBAttribute` 注释是可选的。当这两个名称不同时，请将此注释与 `attributeName` 参数一起使用以指定此属性对应的 DynamoDB 属性。

在上述示例中，`@DynamoDBAttribute` 注释将添加到每个属性中以确保属性名称与上一步骤中创建的表完全匹配，并且与本指南中其他代码示例中使用的属性名称保持一致。

您的类定义的某些属性可以不用映射到表中的任何属性。您可以通过添加 `@DynamoDBIgnore` 注释来识别这些属性。在上述示例中，`SomeProp` 属性是使用 `@DynamoDBIgnore` 注释标记的。在将 `CatalogItem` 实例上传到该表时，您的 `DynamoDBMapper` 实例不包含 `SomeProp` 属性。另外，映射器也不会在您检索表中的项目时返回此属性。

在定义了映射类之后，可以使用 `DynamoDBMapper` 方法将该类的实例写入 `Catalog` 表的对应项目。以下代码示例展示了这一技术。

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();

DynamoDBMapper mapper = new DynamoDBMapper(client);

CatalogItem item = new CatalogItem();
item.setId(102);
item.setTitle("Book 102 Title");
item.setISBN("222-2222222222");
item.setBookAuthors(new HashSet<String>(Arrays.asList("Author 1", "Author 2")));
item.setSomeProp("Test");

mapper.save(item);
```

以下代码示例说明如何检索该项目并访问它的某些属性。

```
CatalogItem partitionKey = new CatalogItem();

partitionKey.setId(102);
DynamoDBQueryExpression<CatalogItem> queryExpression = new DynamoDBQueryExpression<CatalogItem>()
    .withHashKeyValues(partitionKey);

List<CatalogItem> itemList = mapper.query(CatalogItem.class, queryExpression);

for (int i = 0; i < itemList.size(); i++) {
    System.out.println(itemList.get(i).getTitle());
    System.out.println(itemList.get(i).getBookAuthors());
}
```

`DynamoDBMapper` 提供了在 Java 内使用 DynamoDB 数据的一种直观而自然的方式。它还提供了一些内置功能，如乐观锁、ACID 事务、自动生成的分区键和排序键值以及对象版本控制。

# DynamoDBMapper 类
<a name="DynamoDBMapper.Methods"></a>



`DynamoDBMapper` 类是 Amazon DynamoDB 的入口点。它提供对 DynamoDB 端点的访问，让您能够访问各个表中的数据。它还让您能够对项目执行各种创建、读取、更新和删除 (CRUD) 操作，并对表执行查询和扫描。此类提供了以下方法供您在 DynamoDB 中使用。

有关相应的 Javadoc 文档，请参阅*适用于 Java 的 AWS SDKAPI 参考*的 [DynamoDBMapper](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/datamodeling/DynamoDBMapper.html)。

**Topics**
+ [保存](#DynamoDBMapper.Methods.save)
+ [负载](#DynamoDBMapper.Methods.load)
+ [删除](#DynamoDBMapper.Methods.delete)
+ [查询](#DynamoDBMapper.Methods.query)
+ [queryPage](#DynamoDBMapper.Methods.queryPage)
+ [扫描](#DynamoDBMapper.Methods.scan)
+ [scanPage](#DynamoDBMapper.Methods.scanPage)
+ [parallelScan](#DynamoDBMapper.Methods.parallelScan)
+ [batchSave](#DynamoDBMapper.Methods.batchSave)
+ [batchLoad](#DynamoDBMapper.Methods.batchLoad)
+ [batchDelete](#DynamoDBMapper.Methods.batchDelete)
+ [batchWrite](#DynamoDBMapper.Methods.batchWrite)
+ [transactionWrite](#DynamoDBMapper.Methods.transactionWrite)
+ [transactionLoad](#DynamoDBMapper.Methods.transactionLoad)
+ [count](#DynamoDBMapper.Methods.count)
+ [generateCreateTableRequest](#DynamoDBMapper.Methods.generateCreateTableRequest)
+ [createS3Link](#DynamoDBMapper.Methods.createS3Link)
+ [getS3ClientCache](#DynamoDBMapper.Methods.getS3ClientCache)

## 保存
<a name="DynamoDBMapper.Methods.save"></a>

将指定对象保存到表中。您要保存的对象是此方法唯一的必需参数。您可以使用 `DynamoDBMapperConfig` 对象提供可选配置参数。

如果具有相同主键的项目不存在，此方法就会在表中创建一个新项目。如果具有相同主键的项目存在，此方法就会更新现有项目。如果分区键和排序键的类型是 String、使用了 `@DynamoDBAutoGeneratedKey` 注释并且未初始化，那么系统会为其指定一个随机的全局唯一标识符 (UUID)。使用 `@DynamoDBVersionAttribute` 注释的版本字段会增加 1。此外，如果系统更新了版本字段或生成一个键，则该操作会使得系统更新传入的对象。

默认情况下，系统只会更新已映射的类属性对应的属性。项目中任何其他现有属性不会受到影响。但是，如果指定 `SaveBehavior.CLOBBER`，您就可以强制相应的项目实现完全覆盖。

```
DynamoDBMapperConfig config = DynamoDBMapperConfig.builder()
    .withSaveBehavior(DynamoDBMapperConfig.SaveBehavior.CLOBBER).build();
        
mapper.save(item, config);
```

如果您启用了版本控制，客户端和服务器端的项目版本就必须匹配。但是，如果使用的了 `SaveBehavior.CLOBBER` 选项，版本就无需相匹配。有关版本控制的更多信息，请参阅[DynamoDB 和乐观锁（使用版本号）](DynamoDBMapper.OptimisticLocking.md)。

## 负载
<a name="DynamoDBMapper.Methods.load"></a>

检索表中的项目。您必须提供要检索的项目的主键。您可以使用 `DynamoDBMapperConfig` 对象提供可选配置参数。例如，您可以选择请求强一致性读取，以确保此方法只检索最新的项目值（如以下 Java 语句所示）。

```
DynamoDBMapperConfig config = DynamoDBMapperConfig.builder()
    .withConsistentReads(DynamoDBMapperConfig.ConsistentReads.CONSISTENT).build();

CatalogItem item = mapper.load(CatalogItem.class, item.getId(), config);
```

默认情况下，DynamoDB 所返回的项目的值采用最终一致性。有关 DynamoDB 的最终一致性模式的信息，请参阅[DynamoDB 读取一致性](HowItWorks.ReadConsistency.md)。

## 删除
<a name="DynamoDBMapper.Methods.delete"></a>

删除表中的项目。您必须传入已映射类的对象实例。

如果您启用了版本控制，客户端和服务器端的项目版本就必须匹配。但是，如果使用的了 `SaveBehavior.CLOBBER` 选项，版本就无需相匹配。有关版本控制的更多信息，请参阅[DynamoDB 和乐观锁（使用版本号）](DynamoDBMapper.OptimisticLocking.md)。

## 查询
<a name="DynamoDBMapper.Methods.query"></a>

查询表或二级索引。

假定您的 `Reply` 表存储了论坛话题回复，每个话题主题可以有 0 条或更多条回复。`Reply` 表的主键由 `Id` 和 `ReplyDateTime` 字段构成，其中 `Id` 是分区键，`ReplyDateTime` 是主键的排序键。

```
Reply ( Id, ReplyDateTime, ... )
```

假设您在 `Reply` 类和 DynamoDB 中对应的 `Reply` 表之间创建了一个映射。以下 Java 代码使用 `DynamoDBMapper` 查找特定话题主题在过去两周内的所有回复。

**Example**  

```
String forumName = "&DDB;";
String forumSubject = "&DDB; Thread 1";
String partitionKey = forumName + "#" + forumSubject;

long twoWeeksAgoMilli = (new Date()).getTime() - (14L*24L*60L*60L*1000L);
Date twoWeeksAgo = new Date();
twoWeeksAgo.setTime(twoWeeksAgoMilli);
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
String twoWeeksAgoStr = df.format(twoWeeksAgo);

Map<String, AttributeValue> eav = new HashMap<String, AttributeValue>();
eav.put(":v1", new AttributeValue().withS(partitionKey));
eav.put(":v2",new AttributeValue().withS(twoWeeksAgoStr.toString()));

DynamoDBQueryExpression<Reply> queryExpression = new DynamoDBQueryExpression<Reply>()
    .withKeyConditionExpression("Id = :v1 and ReplyDateTime > :v2")
    .withExpressionAttributeValues(eav);

List<Reply> latestReplies = mapper.query(Reply.class, queryExpression);
```

该查询会一系列 `Reply` 对象。

在默认情况下，`query` 方法返回“延迟加载”集合。它最初只返回一页结果，然后在需要时发出服务调用请求下一页结果。要获取所有匹配项，请对 `latestReplies` 集合进行迭代。

请注意，对集合调用 `size()` 方法将会加载每个结果，以提供准确的计数。这可能会导致消耗大量预置的吞吐量，在数据量非常庞大的表上甚至可能会耗尽 JVM 中的所有内存。

要查询索引，您必须先将索引建模为映射器类。假设 `Reply` 表具有一个名为 *PostedBy-Message-Index* 的全局二级索引。此索引的分区键为 `PostedBy`，排序键为 `Message`。该索引中某个项目的类定义将如下所示。

```
@DynamoDBTable(tableName="Reply")
public class PostedByMessage {
    private String postedBy;
    private String message;

    @DynamoDBIndexHashKey(globalSecondaryIndexName = "PostedBy-Message-Index", attributeName = "PostedBy")
    public String getPostedBy() { return postedBy; }
    public void setPostedBy(String postedBy) { this.postedBy = postedBy; }

    @DynamoDBIndexRangeKey(globalSecondaryIndexName = "PostedBy-Message-Index", attributeName = "Message")
    public String getMessage() { return message; }
    public void setMessage(String message) { this.message = message; }

   // Additional properties go here.
}
```

`@DynamoDBTable` 注释表示此索引与 `Reply` 表相关联。`@DynamoDBIndexHashKey` 注释表示此索引的分区键 (*PostedBy*)，`@DynamoDBIndexRangeKey` 表示此索引的排序键 (*Message*)。

现在，您可以使用 `DynamoDBMapper` 来查询此索引，以检索某位用户发布的消息的子集。如果您在表和索引之间没有冲突的映射并且已在映射器中建立映射，则无需指定索引名称。映射器将根据主键和排序键进行推断。以下代码示例查询全局二级索引。由于全局二级索引支持最终一致性读取，但不支持强一致性读取，因此您必须指定 `withConsistentRead(false)`。

```
HashMap<String, AttributeValue> eav = new HashMap<String, AttributeValue>();
eav.put(":v1",  new AttributeValue().withS("User A"));
eav.put(":v2",  new AttributeValue().withS("DynamoDB"));

DynamoDBQueryExpression<PostedByMessage> queryExpression = new DynamoDBQueryExpression<PostedByMessage>()
    .withIndexName("PostedBy-Message-Index")
    .withConsistentRead(false)
    .withKeyConditionExpression("PostedBy = :v1 and begins_with(Message, :v2)")
    .withExpressionAttributeValues(eav);

List<PostedByMessage> iList =  mapper.query(PostedByMessage.class, queryExpression);
```

该查询会一系列 `PostedByMessage` 对象。

## queryPage
<a name="DynamoDBMapper.Methods.queryPage"></a>

查询表或二级索引并返回单页匹配结果。与 `query` 方法一样，您必须指定分区键值以及应用于排序键属性的查询筛选条件。但是，`queryPage` 仅返回第一“页”数据，即适合 1 MB 的数据量 

## 扫描
<a name="DynamoDBMapper.Methods.scan"></a>

扫描整个表或二级索引。您可以选择指定 `FilterExpression` 以筛选结果集。

假定您的 `Reply` 表存储了论坛话题回复，每个话题主题可以有 0 条或更多条回复。`Reply` 表的主键由 `Id` 和 `ReplyDateTime` 字段构成，其中 `Id` 是分区键，`ReplyDateTime` 是主键的排序键。

```
Reply ( Id, ReplyDateTime, ... )
```

如果您已经将 Java 类映射到 `Reply` 表，则可以使用 `DynamoDBMapper` 来扫描表。例如，以下 Java 代码扫描整个 `Reply` 表，仅返回了某一年内的回复。

**Example**  

```
HashMap<String, AttributeValue> eav = new HashMap<String, AttributeValue>();
eav.put(":v1", new AttributeValue().withS("2015"));

DynamoDBScanExpression scanExpression = new DynamoDBScanExpression()
    .withFilterExpression("begins_with(ReplyDateTime,:v1)")
    .withExpressionAttributeValues(eav);

List<Reply> replies =  mapper.scan(Reply.class, scanExpression);
```

在默认情况下，`scan` 方法返回“延迟加载”集合。它最初只返回一页结果，然后在需要时发出服务调用请求下一页结果。要获取所有匹配项，请对 `replies` 集合进行迭代。

请注意，对集合调用 `size()` 方法将会加载每个结果，以提供准确的计数。这可能会导致消耗大量预置的吞吐量，在数据量非常庞大的表上甚至可能会耗尽 JVM 中的所有内存。

要扫描索引，您必须先将索引建模为映射器类。假设 `Reply` 表具有一个名为 `PostedBy-Message-Index` 的全局二级索引。此索引的分区键为 `PostedBy`，排序键为 `Message`。此索引的映射器类显示在 [查询](#DynamoDBMapper.Methods.query) 部分。它使用 `@DynamoDBIndexHashKey` 和 `@DynamoDBIndexRangeKey` 注释来指定此索引的分区键和排序键。

下面的代码示例扫描 `PostedBy-Message-Index`。该代码段并未使用扫描筛选条件，因此索引中的所有项目都会返回给您。

```
DynamoDBScanExpression scanExpression = new DynamoDBScanExpression()
    .withIndexName("PostedBy-Message-Index")
    .withConsistentRead(false);

    List<PostedByMessage> iList =  mapper.scan(PostedByMessage.class, scanExpression);
    Iterator<PostedByMessage> indexItems = iList.iterator();
```

## scanPage
<a name="DynamoDBMapper.Methods.scanPage"></a>

扫描表或二级索引并返回单页匹配结果。与 `scan` 方法一样，您可以选择指定 `FilterExpression` 来筛选结果集。但是，`scanPage` 仅返回第一“页”数据，即适合 1 MB 的数据量。

## parallelScan
<a name="DynamoDBMapper.Methods.parallelScan"></a>

对整个表或二级索引执行并行扫描。您可以指定表的几个逻辑分段，并指定用于筛选结果的扫描表达式。`parallelScan` 将扫描任务分解为多个工作线程，每个逻辑分段各有一个工作线程；工作线程并行处理数据并返回结果。

以下 Java 代码示例对 `Product` 表执行并行扫描。

```
int numberOfThreads = 4;

Map<String, AttributeValue> eav = new HashMap<String, AttributeValue>();
eav.put(":n", new AttributeValue().withN("100"));

DynamoDBScanExpression scanExpression = new DynamoDBScanExpression()
    .withFilterExpression("Price <= :n")
    .withExpressionAttributeValues(eav);

List<Product> scanResult = mapper.parallelScan(Product.class, scanExpression, numberOfThreads);
```

## batchSave
<a name="DynamoDBMapper.Methods.batchSave"></a>

调用一次或多次 `AmazonDynamoDB.batchWriteItem` 方法，以在一个或多个表中保存对象。此方法不提供事务担保。

以下 Java 代码将两个项目（图书）保存到 `ProductCatalog` 表中。

```
Book book1 = new Book();
book1.setId(901);
book1.setProductCategory("Book");
book1.setTitle("Book 901 Title");

Book book2 = new Book();
book2.setId(902);
book2.setProductCategory("Book");
book2.setTitle("Book 902 Title");

mapper.batchSave(Arrays.asList(book1, book2));
```

## batchLoad
<a name="DynamoDBMapper.Methods.batchLoad"></a>

使用主键检索一个或多个表中的多个项目。

以下 Java 代码检索两个不同表中的两个项目。

```
ArrayList<Object> itemsToGet = new ArrayList<Object>();

ForumItem forumItem = new ForumItem();
forumItem.setForumName("Amazon DynamoDB");
itemsToGet.add(forumItem);

ThreadItem threadItem = new ThreadItem();
threadItem.setForumName("Amazon DynamoDB");
threadItem.setSubject("Amazon DynamoDB thread 1 message text");
itemsToGet.add(threadItem);

Map<String, List<Object>> items = mapper.batchLoad(itemsToGet);
```

## batchDelete
<a name="DynamoDBMapper.Methods.batchDelete"></a>

调用一次或多次 `AmazonDynamoDB.batchWriteItem` 方法，以从一个或多个表中删除项目。此方法不提供事务担保。

以下 Java 代码从 `ProductCatalog` 表中删除两个项目（图书）。

```
Book book1 = mapper.load(Book.class, 901);
Book book2 = mapper.load(Book.class, 902);
mapper.batchDelete(Arrays.asList(book1, book2));
```

## batchWrite
<a name="DynamoDBMapper.Methods.batchWrite"></a>

调用一次或多次 `AmazonDynamoDB.batchWriteItem` 方法，以在一个或多个表中和删除保存对象。此方法不提供事务担保，也不支持版本控制 (有条件放置或删除)。

以下 Java 代码向 `Forum` 表写入一个新项目、向 `Thread` 表写入一个新项目，然后删除 `ProductCatalog` 表中的一个项目。

```
// Create a Forum item to save
Forum forumItem = new Forum();
forumItem.setName("Test BatchWrite Forum");

// Create a Thread item to save
Thread threadItem = new Thread();
threadItem.setForumName("AmazonDynamoDB");
threadItem.setSubject("My sample question");

// Load a ProductCatalog item to delete
Book book3 = mapper.load(Book.class, 903);

List<Object> objectsToWrite = Arrays.asList(forumItem, threadItem);
List<Book> objectsToDelete = Arrays.asList(book3);

mapper.batchWrite(objectsToWrite, objectsToDelete);
```

## transactionWrite
<a name="DynamoDBMapper.Methods.transactionWrite"></a>

调用一次 `AmazonDynamoDB.transactWriteItems` 方法以在一个或多个表中保存和删除对象。

有关事务特定的异常列表，请参阅 [TransactWriteItems 错误](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactWriteItems.html#API_TransactWriteItems_Errors)。

有关 DynamoDB 事务和提供的原子性、一致性、隔离性和持久性 (ACID) 保证的更多信息，请参阅 [Amazon DynamoDB Transactions](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transactions.html)。

**注意**  
 该方法不支持以下功能：  
[DynamoDBMapperConfig.SaveBehavior](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/DynamoDBMapper.OptionalConfig.html)。

以下 Java 代码以事务方式将新项目分别写入到 `Forum` 和 `Thread` 表中。

```
Thread s3ForumThread = new Thread();
s3ForumThread.setForumName("S3 Forum");
s3ForumThread.setSubject("Sample Subject 1");
s3ForumThread.setMessage("Sample Question 1");

Forum s3Forum = new Forum();
s3Forum.setName("S3 Forum");
s3Forum.setCategory("Amazon Web Services");
s3Forum.setThreads(1);

TransactionWriteRequest transactionWriteRequest = new TransactionWriteRequest();
transactionWriteRequest.addPut(s3Forum);
transactionWriteRequest.addPut(s3ForumThread);
mapper.transactionWrite(transactionWriteRequest);
```

## transactionLoad
<a name="DynamoDBMapper.Methods.transactionLoad"></a>

调用一次 `AmazonDynamoDB.transactGetItems` 方法以从一个或多个表中加载对象。

有关事务特定的异常列表，请参阅 [TransactGetItems 错误](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_TransactGetItems.html#API_TransactGetItems_Errors)。

有关 DynamoDB 事务和提供的原子性、一致性、隔离性和持久性 (ACID) 保证的更多信息，请参阅 [Amazon DynamoDB Transactions](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transactions.html)。

以下 Java 代码以事务方式分别从 `Forum` 和 `Thread` 表中加载一个项目。

```
Forum dynamodbForum = new Forum();
dynamodbForum.setName("DynamoDB Forum");
Thread dynamodbForumThread = new Thread();
dynamodbForumThread.setForumName("DynamoDB Forum");

TransactionLoadRequest transactionLoadRequest = new TransactionLoadRequest();
transactionLoadRequest.addLoad(dynamodbForum);
transactionLoadRequest.addLoad(dynamodbForumThread);
mapper.transactionLoad(transactionLoadRequest);
```

## count
<a name="DynamoDBMapper.Methods.count"></a>

评估指定的扫描表达式并返回匹配项目的数量。不返回任何项目数据。

## generateCreateTableRequest
<a name="DynamoDBMapper.Methods.generateCreateTableRequest"></a>

分析代表 DynamoDB 表的 POJO 类，并返回针对该表的 `CreateTableRequest`。

## createS3Link
<a name="DynamoDBMapper.Methods.createS3Link"></a>

创建 Amazon S3 中对象的链接。必须指定存储桶名称和用于唯一标识存储桶中的对象的键名称。

要使用 `createS3Link`，您的映射器类必须定义 getter 和 setter 方法。以下代码示例通过将新属性和 getter/setter 方法添加到 `CatalogItem` 类对此加以说明。

```
@DynamoDBTable(tableName="ProductCatalog")
public class CatalogItem {

    ...

    public S3Link productImage;

    ....

    @DynamoDBAttribute(attributeName = "ProductImage")
    public S3Link getProductImage() {
            return productImage;
    }

    public void setProductImage(S3Link productImage) {
        this.productImage = productImage;
    }

...
}
```

以下 Java 代码定义了一个要写入 `Product` 表的新项目。该项目包含某个产品图像的链接；图像数据会上传至 Amazon S3。

```
CatalogItem item = new CatalogItem();

item.setId(150);
item.setTitle("Book 150 Title");

String amzn-s3-demo-bucket = "amzn-s3-demo-bucket";
String myS3Key = "productImages/book_150_cover.jpg";
item.setProductImage(mapper.createS3Link(amzn-s3-demo-bucket, myS3Key));

item.getProductImage().uploadFrom(new File("/file/path/book_150_cover.jpg"));

mapper.save(item);
```

`S3Link` 类提供了许多用于操作 Amazon S3 中的对象的其他方法。有关更多信息，请参阅[适用于 `S3Link` 的 Javadocs](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/datamodeling/S3Link.html)。

## getS3ClientCache
<a name="DynamoDBMapper.Methods.getS3ClientCache"></a>

返回用于访问 Amazon S3 的基础 `S3ClientCache`。一个 `S3ClientCache` 就是一个用于 `AmazonS3Client` 对象的智能映射。如果您有多个客户端，则 `S3ClientCache` 可帮助您按 AWS 区域来组织客户端，并可以按需创建新的 Amazon S3 客户端。

# DynamoDBMapper for Java 支持的数据类型
<a name="DynamoDBMapper.DataTypes"></a>

本部分介绍 Amazon DynamoDB 中支持的 Java 基元数据类型、集合和任意数据类型。

Amazon DynamoDB 支持以下基元 Java 数据类型和基元封装类。
+ `String`
+ `Boolean`, `boolean`
+ `Byte`, `byte`
+ `Date`（为 [ISO\$18601](http://en.wikipedia.org/wiki/ISO_8601) 毫秒精度字符串，转换为 UTC）
+ `Calendar`（为 [ISO\$18601](http://en.wikipedia.org/wiki/ISO_8601) 毫秒精度字符串，转换为 UTC）
+ `Long`, `long`
+ `Integer`, `int`
+ `Double`, `double`
+ `Float`, `float`
+ `BigDecimal`
+ `BigInteger`

**注意**  
有关 DynamoDB 命名规则和支持的各种数据类型的更多信息，请参阅[Amazon DynamoDB 中支持的数据类型和命名规则](HowItWorks.NamingRulesDataTypes.md)。
DynamoDBMapper 支持空二进制值。
空字符串值受 AWS SDK for Java 2.x 支持。  
在 AWS SDK for Java 1.x 中，DynamoDBMapper 支持读取空字符串属性值；但是，它不会写入空字符串属性值，因为这些属性会从请求中删除。

DynamoDB 支持 Java [Set](http://docs.oracle.com/javase/6/docs/api/java/util/Set.html)、[List](http://docs.oracle.com/javase/6/docs/api/java/util/List.html) 和 [Map](http://docs.oracle.com/javase/6/docs/api/java/util/Map.html) 集合类型。下表汇总了上述 Java 类型到 DynamoDB 类型的映射。


****  

| Java 类型 | DynamoDB 类型 | 
| --- | --- | 
|  所有数字类型  |  `N`（数字类型）  | 
|  字符串  |  `S`（字符串类型）   | 
|  布尔值  |  `BOOL`（布尔值类型），0 或 1。  | 
|  字节缓冲区  |  `B`（二进制类型）  | 
|  日期  |  `S`（字符串类型）。日期值存储为符合 ISO-8601 格式的字符串。  | 
| [Set](http://docs.oracle.com/javase/6/docs/api/java/util/Set.html) 集合类型 |  `SS` (字符串集) 类型、`NS` (数字集) 类型或 `BS` (二进制集) 类型。  | 

 `DynamoDBTypeConverter` 接口可让您将自己的任意数据类型映射到受 DynamoDB 原生支持的数据类型。有关更多信息，请参阅 [在 DynamoDB 中映射任意数据](DynamoDBMapper.ArbitraryDataMapping.md)。

# 适用于 DynamoDB 的 Java 注释
<a name="DynamoDBMapper.Annotations"></a>

此部分介绍可用于将类和属性映射到 Amazon DynamoDB 中表和属性的注释。

有关相应的 Javadoc 文档，请参阅[适用于 Java 的 AWS SDKAPI 参考](https://docs.aws.amazon.com/sdk-for-java/latest/reference/)的[注释类型汇总](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/datamodeling/package-summary.html)。

**注意**  
在以下注释中，只有 `DynamoDBTable` 和 `DynamoDBHashKey` 是必需的。

**Topics**
+ [DynamoDBAttribute](#DynamoDBMapper.Annotations.DynamoDBAttribute)
+ [DynamoDBAutoGeneratedKey](#DynamoDBMapper.Annotations.DynamoDBAutoGeneratedKey)
+ [DynamoDBAutoGeneratedTimestamp](#DynamoDBMapper.Annotations.DynamoDBAutoGeneratedTimestamp)
+ [DynamoDBDocument](#DynamoDBMapper.Annotations.DynamoDBDocument)
+ [DynamoDBHashKey](#DynamoDBMapper.Annotations.DynamoDBHashKey)
+ [DynamoDBIgnore](#DynamoDBMapper.Annotations.DynamoDBIgnore)
+ [DynamoDBIndexHashKey](#DynamoDBMapper.Annotations.DynamoDBIndexHashKey)
+ [DynamoDBIndexRangeKey](#DynamoDBMapper.Annotations.DynamoDBIndexRangeKey)
+ [DynamoDBRangeKey](#DynamoDBMapper.Annotations.DynamoDBRangeKey)
+ [DynamoDBTable](#DynamoDBMapper.Annotations.DynamoDBTable)
+ [DynamoDBTypeConverted](#DynamoDBMapper.Annotations.DynamoDBTypeConverted)
+ [DynamoDBTyped](#DynamoDBMapper.Annotations.DynamoDBTyped)
+ [DynamoDBVersionAttribute](#DynamoDBMapper.Annotations.DynamoDBVersionAttribute)

## DynamoDBAttribute
<a name="DynamoDBMapper.Annotations.DynamoDBAttribute"></a>

将属性映射到表属性。默认情况下，每个类属性都会映射到具有同名的项目属性。但是，如果名称不同，您可以使用此注释将某一属性映射到表属性。在以下 Java 代码段中，`DynamoDBAttribute` 将 `BookAuthors` 属性映射到表中的 `Authors` 属性名。

```
@DynamoDBAttribute(attributeName = "Authors")
public List<String> getBookAuthors() { return BookAuthors; }
public void setBookAuthors(List<String> BookAuthors) { this.BookAuthors = BookAuthors; }
```

`DynamoDBMapper` 在向表中保存对象时将 `Authors` 用作属性名称。

## DynamoDBAutoGeneratedKey
<a name="DynamoDBMapper.Annotations.DynamoDBAutoGeneratedKey"></a>

将分区键或排序键属性标记为自动生成。保存这些属性时，`DynamoDBMapper` 将生成随机 [UUID](http://docs.oracle.com/javase/6/docs/api/java/util/UUID.html)。只有字符串属性可被标记为自动生成键。

以下示例演示如何使用自动生成键。

```
@DynamoDBTable(tableName="AutoGeneratedKeysExample")
public class AutoGeneratedKeys {
    private String id;
    private String payload;

    @DynamoDBHashKey(attributeName = "Id")
    @DynamoDBAutoGeneratedKey
    public String getId() { return id; }
    public void setId(String id) { this.id = id; }

    @DynamoDBAttribute(attributeName="payload")
    public String getPayload() { return this.payload; }
    public void setPayload(String payload) { this.payload = payload; }

    public static void saveItem() {
        AutoGeneratedKeys obj = new AutoGeneratedKeys();
        obj.setPayload("abc123");

        // id field is null at this point
        DynamoDBMapper mapper = new DynamoDBMapper(dynamoDBClient);
        mapper.save(obj);

        System.out.println("Object was saved with id " + obj.getId());
    }
}
```

## DynamoDBAutoGeneratedTimestamp
<a name="DynamoDBMapper.Annotations.DynamoDBAutoGeneratedTimestamp"></a>

自动生成时间戳。

```
@DynamoDBAutoGeneratedTimestamp(strategy=DynamoDBAutoGenerateStrategy.ALWAYS)
public Date getLastUpdatedDate() { return lastUpdatedDate; }
public void setLastUpdatedDate(Date lastUpdatedDate) { this.lastUpdatedDate = lastUpdatedDate; }
```

或者，可以通过提供策略属性来定义自动生成策略。默认值为 `ALWAYS`。

## DynamoDBDocument
<a name="DynamoDBMapper.Annotations.DynamoDBDocument"></a>

表示类可以序列化为 Amazon DynamoDB 文档。

例如，假设您要将一个 JSON 文档映射到类型为 Map (`M`) 的 DynamoDB 属性。使用以下代码示例来定义包含类型为 Map 的嵌套属性 (Pictures) 的项目。

```
public class ProductCatalogItem {

    private Integer id;  //partition key
    private Pictures pictures;
    /* ...other attributes omitted... */

    @DynamoDBHashKey(attributeName="Id")
    public Integer getId() { return id;}
    public void setId(Integer id) {this.id = id;}

    @DynamoDBAttribute(attributeName="Pictures")
    public Pictures getPictures() { return pictures;}
    public void setPictures(Pictures pictures) {this.pictures = pictures;}

    // Additional properties go here.

    @DynamoDBDocument
    public static class Pictures {
        private String frontView;
        private String rearView;
        private String sideView;

        @DynamoDBAttribute(attributeName = "FrontView")
        public String getFrontView() { return frontView; }
        public void setFrontView(String frontView) { this.frontView = frontView; }

        @DynamoDBAttribute(attributeName = "RearView")
        public String getRearView() { return rearView; }
        public void setRearView(String rearView) { this.rearView = rearView; }

        @DynamoDBAttribute(attributeName = "SideView")
        public String getSideView() { return sideView; }
        public void setSideView(String sideView) { this.sideView = sideView; }

     }
}
```

然后，您可以使用 `ProductCatalog` 保存新的 `Pictures` 项目，如以下示例所示。

```
ProductCatalogItem item = new ProductCatalogItem();

Pictures pix = new Pictures();
pix.setFrontView("http://example.com/products/123_front.jpg");
pix.setRearView("http://example.com/products/123_rear.jpg");
pix.setSideView("http://example.com/products/123_left_side.jpg");
item.setPictures(pix);

item.setId(123);

mapper.save(item);
```

生成的 `ProductCatalog` 项目将如下所示（JSON 格式）：

```
{
  "Id" : 123
  "Pictures" : {
    "SideView" : "http://example.com/products/123_left_side.jpg",
    "RearView" : "http://example.com/products/123_rear.jpg",
    "FrontView" : "http://example.com/products/123_front.jpg"
  }
}
```

## DynamoDBHashKey
<a name="DynamoDBMapper.Annotations.DynamoDBHashKey"></a>

将类属性映射到表的分区键。属性必须是标量字符串、数字或二进制类型。属性不能是集合类型。

假定您有一个 `ProductCatalog` 表，这个表使用 `Id` 作为主键。以下 Java 代码示例定义了一个 `CatalogItem` 类，并使用 `Id` 标签将其 `ProductCatalog` 属性映射到 `@DynamoDBHashKey` 表的主键。

```
@DynamoDBTable(tableName="ProductCatalog")
public class CatalogItem {
    private Integer Id;
   @DynamoDBHashKey(attributeName="Id")
   public Integer getId() {
        return Id;
   }
   public void setId(Integer Id) {
        this.Id = Id;
   }
   // Additional properties go here.
}
```

## DynamoDBIgnore
<a name="DynamoDBMapper.Annotations.DynamoDBIgnore"></a>

指示 `DynamoDBMapper` 实例忽略相关联的属性。在将数据保存到表中时，`DynamoDBMapper` 不会将此属性保存到表中。

 应用于非模型化属性的 getter 方法或类字段。如果注释直接应用于类字段，则必须在同一个类中声明相应的 getter 和 setter。

## DynamoDBIndexHashKey
<a name="DynamoDBMapper.Annotations.DynamoDBIndexHashKey"></a>

将类属性映射到全局二级属性的分区键。属性必须是标量字符串、数字或二进制类型。属性不能是集合类型。

如果您需要 `Query` 到全局二级索引，使用此注释。必须指定索引名称 (`globalSecondaryIndexName`)。如果类属性的名称不同于索引分区键，则您还必须指定该索引属性的名称 (`attributeName`)。

## DynamoDBIndexRangeKey
<a name="DynamoDBMapper.Annotations.DynamoDBIndexRangeKey"></a>

将类属性映射到全局二级索引或本地二级索引的排序键。属性必须是标量字符串、数字或二进制类型。属性不能是集合类型。

如果您需要对本地二级索引或全局二级索引执行 `Query` 操作，并使用索引排序键细化结果，请使用此注释。必须指定索引名称 (`globalSecondaryIndexName` 或 `localSecondaryIndexName`)。如果类属性的名称不同于索引排序键，则您还必须指定该索引属性的名称 (`attributeName`)。

## DynamoDBRangeKey
<a name="DynamoDBMapper.Annotations.DynamoDBRangeKey"></a>

将类属性映射到表的排序键。属性必须是标量字符串、数字或二进制类型。它不能是集合类型。

如果主键是复合键 (分区键和排序键)，您可以使用此标签将您的类字段映射到排序键。例如，假定您的 `Reply` 表存储了论坛话题的回复，每个话题有多条回复，因此，该表的主键为 `ThreadId` 和 `ReplyDateTime`。`ThreadId` 为分区键，`ReplyDateTime` 为排序键。

以下 Java 代码定义了 `Reply` 类，并将其映射到 `Reply` 表。它同时使用 `@DynamoDBHashKey` 和 `@DynamoDBRangeKey` 标签来确定映射到主键的类属性。

```
@DynamoDBTable(tableName="Reply")
public class Reply {
    private Integer id;
    private String replyDateTime;

    @DynamoDBHashKey(attributeName="Id")
    public Integer getId() { return id; }
    public void setId(Integer id) { this.id = id; }

    @DynamoDBRangeKey(attributeName="ReplyDateTime")
    public String getReplyDateTime() { return replyDateTime; }
    public void setReplyDateTime(String replyDateTime) { this.replyDateTime = replyDateTime; }

   // Additional properties go here.
}
```

## DynamoDBTable
<a name="DynamoDBMapper.Annotations.DynamoDBTable"></a>

确定 DynamoDB 中的目标表。例如，以下 Java 代码定义了 `Developer` 类，并将其映射到 DynamoDB 中的 `People` 表。

```
@DynamoDBTable(tableName="People")
public class Developer { ...}
```

`@DynamoDBTable` 注释可被继承。继承自 `Developer` 类的任何新类都映射到 `People` 表。例如，假定您创建了一个 `Lead` 类，它继承自 `Developer` 类。由于您将 `Developer` 类映射到了 `People` 表，因此 `Lead` 类对象也存储于同一表中。

`@DynamoDBTable` 也可以被覆盖。默认情况下，继承自 `Developer` 类的任何新类都映射到同一 `People` 表。然而，您可以覆盖这一默认映射。例如，如果您创建的某个类继承自 `Developer` 类，您可以通过添加 `@DynamoDBTable` 注释明确将其映射到另一个表（如以下 Java 代码示例所示）。

```
@DynamoDBTable(tableName="Managers")
public class Manager extends Developer { ...}
```

## DynamoDBTypeConverted
<a name="DynamoDBMapper.Annotations.DynamoDBTypeConverted"></a>

用于将属性标记为使用自定义类型转换器的注释。可以在用户定义的注释上进行注释以将更多属性传递到 `DynamoDBTypeConverter`。

 `DynamoDBTypeConverter` 接口可让您将自己的任意数据类型映射到受 DynamoDB 原生支持的数据类型。有关更多信息，请参阅 [在 DynamoDB 中映射任意数据](DynamoDBMapper.ArbitraryDataMapping.md)。

## DynamoDBTyped
<a name="DynamoDBMapper.Annotations.DynamoDBTyped"></a>

用于覆盖标准属性类型绑定的注释。如果应用了针对标准类型的默认属性绑定，则该类型不需要该注释。

## DynamoDBVersionAttribute
<a name="DynamoDBMapper.Annotations.DynamoDBVersionAttribute"></a>

确定一个类属性以存储乐观锁版本号。`DynamoDBMapper` 会在保存新项目时为此属性分配版本号，并且会在您每次更新该项目时增加版本号的值。仅支持数字标量类型。有关数据类型的更多信息，请参阅 [数据类型](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes)。有关版本控制的更多信息，请参阅[DynamoDB 和乐观锁（使用版本号）](DynamoDBMapper.OptimisticLocking.md)。

# DynamoDBMapper 的可选配置设置
<a name="DynamoDBMapper.OptionalConfig"></a>

在创建 `DynamoDBMapper` 实例时，它具有某些默认行为；您可以使用 `DynamoDBMapperConfig` 类来覆盖这些默认行为。

以下代码段创建具有自定义设置的 `DynamoDBMapper`：

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().build();

DynamoDBMapperConfig mapperConfig = DynamoDBMapperConfig.builder()
        .withSaveBehavior(DynamoDBMapperConfig.SaveBehavior.CLOBBER)
        .withConsistentReads(DynamoDBMapperConfig.ConsistentReads.CONSISTENT)
        .withTableNameOverride(null)
        .withPaginationLoadingStrategy(DynamoDBMapperConfig.PaginationLoadingStrategy.EAGER_LOADING)
    .build();

DynamoDBMapper mapper = new DynamoDBMapper(client, mapperConfig);
```

有关更多信息，请参阅[适用于 Java 的 AWS SDK API 参考](https://docs.aws.amazon.com/sdk-for-java/latest/reference/)的 [https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/datamodeling/DynamoDBMapperConfig.html](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/services/dynamodbv2/datamodeling/DynamoDBMapperConfig.html)。

您可以针对 `DynamoDBMapperConfig` 的实例使用以下参数：
+ `DynamoDBMapperConfig.ConsistentReads` 枚举值：
  + `EVENTUAL`—此映射器实例使用最终一致性读取请求。
  + `CONSISTENT`—此映射器实例使用强一致性读取请求。您可以将此可选设置用于 `load`、`query` 或 `scan` 操作。强一致性读取会影响性能和成本；有关更多信息，请参阅 DynamoDB [产品详细信息页](https://aws.amazon.com/dynamodb)。

  如果您未指定适用于映射器实例的读取一致性设置，则默认为 `EVENTUAL`。
**注意**  
此值适用于 DynamoDBMapper 的 `query`、`querypage`、`load` 和 `batch load` 操作。
+ `DynamoDBMapperConfig.PaginationLoadingStrategy` 枚举值 — 控制映射器实例如何处理分页数据列表（如，来自 `query` 或 `scan` 的结果）：
  + `LAZY_LOADING`—该映射器实例在可能时加载数据，并将所有加载的结果保留在内存中。
  + `EAGER_LOADING`—该映射器实例在列表初始化之后立即加载数据。
  + `ITERATION_ONLY`—您只能使用 Iterator 从列表读取。在迭代过程中，该列表会在加载下一页之前清除所有之前的结果，这样该列表就会将至多一页加载的结果保留在内存中。这也意味着只能对该列表迭代一次。当处理较大的项目时，为了减少内存开销，建议采用这种策略。

  如果您不为映射器实例指定分页加载策略，则会默认为 `LAZY_LOADING`。
+ `DynamoDBMapperConfig.SaveBehavior` 枚举值 – 指定映射器实例在保存操作期间如何处理属性：
  + `UPDATE`—在保存操作期间，所有已建模的属性都将更新，未建模的属性不受影响。基元数字类型 (byte、int 和 long) 设置为 0。对象类型设置为空。
  + `CLOBBER`—清除并替换保存操作期间的所有属性，包括未建模的属性。这是通过删除并重新创建项目完成的。受版本控制的字段约束也将被忽略。

   如果您未指定适用于映射器实例的保存行为，则会默认为 `UPDATE`。
**注意**  
DynamoDBMapper 事务操作不支持 `DynamoDBMapperConfig.SaveBehavior` 枚举。
+ `DynamoDBMapperConfig.TableNameOverride` 对象—指示映射器实例忽略由类的 `DynamoDBTable` 注释指定的表名称，改为使用您提供的不同表名称。当您在运行时将数据划分到多个表中时，可以使用它。

如果需要，您可以针对每个操作覆盖 `DynamoDBMapper` 的默认配置对象。

# DynamoDB 和乐观锁（使用版本号）
<a name="DynamoDBMapper.OptimisticLocking"></a>

*乐观锁*是一种确保正在更新（或删除）的客户端项目与 Amazon DynamoDB 中的项目相同的策略。如果您使用此策略，则将防止数据库写入由他人的写入覆盖，反之亦然。

使用乐观锁时，每个项目都具有一个充当版本号的属性。如果您检索表中的项目，则应用程序会记录该项目的版本号。您可以更新该项目，但只有在服务器端的版本号没有改变时才能更新。如果存在版本不匹配，则意味着其他人在您之前修改了该项目。更新尝试会失败，这是因为您拥有的是该项目的过时版本。如果发生此情况，您可以通过检索项目然后尝试更新来重试。乐观锁可防止您意外覆盖他人所做的更改。它还可防止他人意外覆盖您所做的更改。

虽然您可以实现自己的乐观锁策略，但适用于 Java 的 AWS SDK 提供了 `@DynamoDBVersionAttribute` 注释。在适用于表的映射类中，您需要指定一个用于存储版本号的属性，并使用此注释对其进行标记。当您保存对象时，DynamoDB 表中对应的项目就会具有存储相应版本号的属性。`DynamoDBMapper` 会在您第一次保存对象时分配一个版本号，并且在每次更新项目时递增版本号的值。只有在客户端对象版本与 DynamoDB 表中对应的项目版本号相匹配时，您的更新或删除请求才会成功。

 `ConditionalCheckFailedException`如果出现以下情况，则会引发 ：
+  您使用的乐观锁具有 `@DynamoDBVersionAttribute`，而服务器上的版本值与客户端的值不同。
+  您在使用 `DynamoDBMapper` 与 `DynamoDBSaveExpression` 保存数据时指定了自己的条件约束，而这些约束失败了。

**注意**  
DynamoDB 全局表在并发更新之间使用“以最后写入者为准”原则。如果使用全局表，则以最后写入者策略为准。因此，在这种情况下，锁定策略无法按预期方式工作。
`DynamoDBMapper` 事务写入操作在同一对象中不支持 `@DynamoDBVersionAttribute` 注释和条件表达式。如果事务写入中的对象使用 `@DynamoDBVersionAttribute` 进行了注释，并且还包含条件表达式，则将引发 SdkClientException。

例如，以下 Java 代码定义的 `CatalogItem` 类具有多个属性。`Version` 属性由 `@DynamoDBVersionAttribute` 注释进行标记。

**Example**  

```
@DynamoDBTable(tableName="ProductCatalog")
public class CatalogItem {

    private Integer id;
    private String title;
    private String ISBN;
    private Set<String> bookAuthors;
    private String someProp;
    private Long version;

    @DynamoDBHashKey(attributeName="Id")
    public Integer getId() { return id; }
    public void setId(Integer Id) { this.id = Id; }

    @DynamoDBAttribute(attributeName="Title")
    public String getTitle() { return title; }
    public void setTitle(String title) { this.title = title; }

    @DynamoDBAttribute(attributeName="ISBN")
    public String getISBN() { return ISBN; }
    public void setISBN(String ISBN) { this.ISBN = ISBN;}

    @DynamoDBAttribute(attributeName = "Authors")
    public Set<String> getBookAuthors() { return bookAuthors; }
    public void setBookAuthors(Set<String> bookAuthors) { this.bookAuthors = bookAuthors; }

    @DynamoDBIgnore
    public String getSomeProp() { return someProp;}
    public void setSomeProp(String someProp) {this.someProp = someProp;}

    @DynamoDBVersionAttribute
    public Long getVersion() { return version; }
    public void setVersion(Long version) { this.version = version;}
}
```

您可以将 `@DynamoDBVersionAttribute` 注释应用到基元封装类提供的可为空的类型 (例如 `Long` 和 `Integer`)。

乐观锁对这些 `DynamoDBMapper` 方法具有以下影响：
+ `save`— 对于新项目，`DynamoDBMapper` 分配初始版本号 1。如果您检索项目，然后更新它的一个或多个属性，并尝试保存所做更改，那么只有在客户端和服务器端的版本号匹配时保存操作才能成功。`DynamoDBMapper` 会自动递增版本号。
+ `delete` — `delete` 方法接受对象作为参数，并且 `DynamoDBMapper` 会在删除项目之前执行版本检查。在请求中指定 `DynamoDBMapperConfig.SaveBehavior.CLOBBER` 可以禁用版本检查。

  `DynamoDBMapper` 中的内部乐观锁实现利用了 DynamoDB 提供的有条件更新和有条件删除支持。
+ `transactionWrite` —
  + `Put`— 对于新项目，`DynamoDBMapper` 分配初始版本号 1。如果您检索项目，然后更新它的一个或多个属性，并尝试保存所做更改，那么只有在客户端和服务器端的版本号匹配时放置操作才能成功。`DynamoDBMapper` 会自动递增版本号。
  + `Update`— 对于新项目，`DynamoDBMapper` 分配初始版本号 1。如果您检索项目，然后更新它的一个或多个属性，并尝试保存所做更改，那么只有在客户端和服务器端的版本号匹配时更新操作才能成功。`DynamoDBMapper` 会自动递增版本号。
  + `Delete`—`DynamoDBMapper` 会在删除项目之前执行版本检查。仅当客户端与服务器端的版本号相匹配时，删除操作才会成功。
  + `ConditionCheck` — `ConditionCheck` 操作不支持 `@DynamoDBVersionAttribute` 注释。当 `ConditionCheck` 项目使用 `@DynamoDBVersionAttribute` 进行了注释时，将引发 SdkClientException。

## 禁用乐观锁
<a name="DynamoDBMapper.OptimisticLocking.Disabling"></a>

要禁用乐观锁，您可以将 `DynamoDBMapperConfig.SaveBehavior` 枚举值从 `UPDATE` 更改为 `CLOBBER`。您可以通过创建可跳过版本检查的 `DynamoDBMapperConfig` 实例，然后在所有请求中使用此实例来实现这一目的。有关 `DynamoDBMapperConfig.SaveBehavior` 和其他可选 `DynamoDBMapper` 参数的信息，请参阅[DynamoDBMapper 的可选配置设置](DynamoDBMapper.OptionalConfig.md)。

您也可以针对特定操作设置锁定行为。例如，以下 Java 代码段使用 `DynamoDBMapper` 保存目录项目。它可以通过将可选 `DynamoDBMapperConfig.SaveBehavior` 参数添加到 `DynamoDBMapperConfig` 方法来指定 `save`。

**注意**  
transactionWrite 方法不支持 DynamoDBMapperConfig.SaveBehavior 配置。不支持对 transactionWrite 禁用乐观锁。

**Example**  

```
DynamoDBMapper mapper = new DynamoDBMapper(client);

// Load a catalog item.
CatalogItem item = mapper.load(CatalogItem.class, 101);
item.setTitle("This is a new title for the item");
...
// Save the item.
mapper.save(item,
    new DynamoDBMapperConfig(
        DynamoDBMapperConfig.SaveBehavior.CLOBBER));
```

# 在 DynamoDB 中映射任意数据
<a name="DynamoDBMapper.ArbitraryDataMapping"></a>

除支持的 Java 类型（请参阅 [DynamoDBMapper for Java 支持的数据类型](DynamoDBMapper.DataTypes.md)）外，您还可以使用应用程序中不能直接映射到 Amazon DynamoDB 类型的类型。要映射这些类型，您必须提供一种实现来将复杂类型转换为 DynamoDB 支持的类型（反之亦然），并且使用 `@DynamoDBTypeConverted` 注释来注释这一复杂类型访问器方法。转换器代码会在保存或加载对象时转换数据。另外，所有使用复杂类型的操作都可以使用转换器代码。请注意，当您在查询和扫描操作期间比较数据时，比较所针对的是 DynamoDB 中存储的数据。

例如，看看以下 `CatalogItem` 类，它定义了 `Dimension` 属性 (属于 `DimensionType`)。此属性将以高度、宽度和厚度的形式存储项目尺寸。假定您决定将这些项目尺寸存储为 DynamoDB 中的字符串 (例如 8.5x11x.05)。以下示例提供可将 `DimensionType` 对象转换为字符串并将字符串转换为 `DimensionType` 的转换器代码。



**注意**  
此代码示例假定您已按照 [为 DynamoDB 中的代码示例创建表和加载数据](SampleData.md) 部分的说明，将数据加载到您的帐户的 DynamoDB。  
有关运行以下示例的分步说明，请参阅 [Java 代码示例](CodeSamples.Java.md)。

**Example**  

```
public class DynamoDBMapperExample {

    static AmazonDynamoDB client;

    public static void main(String[] args) throws IOException {

        // Set the AWS region you want to access.
        Regions usWest2 = Regions.US_WEST_2;
        client = AmazonDynamoDBClientBuilder.standard().withRegion(usWest2).build();

        DimensionType dimType = new DimensionType();
        dimType.setHeight("8.00");
        dimType.setLength("11.0");
        dimType.setThickness("1.0");

        Book book = new Book();
        book.setId(502);
        book.setTitle("Book 502");
        book.setISBN("555-5555555555");
        book.setBookAuthors(new HashSet<String>(Arrays.asList("Author1", "Author2")));
        book.setDimensions(dimType);

        DynamoDBMapper mapper = new DynamoDBMapper(client);
        mapper.save(book);

        Book bookRetrieved = mapper.load(Book.class, 502);
        System.out.println("Book info: " + "\n" + bookRetrieved);

        bookRetrieved.getDimensions().setHeight("9.0");
        bookRetrieved.getDimensions().setLength("12.0");
        bookRetrieved.getDimensions().setThickness("2.0");

        mapper.save(bookRetrieved);

        bookRetrieved = mapper.load(Book.class, 502);
        System.out.println("Updated book info: " + "\n" + bookRetrieved);
    }

    @DynamoDBTable(tableName = "ProductCatalog")
    public static class Book {
        private int id;
        private String title;
        private String ISBN;
        private Set<String> bookAuthors;
        private DimensionType dimensionType;

        // Partition key
        @DynamoDBHashKey(attributeName = "Id")
        public int getId() {
            return id;
        }

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

        @DynamoDBAttribute(attributeName = "Title")
        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        @DynamoDBAttribute(attributeName = "ISBN")
        public String getISBN() {
            return ISBN;
        }

        public void setISBN(String ISBN) {
            this.ISBN = ISBN;
        }

        @DynamoDBAttribute(attributeName = "Authors")
        public Set<String> getBookAuthors() {
            return bookAuthors;
        }

        public void setBookAuthors(Set<String> bookAuthors) {
            this.bookAuthors = bookAuthors;
        }

        @DynamoDBTypeConverted(converter = DimensionTypeConverter.class)
        @DynamoDBAttribute(attributeName = "Dimensions")
        public DimensionType getDimensions() {
            return dimensionType;
        }

        @DynamoDBAttribute(attributeName = "Dimensions")
        public void setDimensions(DimensionType dimensionType) {
            this.dimensionType = dimensionType;
        }

        @Override
        public String toString() {
            return "Book [ISBN=" + ISBN + ", bookAuthors=" + bookAuthors + ", dimensionType= "
                    + dimensionType.getHeight() + " X " + dimensionType.getLength() + " X "
                    + dimensionType.getThickness()
                    + ", Id=" + id + ", Title=" + title + "]";
        }
    }

    static public class DimensionType {

        private String length;
        private String height;
        private String thickness;

        public String getLength() {
            return length;
        }

        public void setLength(String length) {
            this.length = length;
        }

        public String getHeight() {
            return height;
        }

        public void setHeight(String height) {
            this.height = height;
        }

        public String getThickness() {
            return thickness;
        }

        public void setThickness(String thickness) {
            this.thickness = thickness;
        }
    }

    // Converts the complex type DimensionType to a string and vice-versa.
    static public class DimensionTypeConverter implements DynamoDBTypeConverter<String, DimensionType> {

        @Override
        public String convert(DimensionType object) {
            DimensionType itemDimensions = (DimensionType) object;
            String dimension = null;
            try {
                if (itemDimensions != null) {
                    dimension = String.format("%s x %s x %s", itemDimensions.getLength(), itemDimensions.getHeight(),
                            itemDimensions.getThickness());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return dimension;
        }

        @Override
        public DimensionType unconvert(String s) {

            DimensionType itemDimension = new DimensionType();
            try {
                if (s != null && s.length() != 0) {
                    String[] data = s.split("x");
                    itemDimension.setLength(data[0].trim());
                    itemDimension.setHeight(data[1].trim());
                    itemDimension.setThickness(data[2].trim());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

            return itemDimension;
        }
    }
}
```

# DynamoDBMapper 示例
<a name="DynamoDBMapper.Examples"></a>

AWS SDK for Java 提供了 `DynamoDBMapper` 类，使您能够将客户端类映射到 DynamoDB 表。要使用 `DynamoDBMapper`，您应在代码中定义 DynamoDB 表中项目与其相应对象实例之间的关系。`DynamoDBMapper` 类让您能够对项目执行各种创建、读取、更新和删除 (CRUD) 操作，并对表运行查询和扫描。

要详细了解如何使用 `DynamoDBMapper`，请参阅《AWS SDK for Java 1.x 开发人员指南》**中的[使用 AWS SDK for Java 的 DynamoDB 示例](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/examples-dynamodb.html)。

# Java 2.x：DynamoDB 增强型客户端
<a name="DynamoDBEnhanced"></a>

DynamoDB 增强型客户端是属于 适用于 Java 的 AWS SDK 版本 2（v2）一部分的高级库。它提供一种将客户端类映射到 DynamoDB 表的简单方法。您可以在代码中定义表与其相应模型类之间的关系。在定义这些关系后，您可以直观地对 DynamoDB 中的表或项目执行各种创建、读取、更新或删除（CRUD）操作。

有关如何在 DynamoDB 中使用增强型客户端的更多信息，请参阅[在 适用于 Java 的 AWS SDK 2.x 中使用 DynamoDB 增强型客户端](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/dynamodb-enhanced-client.html)。

# 在 DynamoDB 中使用 .NET 文档模型
<a name="DotNetSDKMidLevel"></a>

适用于 .NET 的 AWS SDK 提供了文档模型类，其中封装了一些低级 Amazon DynamoDB 操作来进一步简化编码工作。在文档模型中，主要的类有 `Table` 和 `Document`。`Table` 类可以提供 `PutItem`、`GetItem` 和 `DeleteItem` 等数据操作方法。同时，它还提供 `Query` 和 `Scan` 方法。`Document` 类表示表中的一个项目。

您可以在 `Amazon.DynamoDBv2.DocumentModel` 命名空间中找到前面所述的文档模型类。

**注意**  
您不能使用这些文档模型类创建、更新和删除表。不过，文档模型支持大部分常见的数据操作。

**Topics**
+ [支持的数据类型](#MidLevelAPILimitations.SupportedTypes)

## 支持的数据类型
<a name="MidLevelAPILimitations.SupportedTypes"></a>

文档模型支持一组原始的 .NET 数据类型和集合数据类型。该模型支持以下基元数据类型。
+ `bool`
+ `byte` 
+ `char`
+ `DateTime`
+ `decimal`
+ `double`
+ `float`
+ `Guid`
+ `Int16`
+ `Int32`
+ `Int64`
+ `SByte`
+ `string`
+ `UInt16`
+ `UInt32`
+ `UInt64`

下表汇总了上述 .NET 类型到 DynamoDB 类型的映射。


****  

| .NET 原始类型 | DynamoDB 类型 | 
| --- | --- | 
|  所有数字类型  |  `N`（数字类型）  | 
|  所有字符串类型  |  `S`（字符串类型）   | 
|  MemoryStream，byte []  |  `B`（二进制类型）   | 
| 布尔 | N（数字类型）。0 表示 false，1 表示 true。 | 
| DateTime | S（字符串类型）。DateTime 值存储为符合 ISO-8601 格式的字符串。 | 
| Guid | S（字符串类型）。 | 
| 集合类型（列表、哈希集和数组） | BS（二进制集）类型、SS（字符串集）类型或 NS（数字集）类型。 | 

适用于 .NET 的 AWS SDK 定义了用于将 DynamoDB 的布尔值、null、列表和映射类型映射到 .NET 文档模型 API 的类型：
+ 将 `DynamoDBBool` 用于布尔值类型。
+ 将 `DynamoDBNull`用于 null 类型。
+ 将 `DynamoDBList` 用于列表类型。
+ 将 `Document` 用于映射类型。

**注意**  
支持空二进制值。
支持读取空字符串值。写入 DynamoDB 时，字符串集类型的属性值中支持空字符串属性值。从写入请求中删除列表或映射类型中包含的字符串类型的空字符串属性值和空字符串值

# 结合使用 .NET 对象持久化模型和 DynamoDB
<a name="DotNetSDKHighLevel"></a>

适用于 .NET 的 AWS SDK 提供的对象持久化模型让您能够将客户端类映射到 Amazon DynamoDB 表。然后，每个对象实例映射到对应表中的项目。为了在表中保存您的客户端对象，对象持久化模型提供了 `DynamoDBContext` 类，这是 DynamoDB 的入口点。这个类提供到 DynamoDB 的连接，让您能够访问表、执行各种 CRUD 操作，以及执行查询。

对象持久化模型提供一系列特性，用于将客户端类映射到表，以及将属性/字段映射到表特性。

**注意**  
对象持久化模型不提供用于创建、更新或删除表的 API。它只提供数据操作。要创建、更新和删除表，您必须使用 适用于 .NET 的 AWS SDK 低级 API。

以下示例显示对象持久化模型的工作原理。从 `ProductCatalog` 表开始。使用 `Id` 作为其主键。

```
ProductCatalog(Id, ...)
```

假设您有一个 `Book` 类，`Title`、`ISBN` 和 `Authors` 属性。您可以添加由对象持久化模型定义的属性，将 `Book` 类映射到 `ProductCatalog` 表。如以下 C\$1 代码示例所示。

**Example**  

```
[DynamoDBTable("ProductCatalog")]
  public class Book
  {
    [DynamoDBHashKey]
    public int Id { get; set; }

    public string Title { get; set; }
    public int ISBN { get; set; }

    [DynamoDBProperty("Authors")]
    public List<string> BookAuthors { get; set; }

    [DynamoDBIgnore]
    public string CoverPage { get; set; }
  }
```

在上述示例中，`DynamoDBTable` 属性将 `Book` 类映射到 `ProductCatalog` 表。

对象持久化模型支持类属性和表特性之间的明确映射和默认映射。
+ **明确映射—**要将属性映射到主键，您必须使用 `DynamoDBHashKey` 和 `DynamoDBRangeKey` 对象持久化模型特性。此外，对于非主键特性，如果类中的属性名称与您希望将其映射到的对应表特性名称不同，那么您必须通过明确添加 `DynamoDBProperty` 特性定义这一映射。

  在上述示例中，`Id` 属性映射到具有相同名称的主键，`BookAuthors` 属性映射到 `ProductCatalog` 表的 `Authors` 属性。
+ **默认映射—**默认情况下，对象持久化模型会将类属性映射到表中同名的特性。

  在上述示例中，属性 `Title` 和 `ISBN` 映射到 `ProductCatalog` 表中具有相同名称的属性。

您无需映射每一个类属性，而是可以通过添加 `DynamoDBIgnore` 属性来标识这些属性。将 `Book` 实例保存到表后，`DynamoDBContext` 实例不包含 `CoverPage` 属性。当您检索书籍实例时，也不会返回此属性。

您可以映射 .NET 基元类型（如 int 和 string）的属性。您还可以映射任意数据类型，只要提供适当的转换器以将任意数据映射到 DynamoDB 类型之一。要了解有关映射任意类型的信息，请参阅[通过使用 适用于 .NET 的 AWS SDK 对象持久化模型的 DynamoDB 映射任意数据](DynamoDBContext.ArbitraryDataMapping.md)。

对象持久化模型支持乐观锁定。在更新操作期间，这可确保您拥有要更新的项目的最新副本。有关更多信息，请参阅 [将 DynamoDB 和 适用于 .NET 的 AWS SDK 对象持久化模型结合使用的乐观锁](DynamoDBContext.VersionSupport.md)。

有关更多信息，请参阅以下主题。

**Topics**
+ [支持的数据类型](#DotNetDynamoDBContext.SupportedTypes)
+ [.NET 对象持久化模型中的 DynamoDB 属性](DeclarativeTagsList.md)
+ [.NET 对象持久化模型中的 DynamoDBContext 类](DotNetDynamoDBContext.md)
+ [将 DynamoDB 和 适用于 .NET 的 AWS SDK 对象持久化模型结合使用的乐观锁](DynamoDBContext.VersionSupport.md)
+ [通过使用 适用于 .NET 的 AWS SDK 对象持久化模型的 DynamoDB 映射任意数据](DynamoDBContext.ArbitraryDataMapping.md)

## 支持的数据类型
<a name="DotNetDynamoDBContext.SupportedTypes"></a>

对象持久化模型支持一系列 .NET 基元数据类型、集合数据类型和其他任意数据类型。该模型支持以下基元数据类型。
+ `bool`
+ `byte` 
+ `char`
+ `DateTime`
+ `decimal`
+ `double`
+ `float`
+ `Int16`
+ `Int32`
+ `Int64`
+ `SByte`
+ `string`
+ `UInt16`
+ `UInt32`
+ `UInt64`

对象持久化模型还支持 .NET 集合类型。`DynamoDBContext`能够转换具体集合类型和简单的普通旧 CLR 对象（POCO）。

下表汇总了上述 .NET 类型到 DynamoDB 类型的映射。


****  

| .NET 原始类型 | DynamoDB 类型 | 
| --- | --- | 
|  所有数字类型  |  `N`（数字类型）  | 
|  所有字符串类型  |  `S`（字符串类型）   | 
|  MemoryStream，byte []  |  `B`（二进制类型）   | 
| 布尔 | N（数字类型）。0 表示 false，1 表示 true。 | 
| 集合类型 | BS（二进制集）类型、SS（字符串集）类型或 NS（数字集）类型。 | 
| 日期时间 | S（字符串类型）。DateTime 值存储为符合 ISO-8601 格式的字符串。 | 

对象持久化模型还支持任意数据类型。但是，您必须提供转换器代码才能将复杂类型映射到 DynamoDB 类型。

**注意**  
支持空二进制值。
支持读取空字符串值。写入 DynamoDB 时，字符串集类型的属性值中支持空字符串属性值。从写入请求中删除列表或映射类型中包含的字符串类型的空字符串属性值和空字符串值

# .NET 对象持久化模型中的 DynamoDB 属性
<a name="DeclarativeTagsList"></a>

本节介绍了对象持久化模型提供的属性，以便您可以将类和属性映射到 DynamoDB 表和属性。

**注意**  
在以下属性中，仅 `DynamoDBTable` 和 `DynamoDBHashKey` 是必需的。

## DynamoDBGlobalSecondaryIndexHashKey
<a name="w2aac17b9c21c23c37b7"></a>

将类属性映射到全局二级属性的分区键。如果您需要 `Query` 到全局二级索引，请使用此属性。

## DynamoDBGlobalSecondaryIndexRangeKey
<a name="w2aac17b9c21c23c37b9"></a>

将类属性映射到全局二级索引的排序键。如果您需要对全局二级索引执行 `Query` 操作，并想使用索引排序键细化结果，请使用此属性。

## DynamoDBHashKey
<a name="w2aac17b9c21c23c37c11"></a>

将类属性映射到表主键的分区键。主键属性不能是集合类型。

下面的 C\$1 代码示例将 `Book` 类映射到 `ProductCatalog` 表，将 `Id` 属性映射到表的主键分区键。

```
[DynamoDBTable("ProductCatalog")]
public class Book 
{
    [DynamoDBHashKey]
    public int Id { get; set; }

    // Additional properties go here.
}
```

## DynamoDBIgnore
<a name="w2aac17b9c21c23c37c13"></a>

指示应忽略关联属性。如果您不想保存任何类属性，可以添加此属性来指示 `DynamoDBContext` 将对象保存到表格时不包含此属性。

## DynamoDBLocalSecondaryIndexRangeKey
<a name="w2aac17b9c21c23c37c15"></a>

将类属性映射到本地二级索引的排序键。如果您需要对本地二级索引执行 `Query` 操作，并想使用索引排序键细化结果，请使用此属性。

## DynamoDBProperty
<a name="w2aac17b9c21c23c37c17"></a>

将属性映射到表属性。如果类属性映射到同名表属性，则无需指定此属性。但是，如果名称不同，您可以使用此标记提供映射。在以下 C\$1 代码段中，`DynamoDBProperty` 将 `BookAuthors` 属性映射到表中的 `Authors` 属性。

```
[DynamoDBProperty("Authors")]
public List<string> BookAuthors { get; set; }
```

将对象数据保存到相应的表，`DynamoDBContext` 使用此映射信息创建 `Authors` 属性。

## DynamoDBRenamable
<a name="w2aac17b9c21c23c37c19"></a>

指定类属性的替代名称。如果您正在编写自定义转换器，用于将任意数据映射到类属性的名称与表属性不同的 DynamoDB 表，则此选项非常有用。

## DynamoDBRangeKey
<a name="w2aac17b9c21c23c37c21"></a>

将类属性映射到表主键的排序键。如果表具有复合主键（分区键和排序键），您必须同时指定类映射的 `DynamoDBHashKey` 和 `DynamoDBRangeKey` 属性。

例如，示例表 `Reply` 有一个由 `Id` 分区键和 `Replenishment` 排序键构成的主键。下面的 C\$1 代码示例将 `Reply` 类映射到 `Reply` 表。类定义还指示其两个属性映射到主键。

```
[DynamoDBTable("Reply")]
public class Reply 
{
   [DynamoDBHashKey]
   public int ThreadId { get; set; }
   [DynamoDBRangeKey]
   public string Replenishment { get; set; }
   
   // Additional properties go here.
}
```

## DynamoDBTable
<a name="w2aac17b9c21c23c37c23"></a>

确定类映射到的 DynamoDB 中的目标表。例如，以下 C\$1 代码示例将 `Developer` 类映射到 DynamoDB 的 `People` 表。

```
[DynamoDBTable("People")]
public class Developer { ...}
```

此属性可以被继承或覆盖。
+ `DynamoDBTable` 属性可被继承。在上述示例中，如果添加继承自 `Developer` 类的新类 `Lead`，则还映射到 `People` 表。`Developer` 和 `Lead` 对象存储在 `People` 表。
+ `DynamoDBTable` 属性也可以被覆盖。在下面的 C\$1 代码示例中，`Manager` 类继承自 `Developer` 类。但是，明确添加 `DynamoDBTable` 属性将类映射到另一个表 (`Managers`)。

  ```
  [DynamoDBTable("Managers")]
  public class Manager : Developer { ...}
  ```

 您可以添加可选参数 `LowerCamelCaseProperties`，在将对象存储到表时请求 DynamoDB 将属性名称的第一个字母设置为小写，如下面的 C\$1 示例所示。

```
[DynamoDBTable("People", LowerCamelCaseProperties=true)]
public class Developer 
{
    string DeveloperName;
    ...
}
```

保存 `Developer` 类实例时，`DynamoDBContext` 保存 `DeveloperName` 属性作为 `developerName`。

## DynamoDBVersion
<a name="w2aac17b9c21c23c37c25"></a>

标识用于存储项目版本号的类属性。有关版本控制的更多信息，请参阅[将 DynamoDB 和 适用于 .NET 的 AWS SDK 对象持久化模型结合使用的乐观锁](DynamoDBContext.VersionSupport.md)。

# .NET 对象持久化模型中的 DynamoDBContext 类
<a name="DotNetDynamoDBContext"></a>

`DynamoDBContext` 类是 Amazon DynamoDB 的入口点。这个类提供到 DynamoDB 的连接，让您能够访问各种表的数据，执行各种 CRUD 操作，以及执行查询。`DynamoDBContext` 类提供了以下方法。

**Topics**
+ [Create​MultiTable​BatchGet](#w2aac17b9c21c23c39b7)
+ [Create​MultiTable​BatchWrite](#w2aac17b9c21c23c39b9)
+ [CreateBatchGet](#w2aac17b9c21c23c39c11)
+ [CreateBatchWrite](#w2aac17b9c21c23c39c13)
+ [删除](#w2aac17b9c21c23c39c15)
+ [Dispose](#w2aac17b9c21c23c39c17)
+ [Execute​Batch​Get](#w2aac17b9c21c23c39c19)
+ [Execute​Batch​Write](#w2aac17b9c21c23c39c21)
+ [FromDocument](#w2aac17b9c21c23c39c23)
+ [FromQuery](#w2aac17b9c21c23c39c25)
+ [FromScan](#w2aac17b9c21c23c39c27)
+ [Get​Target​Table](#w2aac17b9c21c23c39c29)
+ [Load](#w2aac17b9c21c23c39c31)
+ [查询](#w2aac17b9c21c23c39c33)
+ [Save](#w2aac17b9c21c23c39c35)
+ [Scan](#w2aac17b9c21c23c39c37)
+ [ToDocument](#w2aac17b9c21c23c39c39)
+ [指定 DynamoDBContext 的可选参数](#OptionalConfigParams)

## Create​MultiTable​BatchGet
<a name="w2aac17b9c21c23c39b7"></a>

创建 `MultiTableBatchGet` 对象，由多个单独 `BatchGet` 对象组成。每个 `BatchGet` 对象可用于从单个 DynamoDB 表中检索项目。

要从表中检索项目，请使用 `ExecuteBatchGet` 方法，传递 `MultiTableBatchGet` 对象作为参数。

## Create​MultiTable​BatchWrite
<a name="w2aac17b9c21c23c39b9"></a>

创建 `MultiTableBatchWrite` 对象，由多个单独 `BatchWrite` 对象组成。每个 `BatchWrite` 对象可用于写入或删除单个 DynamoDB 表中的项目。

要写入表，请使用 `ExecuteBatchWrite` 方法，传递 `MultiTableBatchWrite` 对象作为参数。

## CreateBatchGet
<a name="w2aac17b9c21c23c39c11"></a>

创建 `BatchGet` 对象，可以用于从表中检索多个项目。

## CreateBatchWrite
<a name="w2aac17b9c21c23c39c13"></a>

创建 `BatchWrite` 对象，可以用于将多个项目放入表中，或者从表中删除多个项目。

## 删除
<a name="w2aac17b9c21c23c39c15"></a>

删除表中的项目。此方法需要要删除的项目的主键。您可以提供主键值或包含主键值的客户端对象作为此方法的参数。
+ 如果将客户端对象指定为参数，并且启用了乐观锁定，则只有在对象的客户端版本和服务器端版本匹配时，删除才会成功。
+ 如果仅将主键值指定为参数，则无论您是否启用了乐观锁定，删除都会成功。

**注意**  
要在后台执行此操作，请使用 `DeleteAsync` 方法。

## Dispose
<a name="w2aac17b9c21c23c39c17"></a>

处置所有托管和非托管资源。

## Execute​Batch​Get
<a name="w2aac17b9c21c23c39c19"></a>

从一个或多个表中读取数据，处理 `MultiTableBatchGet` 中的所有 `BatchGet` 对象。

**注意**  
要在后台执行此操作，请使用`ExecuteBatchGetAsync`方法。

## Execute​Batch​Write
<a name="w2aac17b9c21c23c39c21"></a>

在一个或多个表中写入或删除数据，处理 `MultiTableBatchWrite` 中的所有 `BatchWrite` 对象。

**注意**  
要在后台执行此操作，请使用`ExecuteBatchWriteAsync`方法。

## FromDocument
<a name="w2aac17b9c21c23c39c23"></a>

给定一个 `Document` 实例，`FromDocument` 方法返回客户端类的实例。

如果要将文档模型类与对象持久化模型一起使用来执行任何数据操作，这将非常有用。有关 适用于 .NET 的 AWS SDK 提供的文档模型类的更多信息，请参阅[在 DynamoDB 中使用 .NET 文档模型](DotNetSDKMidLevel.md)。

假设您有一个名为 `doc` 的 `Document` 对象，其中包含 `Forum` 项目。（要了解如何构造此对象，请参阅本主题稍后的 `ToDocument` 方法说明。） 您可以使用 `FromDocument` 从 `Document` 检索 `Forum` 项目，如以下 C\$1 代码示例所示。

**Example**  

```
forum101 = context.FromDocument<Forum>(101);
```

**注意**  
如果您的`Document`对象实现`IEnumerable`接口，您可以使用`FromDocuments`方法。这允许您遍历`Document`的所有类实例。

## FromQuery
<a name="w2aac17b9c21c23c39c25"></a>

运行`Query`操作，查询参数定义在`QueryOperationConfig`对象。

**注意**  
要在后台执行此操作，请使用`FromQueryAsync`方法。

## FromScan
<a name="w2aac17b9c21c23c39c27"></a>

运行`Scan`操作，扫描参数定义在`ScanOperationConfig`对象。

**注意**  
要在后台执行此操作，请使用`FromScanAsync`方法。

## Get​Target​Table
<a name="w2aac17b9c21c23c39c29"></a>

检索指定类型的目标表。如果您正在编写用于将任意数据映射到 DynamoDB 表的自定义转换器，并且需要确定哪个表与自定义数据类型相关联，则此选项非常有用。

## Load
<a name="w2aac17b9c21c23c39c31"></a>

检索表中的项目。方法只需要要检索的项目的主键。

默认情况下，DynamoDB 所返回的项目的值采用最终一致性。有关最终一致性模式的信息，请参阅[DynamoDB 读取一致性](HowItWorks.ReadConsistency.md)。

`Load` 或 `LoadAsync` 方法调用 [GetItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_GetItem.html) 操作，该操作要求您为表指定主键。由于 `GetItem` 忽略了 `IndexName` 参数，因此您无法使用索引的分区或排序键加载项目。因此，必须使用表的主键来加载项目。

**注意**  
要在后台执行此操作，请使用 `LoadAsync` 方法。要查看使用 `LoadAsync` 方法对 DynamoDB 表执行高级 CRUD 操作的示例，请参阅以下示例。

```
    /// <summary>
    /// Shows how to perform high-level CRUD operations on an Amazon DynamoDB
    /// table.
    /// </summary>
    public class HighLevelItemCrud
    {
        public static async Task Main()
        {
            var client = new AmazonDynamoDBClient();
            DynamoDBContext context = new DynamoDBContext(client);
            await PerformCRUDOperations(context);
        }

        public static async Task PerformCRUDOperations(IDynamoDBContext context)
        {
            int bookId = 1001; // Some unique value.
            Book myBook = new Book
            {
                Id = bookId,
                Title = "object persistence-AWS SDK for.NET SDK-Book 1001",
                Isbn = "111-1111111001",
                BookAuthors = new List<string> { "Author 1", "Author 2" },
            };

            // Save the book to the ProductCatalog table.
            await context.SaveAsync(myBook);

            // Retrieve the book from the ProductCatalog table.
            Book bookRetrieved = await context.LoadAsync<Book>(bookId);

            // Update some properties.
            bookRetrieved.Isbn = "222-2222221001";

            // Update existing authors list with the following values.
            bookRetrieved.BookAuthors = new List<string> { " Author 1", "Author x" };
            await context.SaveAsync(bookRetrieved);

            // Retrieve the updated book. This time, add the optional
            // ConsistentRead parameter using DynamoDBContextConfig object.
            await context.LoadAsync<Book>(bookId, new DynamoDBContextConfig
            {
                ConsistentRead = true,
            });

            // Delete the book.
            await context.DeleteAsync<Book>(bookId);

            // Try to retrieve deleted book. It should return null.
            Book deletedBook = await context.LoadAsync<Book>(bookId, new DynamoDBContextConfig
            {
                ConsistentRead = true,
            });

            if (deletedBook == null)
            {
                Console.WriteLine("Book is deleted");
            }
        }
    }
```

## 查询
<a name="w2aac17b9c21c23c39c33"></a>

根据您提供的查询参数查询表。

只有当表或索引具有复合主键（分区键和排序键）时，您才能对其执行查询。在查询时，您必须指定分区键以及适用于排序键的条件。

假设您有一个客户端`Reply`类映射到 DynamoDB 的 `Reply` 表。以下 C\$1 代码示例查询 `Reply` 以查找过去 15 天内发布的论坛话题回复。这些区域有：`Reply`表中有一个主键，该主键具有`Id`分区键和`ReplyDateTime`排序键。

**Example**  

```
DynamoDBContext context = new DynamoDBContext(client);

string replyId = "DynamoDB#DynamoDB Thread 1"; //Partition key
DateTime twoWeeksAgoDate = DateTime.UtcNow.Subtract(new TimeSpan(14, 0, 0, 0)); // Date to compare.
IEnumerable<Reply> latestReplies = context.Query<Reply>(replyId, QueryOperator.GreaterThan, twoWeeksAgoDate);
```

该查询会一系列 `Reply` 对象。

在默认情况下，`Query` 方法返回“延迟加载”`IEnumerable`集合。它最初只返回一页结果，然后在需要时发出服务调用请求下一页结果。要获取所有匹配项目，请对 `IEnumerable` 集合进行迭代。

如果表具有简单主键（分区键），则无法使用 `Query` 方法。相反，您可以使用`Load`方法并提供用于检索项目的分区键。

**注意**  
要在后台执行此操作，请使用`QueryAsync`方法。

## Save
<a name="w2aac17b9c21c23c39c35"></a>

将指定对象保存到表中。如果表中不存在输入对象中指定的主键，则方法会将新项目添加到表中。如果存在主键，此方法就会更新现有项目。

如果您配置了乐观锁定，则仅当客户端和项目的服务器端版本匹配时，更新才会成功。有关更多信息，请参阅 [将 DynamoDB 和 适用于 .NET 的 AWS SDK 对象持久化模型结合使用的乐观锁](DynamoDBContext.VersionSupport.md)。

**注意**  
要在后台执行此操作，请使用`SaveAsync`方法。

## Scan
<a name="w2aac17b9c21c23c39c37"></a>

执行整个表扫描。

您可以通过指定扫描条件来筛选扫描结果。可以根据表中的任何属性对条件进行评估。假设您有一个客户端类 `Book` 映射到 DynamoDB 的 `ProductCatalog` 表。以下 C\$1 示例扫描表并仅返回价格小于 0 的书籍项目。

**Example**  

```
IEnumerable<Book> itemsWithWrongPrice = context.Scan<Book>(
                    new ScanCondition("Price", ScanOperator.LessThan, price),
                    new ScanCondition("ProductCategory", ScanOperator.Equal, "Book")
      );
```

在默认情况下，`Scan` 方法返回“延迟加载”`IEnumerable`集合。它最初只返回一页结果，然后在需要时发出服务调用请求下一页结果。要获取所有匹配项，请对 `IEnumerable` 集合进行迭代。

出于性能考虑，您应查询表并避免表扫描。

**注意**  
要在后台执行此操作，请使用`ScanAsync`方法。

## ToDocument
<a name="w2aac17b9c21c23c39c39"></a>

返回类实例中的 `Document` 文档模型类。

如果要将文档模型类与对象持久化模型一起使用来执行任何数据操作，这将非常有用。有关 适用于 .NET 的 AWS SDK 提供的文档模型类的更多信息，请参阅[在 DynamoDB 中使用 .NET 文档模型](DotNetSDKMidLevel.md)。

假设您有一个映射到示例 `Forum` 表的客户端类。然后，您可以使用 `DynamoDBContext` 从 `Forum` 表获取一个项目作为 `Document` 对象，如以下 C\$1 代码示例所示。

**Example**  

```
DynamoDBContext context = new DynamoDBContext(client);

Forum forum101 = context.Load<Forum>(101); // Retrieve a forum by primary key.
Document doc = context.ToDocument<Forum>(forum101);
```

## 指定 DynamoDBContext 的可选参数
<a name="OptionalConfigParams"></a>

使用对象持久化模型时，您可以为 `DynamoDBContext` 指定以下可选参数。
+ **`ConsistentRead`—**使用 `Load`、`Query` 或 `Scan` 操作检索数据时，可以添加此可选参数来请求数据的最新值。
+ **`IgnoreNullValues`—**此参数通知 `DynamoDBContext` 在 `Save` 操作时忽略属性空值。如果此参数为 false（或未设置），则空值将被解释为删除特定属性的指令。
+ **`SkipVersionCheck`—**此参数通知`DynamoDBContext`保存或删除项目时不比较版本。有关版本控制的更多信息，请参阅[将 DynamoDB 和 适用于 .NET 的 AWS SDK 对象持久化模型结合使用的乐观锁](DynamoDBContext.VersionSupport.md)。
+ **`TableNamePrefix`—**在所有表名称前加上特定字符串。如果此参数为 null（或未设置），则不使用前缀。
+ `DynamoDBEntryConversion` – 指定客户端使用的转换架构。可以将此参数设置为版本 V1 或 V2。默认版本是 V1。

  根据您设置的版本，此参数的行为会发生变化。例如：
  + 在 V1 中，`bool` 数据类型转换为 `N` 数字类型，其中 0 代表 false，1 代表 true。在 V2 中，`bool` 转换为 `BOOL`。
  + 在 V2 中，列表和数组不与哈希集组合在一起。数字、基于字符串的类型和基于二进制的类型的列表和数组将转换为 `L`（列表）类型，该类型可以发送空值以更新列表。这与 V1 不同，在 V1 中，不通过网络发送空列表。

    在 V1 中，集合类型（例如列表、哈希集和数组）的处理方式相同。列表、哈希集和数组将转换为 `NS`（数字集）类型。

  以下示例将转换架构版本设置为 V2，这会更改 .NET 类型和 DynamoDB 数据类型之间的转换行为。

  ```
  var config = new DynamoDBContextConfig
  {
      Conversion = DynamoDBEntryConversion.V2
  };
  var contextV2 = new DynamoDBContext(client, config);
  ```

下面的 C\$1 示例通过指定前面的两个可选参数（`ConsistentRead` 和 `SkipVersionCheck`），来创建一个新的 `DynamoDBContext`。

**Example**  

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
...
DynamoDBContext context =
       new DynamoDBContext(client, new DynamoDBContextConfig { ConsistentRead = true, SkipVersionCheck = true});
```

`DynamoDBContext` 包含这些可选参数以及您使用此上下文发送的每个请求。

不在 `DynamoDBContext` 级别设置这些参数，可以为使用 `DynamoDBContext` 运行的各个操作指定，如以下 C\$1 代码示例所示。此示例加载特定的图书项目。`DynamoDBContext` 的 `Load` 方法指定 `ConsistentRead` 和 `SkipVersionCheck` 可选参数。

**Example**  

```
AmazonDynamoDBClient client = new AmazonDynamoDBClient();
...
DynamoDBContext context = new DynamoDBContext(client);
Book bookItem = context.Load<Book>(productId,new DynamoDBContextConfig{ ConsistentRead = true, SkipVersionCheck = true });
```

在这种情况下，`DynamoDBContext` 仅在发送 `Get` 请求时包含这些参数。

# 将 DynamoDB 和 适用于 .NET 的 AWS SDK 对象持久化模型结合使用的乐观锁
<a name="DynamoDBContext.VersionSupport"></a>

对象持久化模型中的乐观锁定支持可确保应用程序的项目版本与服务器端的项目版本相同，然后再更新或删除项目。假设您检索要更新的项目。但是，在您发送更新之前，其他一些应用程序会更新相同的项目。现在，您的应用程序有该项目的过时副本。如果没有乐观锁定，您执行的任何更新都将覆盖其他应用程序所做的更新。

对象持久化模型的乐观锁定功能提供了`DynamoDBVersion`标签，您可以使用它来启用乐观锁定。要使用此功能，请向类添加一个属性以存储版本号。您可以添加`DynamoDBVersion`属性添加到属性。首次保存对象时，`DynamoDBContext` 分配一个版本号，并且在每次更新项目时递增版本号的值。

只有在客户端对象版本与服务器中对应的项目版本号相匹配时，您的更新或删除请求才会成功。如果您的应用程序有过时的副本，则必须从服务器获取最新版本，然后才能更新或删除该项目。

下面的 C\$1 代码示例定义了 `Book` 类，其中包含对象持久性属性将其映射到 `ProductCatalog` 表。有 `DynamoDBVersion` 属性的类的 `VersionNumber` 属性存储版本号值。

**Example**  

```
[DynamoDBTable("ProductCatalog")]
  public class Book
  {
    [DynamoDBHashKey]   //Partition key
    public int Id { get; set; }
    [DynamoDBProperty]
    public string Title { get; set; }
    [DynamoDBProperty]
    public string ISBN { get; set; }
    [DynamoDBProperty("Authors")]
    public List<string> BookAuthors { get; set; }
    [DynamoDBVersion]
    public int? VersionNumber { get; set; }
  }
```

**注意**  
您可以应用`DynamoDBVersion`属性仅设置为可为空的数字基本类型（例如`int?`)。

乐观锁对这些 `DynamoDBContext` 方法具有以下影响：
+ 对于新项目，`DynamoDBContext`分配初始版本号 0。如果您检索现有项目，然后更新它的一个或多个属性，并尝试保存所做更改，那么只有在客户端和服务器端的版本号匹配时保存操作才能成功，`DynamoDBContext` 递增版本号。您无需设置版本号。
+ `Delete` 方法提供重载，可以将主键值或对象作为参数，如以下 C\$1 代码示例所示。  
**Example**  

  ```
  DynamoDBContext context = new DynamoDBContext(client);
  ...
  // Load a book.
  Book book = context.Load<ProductCatalog>(111);
  // Do other operations.
  // Delete 1 - Pass in the book object.
  context.Delete<ProductCatalog>(book);
  
  // Delete 2 - Pass in the Id (primary key)
  context.Delete<ProductCatalog>(222);
  ```

  如果提供对象作为参数，则仅当对象版本与相应的服务器端项目版本匹配时，才会成功删除。但是，如果提供主键值作为参数，`DynamoDBContext`不知道任何版本号，它会删除项目而不进行版本检查。

  请注意，对象持久化模型代码中乐观锁定的内部实现使用 DynamoDB 中的条件更新和条件删除 API 操作。

## 禁用乐观锁
<a name="DotNetDynamoDBContext.DisablingOptimisticLocking"></a>

要禁用乐观锁定，请使用`SkipVersionCheck`配置属性。您可以在创建 `DynamoDBContext` 时设置此属性。在这种情况下，对于您使用上下文进行的任何请求，都会禁用乐观锁定。有关更多信息，请参阅 [指定 DynamoDBContext 的可选参数](DotNetDynamoDBContext.md#OptionalConfigParams)。

您不需要在上下文级别设置属性，而是为特定操作禁用乐观锁定，如以下 C\$1 代码示例所示。该示例使用上下文删除书籍项目。`Delete` 方法设置可选 `SkipVersionCheck` 属性设置为 true，禁用版本检查。

**Example**  

```
DynamoDBContext context = new DynamoDBContext(client);
// Load a book.
Book book = context.Load<ProductCatalog>(111);
...
// Delete the book.
context.Delete<Book>(book, new DynamoDBContextConfig { SkipVersionCheck = true });
```

# 通过使用 适用于 .NET 的 AWS SDK 对象持久化模型的 DynamoDB 映射任意数据
<a name="DynamoDBContext.ArbitraryDataMapping"></a>

除支持的 NET 类型（请参阅[支持的数据类型](DotNetSDKHighLevel.md#DotNetDynamoDBContext.SupportedTypes)）外，您还可以使用应用程序中不能直接映射到 Amazon DynamoDB 类型的类型。只要您提供转换器将数据从任意类型转换为 DynamoDB 类型，反之亦然，对象持久化模型支持存储任意类型的数据。转换器代码在保存和加载对象期间转换数据。

您可以在客户端创建任何类型。但是，表中存储的数据是 DynamoDB 类型之一，在查询和扫描期间，所做的任何数据比较都与 DynamoDB 中存储的数据进行。

下面的 C\$1 代码示例定义了具有 `Id`、`Title`、`ISBN` 和 `Dimension` 属性的 `Book` 类。`Dimension` 属性是描述 `Height`、`Width` 和 `Thickness` 属性的 `DimensionType`。示例代码提供了转换器方法 `ToEntry` 和 `FromEntry`，在 `DimensionType` 和 DynamoDB 字符串类型之间转换数据。例如，保存`Book`实例时，转换器会创建一本书 `Dimension` 字符串，例如“8.5x11x.05”。当您检索书籍时，它会将字符串转换为`DimensionType`实例。

该示例将`Book`类型映射到`ProductCatalog`表。它保存了一个样本`Book`实例，检索它，更新其维度，并保存更新后的`Book`。



有关测试以下示例的分步说明，请参阅 [.NET 代码示例](CodeSamples.DotNet.md)。

**Example**  

```
using System;
using System.Collections.Generic;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;
using Amazon.DynamoDBv2.DocumentModel;
using Amazon.Runtime;
using Amazon.SecurityToken;

namespace com.amazonaws.codesamples
{
    class HighLevelMappingArbitraryData
    {
        private static AmazonDynamoDBClient client = new AmazonDynamoDBClient();

        static void Main(string[] args)
        {
            try
            {
                DynamoDBContext context = new DynamoDBContext(client);

                // 1. Create a book.
                DimensionType myBookDimensions = new DimensionType()
                {
                    Length = 8M,
                    Height = 11M,
                    Thickness = 0.5M
                };

                Book myBook = new Book
                {
                    Id = 501,
                    Title = "AWS SDK for .NET Object Persistence Model Handling Arbitrary Data",
                    ISBN = "999-9999999999",
                    BookAuthors = new List<string> { "Author 1", "Author 2" },
                    Dimensions = myBookDimensions
                };

                context.Save(myBook);

                // 2. Retrieve the book.
                Book bookRetrieved = context.Load<Book>(501);

                // 3. Update property (book dimensions).
                bookRetrieved.Dimensions.Height += 1;
                bookRetrieved.Dimensions.Length += 1;
                bookRetrieved.Dimensions.Thickness += 0.2M;
                // Update the book.
                context.Save(bookRetrieved);

                Console.WriteLine("To continue, press Enter");
                Console.ReadLine();
            }
            catch (AmazonDynamoDBException e) { Console.WriteLine(e.Message); }
            catch (AmazonServiceException e) { Console.WriteLine(e.Message); }
            catch (Exception e) { Console.WriteLine(e.Message); }
        }
    }
    [DynamoDBTable("ProductCatalog")]
    public class Book
    {
        [DynamoDBHashKey] //Partition key
        public int Id
        {
            get; set;
        }
        [DynamoDBProperty]
        public string Title
        {
            get; set;
        }
        [DynamoDBProperty]
        public string ISBN
        {
            get; set;
        }
        // Multi-valued (set type) attribute.
        [DynamoDBProperty("Authors")]
        public List<string> BookAuthors
        {
            get; set;
        }
        // Arbitrary type, with a converter to map it to DynamoDB type.
        [DynamoDBProperty(typeof(DimensionTypeConverter))]
        public DimensionType Dimensions
        {
            get; set;
        }
    }

    public class DimensionType
    {
        public decimal Length
        {
            get; set;
        }
        public decimal Height
        {
            get; set;
        }
        public decimal Thickness
        {
            get; set;
        }
    }

    // Converts the complex type DimensionType to string and vice-versa.
    public class DimensionTypeConverter : IPropertyConverter
    {
        public DynamoDBEntry ToEntry(object value)
        {
            DimensionType bookDimensions = value as DimensionType;
            if (bookDimensions == null) throw new ArgumentOutOfRangeException();

            string data = string.Format("{1}{0}{2}{0}{3}", " x ",
                            bookDimensions.Length, bookDimensions.Height, bookDimensions.Thickness);

            DynamoDBEntry entry = new Primitive
            {
                Value = data
            };
            return entry;
        }

        public object FromEntry(DynamoDBEntry entry)
        {
            Primitive primitive = entry as Primitive;
            if (primitive == null || !(primitive.Value is String) || string.IsNullOrEmpty((string)primitive.Value))
                throw new ArgumentOutOfRangeException();

            string[] data = ((string)(primitive.Value)).Split(new string[] { " x " }, StringSplitOptions.None);
            if (data.Length != 3) throw new ArgumentOutOfRangeException();

            DimensionType complexData = new DimensionType
            {
                Length = Convert.ToDecimal(data[0]),
                Height = Convert.ToDecimal(data[1]),
                Thickness = Convert.ToDecimal(data[2])
            };
            return complexData;
        }
    }
}
```

# 运行本开发人员指南中的代码示例
<a name="CodeSamples"></a>

AWS SDK 以下列语言为 Amazon DynamoDB 提供了广泛的支持：
+ [Java](https://aws.amazon.com/sdk-for-java)：
+ [浏览器中的 JavaScript](https://aws.amazon.com/sdk-for-browser)
+ [.NET](https://aws.amazon.com/sdk-for-net)
+ [Node.js](https://aws.amazon.com/sdk-for-node-js)
+ [PHP](https://aws.amazon.com/sdk-for-php)
+ [Python](https://aws.amazon.com/sdk-for-python)
+ [Ruby](https://aws.amazon.com/sdk-for-ruby)
+ [C\$1：](https://aws.amazon.com/sdk-for-cpp)
+ [Go](https://aws.amazon.com/sdk-for-go)\$1
+ [Android](https://aws.amazon.com/mobile/sdk/)：
+ [iOS](https://aws.amazon.com/mobile/sdk/)：

本开发人员指南的代码示例更深入介绍使用以下编程语言的 DynamoDB 操作：
+ [Java 代码示例](CodeSamples.Java.md)
+ [.NET 代码示例](CodeSamples.DotNet.md)

在开始本练习之前，您需要创建 AWS 账户，获取访问密钥和私有密钥，然后在计算机上设置 AWS Command Line Interface (AWS CLI)。有关更多信息，请参阅 [设置 DynamoDB（Web 服务）](SettingUp.DynamoWebService.md)。

**注意**  
如果使用可下载版本的 DynamoDB，则需要使用 AWS CLI 创建表和样本数据。还需要指定每个 AWS CLI 命令的 `--endpoint-url` 参数。有关更多信息，请参阅 [设置本地端点](DynamoDBLocal.UsageNotes.md#DynamoDBLocal.Endpoint)。

# 为 DynamoDB 中的代码示例创建表和加载数据
<a name="SampleData"></a>

有关在 DynamoDB 中创建表、加载示例数据集、查询数据和更新数据的基础知识，请参阅下文。
+ [第 1 步：在 DynamoDB 中创建表](getting-started-step-1.md)
+ [第 2 步：将数据写入 DynamoDB 表](getting-started-step-2.md)
+ [第 3 步：从 DynamoDB 表中读取数据](getting-started-step-3.md)
+ [第 4 步：更新 DynamoDB 表中的数据](getting-started-step-4.md)

# Java 代码示例
<a name="CodeSamples.Java"></a>

**Topics**
+ [Java：设置 AWS 凭证](#CodeSamples.Java.Credentials)
+ [Java：设置 AWS 区域和端点](#CodeSamples.Java.RegionAndEndpoint)

本开发人员指南中包含 Java 代码片段以及可现成运行的程序。可以在以下章节中找到这些代码示例：
+ [使用 DynamoDB 中的项目和属性](WorkingWithItems.md)
+ [使用 DynamoDB 中的表和数据](WorkingWithTables.md)
+ [在 DynamoDB 中查询表](Query.md)
+ [在 DynamoDB 中扫描表](Scan.md)
+ [在 DynamoDB 中使用二级索引改进数据访问](SecondaryIndexes.md)
+ [Java 1.x：DynamoDBMapper](DynamoDBMapper.md)
+ [将更改数据捕获用于 DynamoDB Streams](Streams.md)

可以结合使用 Eclipse 和 [AWS Toolkit for Eclipse](https://aws.amazon.com/eclipse/) 来实现快速入门。除功能全面的 IDE 之外，还有带自动更新和预置模板的 适用于 Java 的 AWS SDK，用于构建 AWS 应用程序。

**运行 Java 代码示例（使用 Eclipse）**

1. 下载并安装 [Eclipse](http://www.eclipse.org) IDE。

1. 下载并安装 [AWS Toolkit for Eclipse](https://aws.amazon.com/eclipse/)。

1. 启动 Eclipse，然后在 **Eclipse** 菜单中，依次选择**文件**、**新建**和**其他**。

1. 在**选择向导**中，依次选择 **AWS**、**AWS Java 项目**和**下一步**。

1. 在**创建 AWS Java** 中，执行以下操作：

   1. 在**项目名称**中输入项目名称。

   1. 在**选择账户**中，从列表中选择凭证配置文件。

      如果这是您首次使用 [AWS Toolkit for Eclipse](https://aws.amazon.com/eclipse/)，请选择**配置 AWS 账户**以设置 AWS 凭证。

1. 选择**完成**创建项目。

1. 从 **Eclipse** 菜单中，依次选择**文件**、**新建**和**类**。

1. 在 **Java 类**的**名称**中输入类名（使用与要运行的代码示例相同的名称），然后选择**完成**以创建类。

1. 将文档页的代码示例复制到 Eclipse 编辑器。

1. 要运行代码，请在 Eclipse 菜单中选择**运行**。

SDK for Java提供线程安全的客户端来处理 DynamoDB。应用程序应创建一个客户端并在线程之间重复使用此客户端，您应将此作为一项最佳实践。

有关更多信息，请参见 [适用于 Java 的 AWS SDK](https://aws.amazon.com/sdk-for-java)。

**注意**  
本指南中的代码示例旨在用于最新版本的 适用于 Java 的 AWS SDK。  
如果使用 AWS Toolkit for Eclipse，则可以为 SDK for Java 配置自动更新。要在 Eclipse 中进行此操作，转到**首选项**，选择 **AWS Toolkit**、**适用于 Java 的 AWS SDK**、**自动下载新 SDK**。

## Java：设置 AWS 凭证
<a name="CodeSamples.Java.Credentials"></a>

SDK for Java 要求在运行时为应用程序提供 AWS 凭证。本指南中的代码示例假设您使用 AWS 凭证文件，如《适用于 Java 的 AWS SDK开发人员指南》中的[设置 AWS 凭证](https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/set-up-creds.html)所述。

下面是一个名为 `~/.aws/credentials` 的 AWS 凭证文件示例，其中波浪号字符 (`~`) 表示主目录。

```
[default]
aws_access_key_id = AWS access key ID goes here
aws_secret_access_key = Secret key goes here
```

## Java：设置 AWS 区域和端点
<a name="CodeSamples.Java.RegionAndEndpoint"></a>

代码示例默认访问美国西部（俄勒冈）区域的 DynamoDB。可以修改 `AmazonDynamoDB` 属性来更改区域。

下面的代码示例实例化一个新的 `AmazonDynamoDB`。

```
import software.amazon.dynamodb.AmazonDynamoDBClientBuilder;
import com.amazonaws.regions.Regions;
...
// This client will default to US West (Oregon)
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard()
.withRegion(Regions.US_WEST_2)
.build();
```

可以使用 `withRegion` 方法对任何区域的 DynamoDB 运行代码。有关完整列表，请参阅《Amazon Web Services 一般参考》中的 [AWS 区域和终端节点](https://docs.aws.amazon.com/general/latest/gr/rande.html#ddb_region)**。

如果要使用 DynamoDB在计算机本地运行代码示例，请按如下方式设置端点。

### AWS SDK V1
<a name="CodeSamples.Java.RegionAndEndpoint.V1"></a>

```
AmazonDynamoDB client = AmazonDynamoDBClientBuilder.standard().withEndpointConfiguration(
new AwsClientBuilder.EndpointConfiguration("http://localhost:8000", "us-west-2"))
.build();
```

### AWS SDK V2
<a name="CodeSamples.Java.RegionAndEndpoint.V2"></a>

```
DynamoDbClient client = DynamoDbClient.builder()
    .endpointOverride(URI.create("http://localhost:8000"))
    // The region is meaningless for local DynamoDb but required for client builder validation
    .region(Region.US_EAST_1)
    .credentialsProvider(StaticCredentialsProvider.create(
    AwsBasicCredentials.create("dummy-key", "dummy-secret")))
    .build();
```

# .NET 代码示例
<a name="CodeSamples.DotNet"></a>

**Topics**
+ [.NET：设置 AWS 凭证](#CodeSamples.DotNet.Credentials)
+ [.NET：设置 AWS 区域和端点](#CodeSamples.DotNet.RegionAndEndpoint)

本指南包含 .NET 代码片段以及可现成运行的程序。可以在以下章节中找到这些代码示例：
+ [使用 DynamoDB 中的项目和属性](WorkingWithItems.md)
+ [使用 DynamoDB 中的表和数据](WorkingWithTables.md)
+ [在 DynamoDB 中查询表](Query.md)
+ [在 DynamoDB 中扫描表](Scan.md)
+ [在 DynamoDB 中使用二级索引改进数据访问](SecondaryIndexes.md)
+ [在 DynamoDB 中使用 .NET 文档模型](DotNetSDKMidLevel.md)
+ [结合使用 .NET 对象持久化模型和 DynamoDB](DotNetSDKHighLevel.md)
+ [将更改数据捕获用于 DynamoDB Streams](Streams.md)

可以使用 适用于 .NET 的 AWS SDK 和 Toolkit for Visual Studio 快速入门。

**运行 .NET 代码示例（使用 Visual Studio）**

1. 下载并安装 [Microsoft Visual Studio](https://www.visualstudio.com)。

1. （可选）下载并安装 [Toolkit for Visual Studio](https://aws.amazon.com/visualstudio/)。

1. 设置您的 AWS 凭证。在您的共享 AWS 凭证文件 (`~/.aws/credentials`) 中配置凭证配置文件。有关更多信息，请参阅《适用于 .NET 的 AWS SDK 开发人员指南》**中的[配置 AWS 凭证](https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-creds.html)。

1. 启动 Visual Studio。依次选择**文件**、**新建**、**项目**。

1. 搜索**控制台应用程序**，选择以 .NET 为目标的 C\$1 模板，然后选择**下一步**。配置您的项目名称和位置，然后选择**创建**。

1. 将适用于 DynamoDB 的 AWS SDK NuGet 程序包添加到项目中：

   1. 在解决方案资源管理器中，打开项目的上下文（右键单击）菜单，然后选择**管理 NuGet 程序包**。

   1. 在 NuGet 程序包管理器中，选择**浏览**。

   1. 在搜索框中输入 **AWSSDK.DynamoDBv2**，等待搜索完成。

   1. 选择 **AWSSDK.DynamoDBv2**，然后选择**安装**。

1. 在 Visual Studio 项目中，打开 `Program.cs`。用要运行的文档页中的代码示例替换内容。

1. 要运行代码，请在 Visual Studio 工具栏中选择**开始**。

适用于 .NET 的 SDK 提供线程安全的客户端来处理 DynamoDB。应用程序应创建一个客户端并在线程之间重复使用此客户端，您应将此作为一项最佳实践。

有关更多信息，请参阅 [AWS SDK for .NET](https://aws.amazon.com/sdk-for-net)。

**注意**  
本指南中的代码示例旨在用于最新版本的 适用于 .NET 的 AWS SDK。

## .NET：设置 AWS 凭证
<a name="CodeSamples.DotNet.Credentials"></a>

适用于 .NET 的 SDK 要求在运行时向应用程序提供 AWS 凭证。本指南中的代码示例假定使用 SDK 存储来管理 AWS 凭证文件，如《适用于 .NET 的 AWS SDK 开发人员指南》**的[使用 SDK 存储](https://docs.aws.amazon.com/sdk-for-net/v3/developer-guide/net-dg-config-creds.html#sdk-store)所述。

Toolkit for Visual Studio 支持来自任意数量账户的多组凭证。每组凭证称为一个*配置文件*。Visual Studio 将条目添加到项目的 `App.config` 文件，这样应用程序可在运行时查找 AWS 凭证。

下面的示例显示使用 Toolkit for Visual Studio 创建新项目时生成的默认 `App.config` 文件。

```
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>
    <add key="AWSProfileName" value="default"/>
    <add key="AWSRegion" value="us-west-2" />
 </appSettings>
</configuration>
```

运行时，程序使用 `AWSProfileName` 条目所指定的 `default` 组 AWS 凭证。AWS 凭证以加密形式保存在 SDK Store 中。Toolkit for Visual Studio 提供一个图形用户界面，用于在 Visual Studio 中管理凭证。有关更多信息，请参阅《AWS Toolkit for Visual Studio 用户指南》**的[指定凭证](https://docs.aws.amazon.com/AWSToolkitVS/latest/UserGuide/tkv_setup.html#creds)。

**注意**  
代码示例默认访问美国西部（俄勒冈）区域的 DynamoDB。可以通过修改 App.config 文件的 `AWSRegion` 条目更改区域。可以将 `AWSRegion` 设置为 DynamoDB 可用的任何区域。有关完整列表，请参阅《Amazon Web Services 一般参考》中的 [AWS 区域和终端节点](https://docs.aws.amazon.com/general/latest/gr/rande.html#ddb_region)**。

## .NET：设置 AWS 区域和端点
<a name="CodeSamples.DotNet.RegionAndEndpoint"></a>

代码示例默认访问美国西部（俄勒冈）区域的 DynamoDB。可以修改 `App.config` 文件中 `AWSRegion` 条目更改区域。或者，可以修改 `AmazonDynamoDBClient` 属性更改区域。

下面的代码示例实例化一个新的 `AmazonDynamoDBClient`。修改客户端，对其他区域的 DynamoDB 运行该代码。

```
AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig();
// This client will access the US East 1 region.
clientConfig.RegionEndpoint = RegionEndpoint.USEast1;
AmazonDynamoDBClient client = new AmazonDynamoDBClient(clientConfig);
```

有关区域的完整列表，请参阅《Amazon Web Services 一般参考》中的 [AWS 区域和终端节点](https://docs.aws.amazon.com/general/latest/gr/rande.html#ddb_region)**。

如果要使用 DynamoDB在计算机本地运行代码示例，请按如下方式设置端点。

```
AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig();
// Set the endpoint URL
clientConfig.ServiceURL = "http://localhost:8000";
AmazonDynamoDBClient client = new AmazonDynamoDBClient(clientConfig);
```

# DynamoDB 低级 API
<a name="Programming.LowLevelAPI"></a>

Amazon DynamoDB *低级别 API* 是 DynamoDB 的协议级接口。在此级别，每个 HTTP(S) 请求的格式必须正确并带有有效的数字签名。

AWS SDK 代表您构建低级 DynamoDB API 请求并处理来自 DynamoDB 的响应。这可让您专注于应用程序逻辑而不是低级详细信息。不过，您仍可通过大致了解低级 DynamoDB API 的工作方式获益。

有关低级 DynamoDB API 的更多信息，请参阅[Amazon DynamoDB API 参考](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/)。

**注意**  
DynamoDB Streams 具有自己的低级别 API，此 API 独立于 DynamoDB 的低级别 API 且完全受 AWS SDK 支持。  
有关更多信息，请参阅 [将更改数据捕获用于 DynamoDB Streams](Streams.md)。有关低级 DynamoDB Streams API，请参阅[Amazon DynamoDB Streams API 参考](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Operations_Amazon_DynamoDB_Streams.html)。

低级 DynamoDB API 使用 JavaScript 对象表示法 (JSON) 作为通信协议格式。JSON 以层次结构的方式呈现数据，因此可同时传递数据值和数据结构。名称/值对以 `name:value` 格式定义。数据层次结构通过名称/值对的嵌套括号方式来定义。

DynamoDB 只将 JSON 用作传输协议而非存储格式。AWS SDK使用 JSON 将数据发送到 DynamoDB，DynamoDB 会通过 JSON 进行响应。DynamoDB 不会以 JSON 格式持久存储数据。

**注意**  
有关 JSON 的更多信息，请参阅 `JSON.org` 网站的[介绍 JSON](http://json.org)。

**Topics**
+ [请求格式](#Programming.LowLevelAPI.RequestFormat)
+ [响应格式](#Programming.LowLevelAPI.ResponseFormat)
+ [数据类型描述符](#Programming.LowLevelAPI.DataTypeDescriptors)
+ [数值数据](#Programming.LowLevelAPI.Numbers)
+ [二进制数据](#Programming.LowLevelAPI.Binary)

![\[DynamoDB 低级别 API 以及 AWS SDK 如何处理协议级别的请求和响应。\]](http://docs.aws.amazon.com/zh_cn/amazondynamodb/latest/developerguide/images/SDKSupport.DDBLowLevelAPI.png)


## 请求格式
<a name="Programming.LowLevelAPI.RequestFormat"></a>

DynamoDB 低级别 API 接受 HTTP(S) `POST` 请求作为输入。AWS SDK 为您构建这些请求。

假设您有一个名为 `Pets` 的表和一个包含 `AnimalType`（分区键）和 `Name`（排序键）的键架构。这两个属性的类型为 `string`。为了从 `Pets` 中检索项目，AWS SDK 构建了以下请求。

```
POST / HTTP/1.1
Host: dynamodb.<region>.<domain>;
Accept-Encoding: identity
Content-Length: <PayloadSizeBytes>
User-Agent: <UserAgentString>
Content-Type: application/x-amz-json-1.0
Authorization: AWS4-HMAC-SHA256 Credential=<Credential>, SignedHeaders=<Headers>, Signature=<Signature>
X-Amz-Date: <Date> 
X-Amz-Target: DynamoDB_20120810.GetItem

{
    "TableName": "Pets",
    "Key": {
        "AnimalType": {"S": "Dog"},
        "Name": {"S": "Fido"}
    }
}
```

请注意有关此请求的以下信息：
+ `Authorization` 标头包含 DynamoDB 验证请求所需的信息。有关更多信息，请参阅《Amazon Web Services 一般参考》中的[签署 AWS API 请求](https://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html)和 [签名版本 4 签名流程](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html)**。
+ `X-Amz-Target` 标头包含 DynamoDB 操作的名称：`GetItem`。(这也是低级 API 版本附带的，此示例中为 `20120810`。)
+ 请求的负载 (正文) 包含 JSON 格式的操作参数。对于 `GetItem` 操作，参数为 `TableName` 和 `Key`。

## 响应格式
<a name="Programming.LowLevelAPI.ResponseFormat"></a>

收到请求后，DynamoDB 将处理请求并返回响应。对于前面显示的请求，HTTP(S) 响应负载包含来自操作的结果，如以下示例所示。

```
HTTP/1.1 200 OK
x-amzn-RequestId: <RequestId>
x-amz-crc32: <Checksum>
Content-Type: application/x-amz-json-1.0
Content-Length: <PayloadSizeBytes>
Date: <Date>
{
    "Item": {
        "Age": {"N": "8"},
        "Colors": {
            "L": [
                {"S": "White"},
                {"S": "Brown"},
                {"S": "Black"}
            ]
        },
        "Name": {"S": "Fido"},
        "Vaccinations": {
            "M": {
                "Rabies": {
                    "L": [
                        {"S": "2009-03-17"},
                        {"S": "2011-09-21"},
                        {"S": "2014-07-08"}
                    ]
                },
                "Distemper": {"S": "2015-10-13"}
            }
        },
        "Breed": {"S": "Beagle"},
        "AnimalType": {"S": "Dog"}
    }
}
```

此时，AWS SDK 会将响应数据返回给应用程序以供进一步的处理。

**注意**  
如果 DynamoDB 无法处理请求，它会返回 HTTP 错误代码和消息。AWS SDK 会以异常形式将这些错误代码和消息传播到应用程序。有关更多信息，请参阅 [DynamoDB 错误处理](Programming.Errors.md)。

## 数据类型描述符
<a name="Programming.LowLevelAPI.DataTypeDescriptors"></a>

低级 DynamoDB API 协议要求每个属性均带有数据类型描述符。*数据类型描述符*是告知 DynamoDB 如何解释每个属性的令牌。

[请求格式](#Programming.LowLevelAPI.RequestFormat)和[响应格式](#Programming.LowLevelAPI.ResponseFormat)中的示例说明了如何使用数据类型描述符。`GetItem` 请求为 `S` 类型的 `Pets` 键架构属性（`AnimalType` 和 `Name`）指定 `string`。`GetItem` 响应包含一个 *Pets* 项目，该项目带有 `string` (`S`)、`number` (`N`)、`map` (`M`) 和 `list` (`L`) 类型的属性。

以下是 DynamoDB 数据类型描述符的完整列表：
+ **`S`** – String
+ **`N`** – Number
+ **`B`** – Binary
+ **`BOOL`** – Boolean
+ **`NULL`** – Null
+ **`M`** – Map
+ **`L`** – List
+ **`SS`** – String Set
+ **`NS`** – Number Set
+ **`BS`** – Binary Set

下表展示了每个数据类型描述符的正确 JSON 格式。请注意，为了保持精度，数字以字符串表示，而布尔值和 null 则使用其原生 JSON 类型。


| 描述符 | JSON 格式 | 备注 | 
| --- | --- | --- | 
| S | \$1"S": "Hello"\$1 | 值是 JSON 字符串。 | 
| N | \$1"N": "123.45"\$1 | 值是字符串，而不是 JSON 数字。这样可以在不同的语言中保持精度。 | 
| B | \$1"B": "dGhpcyBpcyBhIHRlc3Q="\$1 | 值是 base64 编码的字符串。 | 
| BOOL | \$1"BOOL": true\$1 | 值是 JSON 布尔值（true 或 false），而不是字符串。 | 
| NULL | \$1"NULL": true\$1 | 值是 JSON 布尔值 true，表示 null。 | 
| M | \$1"M": \$1"Name": \$1"S": "Joe"\$1\$1\$1 | 值是采用属性名称/值对形式的 JSON 对象。 | 
| L | \$1"L": [\$1"S": "Red"\$1, \$1"N": "5"\$1]\$1 | 值是属性值类型的 JSON 数组。 | 
| SS | \$1"SS": ["Red", "Blue"]\$1 | 值是字符串类型的 JSON 数组。 | 
| NS | \$1"NS": ["1", "2.5"]\$1 | 值是数字字符串类型的 JSON 数组。 | 
| BS | \$1"BS": ["U3Vubnk=", "UmFpbnk="]\$1 | 值是 base64 编码字符串类型的 JSON 数组。 | 

**注意**  
 有关 DynamoDB 数据类型的详细描述，请参阅[数据类型](HowItWorks.NamingRulesDataTypes.md#HowItWorks.DataTypes)。

## 数值数据
<a name="Programming.LowLevelAPI.Numbers"></a>

不同的编程语言提供对 JSON 的不同级别的支持。在某些情况下，您可能会决定使用第三方库来验证和分析 JSON 文档。

一些第三方库基于 JSON 数字类型而构建，并提供它们自己的类型，例如 `int`、`long` 或 `double`。但是，DynamoDB 中的原生数字数据类型不能精确映射到其他数据类型，因此这些类型区别会造成冲突。此外，很多 JSON 库不能处理固定精度的数字，它们会将包含小数点的数字序列自动推断为双精度数据类型。

为了解决这些问题，DynamoDB 提供了不会造成数据丢失的单一数字类型。为了避免不必要的双精度值隐式转化，DynamoDB 将字符串用于数字值的数据传输。此方法可以灵活地更新属性值，同时能够保证排序语义的正确性，例如将“01”、“2”和“03”值按适当的顺序排列。

如果数字精度对您的应用程序十分重要，您应该先将数字值转换为字符串，然后再将它们传递到 DynamoDB。

## 二进制数据
<a name="Programming.LowLevelAPI.Binary"></a>

DynamoDB 支持二进制属性。但是，JSON 不支持在本地编码二进制数据。要在请求中发送二进制数据，您需要将其编码为 Base64 格式。收到请求后，DynamoDB 会将 Base64 数据解码回二进制数据。

DynamoDB 使用的 base64 编码方案在 Internet Engineering Task Force (IETF) 网站的 [RFC 4648](http://tools.ietf.org/html/rfc4648) 介绍。

# 使用 Python 和 Boto3 对 Amazon DynamoDB 进行编程
<a name="programming-with-python"></a>

本指南为想要结合 Python 使用 Amazon DynamoDB 的程序员提供了指导。了解不同的抽象层、配置管理、错误处理、控制重试策略、管理 keep-alive 等。

**Topics**
+ [Boto 简介](#programming-with-python-about)
+ [使用 Boto 文档](#programming-with-python-documentation)
+ [了解客户端和资源抽象层](#programming-with-python-client-resource)
+ [使用表资源 batch\$1writer](#programming-with-python-batch-writer)
+ [探索客户端和资源层的其它代码示例](#programming-with-python-additional-code)
+ [了解客户端和资源对象如何与会话和线程交互](#programming-with-python-sessions-thread-safety)
+ [自定义配置对象](#programming-with-python-config)
+ [错误处理](#programming-with-python-error-handling)
+ [日志记录](#programming-with-python-logging)
+ [事件钩子](#programming-with-python-event-hooks)
+ [分页和分页工具](#programming-with-python-pagination)
+ [Waiter](#programming-with-python-waiters)

## Boto 简介
<a name="programming-with-python-about"></a>

您可以使用官方适用于 Python 的 AWS SDK（通常称为 **Boto3**）从 Python 访问 DynamoDB。Boto 这个名字（发音为 boh-toh）来自一种原产于亚马逊河的淡水海豚。Boto3 库是该库的第三个主要版本，于 2015 年首次发布。Boto3 库非常大，因为它支持所有 AWS 服务，而不仅仅是 DynamoDB。此指导仅针对 Boto3 中与 DynamoDB 相关的部分。

Boto 由 AWS 作为 GitHub 上托管的开源项目进行维护和发布。它分为两个软件包：[Botocore](https://github.com/boto/botocore) 和 [Boto3](https://github.com/boto/boto3)。
+ **Botocore** 提供了低级别功能。在 Botocore 中，您将找到客户端、会话、凭证、配置和异常类。
+ **Boto3** 基于 Botocore 构建。它提供了更高级别、更具 Python 风格的接口。具体而言，它将 DynamoDB 表作为资源公开，与较低级别的面向服务的客户端接口相比，它提供了更简单、更优雅的接口。

由于这些项目托管在 GitHub 上，因此您可以查看源代码、跟踪未解决的问题或提交自己的问题。

## 使用 Boto 文档
<a name="programming-with-python-documentation"></a>

通过以下资源开始学习 Boto 文档：
+ 从[快速入门部分](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html)开始，该部分为软件包安装提供了可靠的起点。如果尚未安装 Boto3，请前往那里获取有关安装 Boto3 的说明（Boto3 通常会在诸如 AWS Lambda 的 AWS 服务中自动提供）。
+ 之后，请关注文档的 [DynamoDB 指南](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/dynamodb.html)。它向您展示了如何执行基本的 DynamoDB 活动：创建和删除表、操作项目、运行批量操作、运行查询和执行扫描。其示例使用**资源**接口。如果看到 `boto3.resource('dynamodb')`，表明您正在使用较高级别的**资源**接口。
+ 阅读完指南后，您可以查看 [DynamoDB 参考](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html)。此登录页面提供了可供您使用的类和方法的详尽列表。在顶部，您将看到 `DynamoDB.Client` 类。这提供了对所有控制面板和数据面板操作的低级别访问。在底部，请看 `DynamoDB.ServiceResource` 类。这是较高级别的 Python 风格的接口。使用它，您可以创建表、对表执行批量操作或获取表特定操作的 `DynamoDB.ServiceResource.Table` 实例。

## 了解客户端和资源抽象层
<a name="programming-with-python-client-resource"></a>

您将使用的两个接口是**客户端**接口和**资源**接口。
+ 低级别**客户端**接口提供与底层服务 API 的一对一映射。DynamoDB 提供的每个 API 都可通过客户端获得。这意味着客户端接口可以提供完整的功能，但使用起来往往更加冗长且复杂。
+ 更高级别的**资源**接口不提供底层服务 API 的一对一映射。但是，它提供了一些方法，让您能够更方便地访问 `batch_writer` 等服务。

以下是使用客户端接口插入项目的示例。请注意所有值是如何以映射形式传递的，键表示它们的类型（“S”代表字符串，“N”代表数字），它们的值作为字符串。这被称为 DynamoDB JSON 格式。

```
import boto3

dynamodb = boto3.client('dynamodb')

dynamodb.put_item(
    TableName='YourTableName',
    Item={
        'pk': {'S': 'id#1'},
        'sk': {'S': 'cart#123'},
        'name': {'S': 'SomeName'},
        'inventory': {'N': '500'},
        # ... more attributes ...
    }
)
```

以下是使用资源接口的相同 `PutItem` 操作。数据输入是隐式的：

```
import boto3

dynamodb = boto3.resource('dynamodb')

table = dynamodb.Table('YourTableName')

table.put_item(
    Item={
        'pk': 'id#1',
        'sk': 'cart#123',
        'name': 'SomeName',
        'inventory': 500,
        # ... more attributes ...
    }
)
```

如果需要，您可以使用随 boto3 提供的 `TypeSerializer` 和 `TypeDeserializer` 类在常规 JSON 和 DynamoDB JSON 之间进行转换：

```
def dynamo_to_python(dynamo_object: dict) -> dict:
    deserializer = TypeDeserializer()
    return {
        k: deserializer.deserialize(v) 
        for k, v in dynamo_object.items()
    }  
  
def python_to_dynamo(python_object: dict) -> dict:
    serializer = TypeSerializer()
    return {
        k: serializer.serialize(v)
        for k, v in python_object.items()
    }
```

下面展示了如何使用客户端接口执行查询。它将查询表示为 JSON 构造。它使用 `KeyConditionExpression` 字符串，该字符串需要变量替换来处理任何潜在的关键字冲突：

```
import boto3

client = boto3.client('dynamodb')

# Construct the query
response = client.query(
    TableName='YourTableName',
    KeyConditionExpression='pk = :pk_val AND begins_with(sk, :sk_val)',
    FilterExpression='#name = :name_val',
    ExpressionAttributeValues={
        ':pk_val': {'S': 'id#1'},
        ':sk_val': {'S': 'cart#'},
        ':name_val': {'S': 'SomeName'},
    },
    ExpressionAttributeNames={
        '#name': 'name',
    }
)
```

使用资源接口的相同查询操作可以缩短和简化：

```
import boto3
from boto3.dynamodb.conditions import Key, Attr

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('YourTableName')

response = table.query(
    KeyConditionExpression=Key('pk').eq('id#1') & Key('sk').begins_with('cart#'),
    FilterExpression=Attr('name').eq('SomeName')
)
```

最后一个例子，假设您想得到一个表的大致大小（这是保存在表中的元数据，大约每 6 小时更新一次）。使用客户端接口，您可以执行 `describe_table()` 操作并从返回的 JSON 结构中提取答案：

```
import boto3

dynamodb = boto3.client('dynamodb')

response = dynamodb.describe_table(TableName='YourTableName')
size = response['Table']['TableSizeBytes']
```

通过资源接口，表隐式执行描述操作，并将数据直接作为属性呈现：

```
import boto3

dynamodb = boto3.resource('dynamodb')

table = dynamodb.Table('YourTableName')
size = table.table_size_bytes
```

**注意**  
在考虑使用客户端接口还是资源接口进行开发时，请注意，根据以下[资源文档](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/resources.html)，不会向资源接口添加新特征：“AWS Python SDK 团队不打算向 boto3 的资源接口中添加新特征。现有接口将在 boto3 的生命周期内继续使用。客户可以通过客户端接口访问更新的服务特征。”

## 使用表资源 batch\$1writer
<a name="programming-with-python-batch-writer"></a>

只有较高级别的表资源才有的一种便利就是 `batch_writer`。DynamoDB 支持批量写入操作，允许在一个网络请求中执行多达 25 个放置或删除操作。像这样的批处理可以最大限度地减少网络往返行程，从而提高效率。

使用低级别客户端库，您可以使用 `client.batch_write_item()` 操作来运行批处理。您必须手动将工作分成 25 个批次。每次操作后，您还必须请求接收未处理的项目列表（有些写入操作可能成功，而有些可能失败）。然后，您必须将这些未处理的项目再次传递到以后的 `batch_write_item()` 操作中。有大量的样板代码。

[Table.batch\$1writer](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/table/batch_writer.html) 方法创建了一个上下文管理器，用于批量写入对象。它提供了一个接口，在这个接口中，您好像是一次写入一个项目，但在内部，它是缓冲并批量发送项目。此外，它还会隐式处理未处理的项目重试。

```
dynamodb = boto3.resource('dynamodb')

table = dynamodb.Table('YourTableName')

movies = # long list of movies in {'pk': 'val', 'sk': 'val', etc} format
with table.batch_writer() as writer:
    for movie in movies:
        writer.put_item(Item=movie)
```

## 探索客户端和资源层的其它代码示例
<a name="programming-with-python-additional-code"></a>

您还可以参考以下代码示例存储库，这些存储库使用客户端和资源探索各种函数的用法：
+ [官方 AWS 单一操作代码示例。](https://docs.aws.amazon.com/code-library/latest/ug/python_3_dynamodb_code_examples.html)
+ [官方 AWS 面向场景的代码示例。](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/python)
+ [社区维护的单一操作代码示例。](https://github.com/aws-samples/aws-dynamodb-examples/tree/master/examples/SDK/python)

## 了解客户端和资源对象如何与会话和线程交互
<a name="programming-with-python-sessions-thread-safety"></a>

资源对象并非线程安全的，不应跨线程或进程共享。有关更多详细信息，请参阅[资源指南](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/resources.html#multithreading-or-multiprocessing-with-resources)。

相比之下，客户端对象通常是线程安全的，但特定的高级特征除外。有关更多详细信息，请参阅[客户端指南](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/clients.html#multithreading-or-multiprocessing-with-clients)。

会话对象不是线程安全的。因此，每次在多线程环境中创建客户端或资源时，都应先创建一个新会话，然后从会话中创建客户端或资源。有关更多详细信息，请参阅[会话指南](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/session.html#multithreading-or-multiprocessing-with-sessions)。

如果调用 `boto3.resource()`，则隐式使用默认会话。这便于编写单线程代码。在编写多线程代码时，您需要先为每个线程构造一个新的会话，然后从该会话中检索资源：

```
# Explicitly create a new Session for this thread 
session = boto3.Session()
dynamodb = session.resource('dynamodb')
```

## 自定义配置对象
<a name="programming-with-python-config"></a>

在构造客户端或资源对象时，您可以传递可选的命名参数来自定义行为。名为 `config` 的参数可以解锁各种功能。它是 `botocore.client.Config` 的一个实例，[配置参考文档](https://botocore.amazonaws.com/v1/documentation/api/latest/reference/config.html)展示了它公开供您控制的所有内容。[配置指南](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html)提供了一个很好的概述。

**注意**  
您可以在 AWS 配置文件内的会话级别，或作为环境变量修改其中的许多行为设置。

**超时配置**

自定义配置的一个用途是调整网络行为：
+ **connect\$1timeout（浮点或整数）**– 尝试建立连接时引发超时异常之前的时间（以秒为单位）。默认值为 60 秒。
+ **read\$1timeout（浮点或整数）**– 尝试从连接中读取时引发超时异常之前的时间（以秒为单位）。默认值为 60 秒。

对于 DynamoDB 来说，60 秒的超时时间过长。这意味着暂时性网络故障会导致客户端延迟一分钟，然后才能重试。以下代码将超时时间缩短到一秒：

```
import boto3
from botocore.config import Config

my_config = Config(
   connect_timeout = 1.0,
   read_timeout = 1.0
)
dynamodb = boto3.resource('dynamodb', config=my_config)
```

有关超时的更多讨论，请参阅[调整延迟感知型 DynamoDB 应用程序的 AWS Java SDK HTTP 请求设置](https://aws.amazon.com/blogs/database/tuning-aws-java-sdk-http-request-settings-for-latency-aware-amazon-dynamodb-applications/)。注意，Java SDK 的超时配置比 Python 多。

**keep-alive 配置**

如果您使用的是 botocore 1.27.84 或更高版本，您也可以控制 **TCP Keep-Alive**：
+ **tcp\$1keepalive**（布尔值）- 如果设置为 `True`（默认为 `False`），则启用创建新连接时使用的 TCP Keep-Alive 套接字选项。这仅支持 botocore 1.27.84 及更高版本。

将 TCP Keep-Alive 设置为 `True` 可以减少平均延迟。以下是当您拥有合适的 botocore 版本时，有条件地将 TCP Keep-Alive 设置为 true 的示例代码：

```
import botocore
import boto3
from botocore.config import Config
from distutils.version import LooseVersion

required_version = "1.27.84"
current_version = botocore.__version__

my_config = Config(
   connect_timeout = 0.5,
   read_timeout = 0.5
)
if LooseVersion(current_version) > LooseVersion(required_version):
    my_config = my_config.merge(Config(tcp_keepalive = True))

dynamodb = boto3.resource('dynamodb', config=my_config)
```

**注意**  
TCP Keep-Alive 与 HTTP Keep-Alive 不同。使用 TCP Keep-Alive，底层操作系统通过套接字连接发送小数据包，以保持连接处于活动状态并立即检测到任何丢包。使用 HTTP Keep-Alive，在底层套接字上构建的 Web 连接可以重复使用。使用 boto3 时，HTTP Keep-Alive 始终处于启用状态。

空闲连接可以保持活动状态的时间是有限制的。如果您有一个空闲的连接，但希望下次请求使用已经建立的连接，可以考虑定期发送请求（比如每分钟发送一次）。

**重试配置**

该配置还接受名为 **retries** 的字典，您可以在其中指定所需的重试行为。当 SDK 收到错误且错误属于临时类型时，SDK 中会发生重试。如果在内部重试错误（并且重试最终生成成功响应），则从调用代码的角度来看，没有错误，只是延迟略有增加。以下是您可以指定的值：
+ **max\$1attempts** – 一个整数，表示单个请求将进行的最大重试次数。例如，将此值设置为 2 将导致请求在初始请求之后最多重试两次。将此值设置为 0 将导致在初始请求之后不会尝试任何重试。
+ **total\$1max\$1attempts** – 一个整数，表示单个请求将进行的最大总尝试次数。这包括初始请求，因此值为 1 表示不会重试任何请求。如果同时提供 `total_max_attempts` 和 `max_attempts`，则 `total_max_attempts` 优先。`total_max_attempts` 之所以优先于 `max_attempts`，是因为它映射到 `AWS_MAX_ATTEMPTS` 环境变量和 `max_attempts` 配置文件值。
+ **mode** – 一个字符串，表示 botocore 应使用的重试模式类型。有效值为：
  + **legacy** - 默认模式。首次重试等待 50 毫秒，然后使用基准因子为 2 的指数回退。对于 DynamoDB，除非使用上述值覆盖，否则它最多总共可执行 10 次尝试。
**注意**  
使用指数回退，最后一次尝试将等待几乎 13 秒。
  + **standard** – 之所以命名为标准版，是因为它与其它 AWS SDK 更加一致。对于首次重试，随机等待 0 毫秒到 1000 毫秒不等。如果需要再次重试，它会从 0 毫秒到 1000 毫秒之间随机选择另一个时间，然后将其乘以 2。如果需要更多重试，它会进行相同的随机选择，然后乘以 4，依此类推。每次等待的上限为 20 秒。与 `legacy` 模式相比，此模式将对更多检测到的故障条件执行重试。对于 DynamoDB，除非使用上述值覆盖，否则它最多总共可执行 3 次尝试。
  + **adaptive** - 一种实验性重试模式，包括标准模式的所有功能，但添加了自动客户端限制。通过自适应速率限制，SDK 可以降低请求的发送速率，以便更好地容纳 AWS 服务的容量。这是一种临时模式，其行为可能会发生变化。

这些重试模式的扩展定义可以在[重试指南](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/retries.html)中找到，也可以在 [SDK 参考的“重试行为”主题](https://docs.aws.amazon.com/sdkref/latest/guide/feature-retry-behavior.html)中找到。

以下示例明确使用 `legacy` 重试策略，请求总数最多为 3 次（2 次重试）：

```
import boto3
from botocore.config import Config

my_config = Config(
   connect_timeout = 1.0,
   read_timeout = 1.0,
   retries = {
     'mode': 'legacy',
     'total_max_attempts': 3
   }
)
dynamodb = boto3.resource('dynamodb', config=my_config)
```

由于 DynamoDB 是一个高可用、低延迟的系统，因此您可能希望在重试速度上比内置重试策略所允许的更激进。您可以实现自己的重试策略，方法是将最大尝试次数设置为 0，自己捕获异常，然后根据自己的代码酌情重试，而不是依赖 boto3 进行隐式重试。

如果您管理自己的重试策略，则需要区分节流和错误：
+ **节流**（由 `ProvisionedThroughputExceededException` 或 `ThrottlingException` 表示）表示服务运行正常，它会通知您已超出 DynamoDB 表或分区的读取或写入容量。每过一毫秒，就会有多一点的读取或写入容量可用，因此您可以快速重试（例如每 50 毫秒重试一次），尝试访问新释放的容量。使用节流，您并不特别需要指数回退，因为节流属于轻量级，DynamoDB 可以返回，而且不会向您收取每次请求的费用。指数回退会将更长的延迟分配给已经等待最长时间的客户端线程，从统计学上讲，将超越 p50 和 p99。
+ **错误**（由 `InternalServerError` 或 `ServiceUnavailable` 等表示）表示服务存在暂时性问题。这可以是针对整个表，也可以只是您正在读取或写入的分区。使用错误，您可以在重试前暂停更长时间（例如 250 毫秒或 500 毫秒），并使用抖动来错开重试。

**最大池连接数配置**

最后，配置允许您控制连接池大小：
+ **max\$1pool\$1connections (int)** – 连接池中要保留的最大连接数。如果未指定值，则使用默认值 10。

此选项控制要保留在池中以供重复使用的最大 HTTP 连接数。每个会话保留一个不同的池。如果您预计会有超过 10 个线程使用基于同一会话构建的客户端或资源，则应考虑提高该值，这样线程就不必等待使用池连接的其它线程。

```
import boto3
from botocore.config import Config

my_config = Config(
   max_pool_connections = 20
)

# Setup a single session holding up to 20 pooled connections
session = boto3.Session(my_config)

# Create up to 20 resources against that session for handing to threads
# Notice the single-threaded access to the Session and each Resource
resource1 = session.resource('dynamodb')
resource2 = session.resource('dynamodb')
# etc
```

## 错误处理
<a name="programming-with-python-error-handling"></a>

Boto3 中并未全部静态定义 AWS 服务异常。这是因为 AWS 服务的错误和异常差异很大，并且可能会发生变化。Boto3 将所有服务异常封装为 `ClientError`，并以结构化 JSON 公开详细信息。例如，错误响应的结构可能如下所示：

```
{
    'Error': {
        'Code': 'SomeServiceException',
        'Message': 'Details/context around the exception or error'
    },
    'ResponseMetadata': {
        'RequestId': '1234567890ABCDEF',
        'HostId': 'host ID data will appear here as a hash',
        'HTTPStatusCode': 400,
        'HTTPHeaders': {'header metadata key/values will appear here'},
        'RetryAttempts': 0
    }
}
```

以下代码会捕获任何 `ClientError` 异常，并查看 `Error` 中 `Code` 的字符串值来确定要采取的操作：

```
import botocore
import boto3

dynamodb = boto3.client('dynamodb')

try:
    response = dynamodb.put_item(...)

except botocore.exceptions.ClientError as err:
    print('Error Code: {}'.format(err.response['Error']['Code']))
    print('Error Message: {}'.format(err.response['Error']['Message']))
    print('Http Code: {}'.format(err.response['ResponseMetadata']['HTTPStatusCode']))
    print('Request ID: {}'.format(err.response['ResponseMetadata']['RequestId']))

    if err.response['Error']['Code'] in ('ProvisionedThroughputExceededException', 'ThrottlingException'):
        print("Received a throttle")
    elif err.response['Error']['Code'] == 'InternalServerError':
        print("Received a server error")
    else:
        raise err
```

一些（但不是全部）异常代码已被具体化为顶级类。您可以选择直接处理这些类。使用客户端接口时，这些异常会在您的客户端上动态填充，您可以使用客户端实例捕获这些异常，如下所示：

```
except ddb_client.exceptions.ProvisionedThroughputExceededException:
```

使用资源接口时，必须使用 `.meta.client` 从资源遍历到底层客户端才能访问异常，如下所示：

```
except ddb_resource.meta.client.exceptions.ProvisionedThroughputExceededException:
```

要查看具体化异常类型的列表，可以动态生成列表：

```
ddb = boto3.client("dynamodb")
print([e for e in dir(ddb.exceptions) if e.endswith('Exception') or e.endswith('Error')])
```

使用条件表达式执行写入操作时，您可以请求：如果表达式失败，则应在错误响应中返回该项目的值。

```
try:
    response = table.put_item(
        Item=item,
        ConditionExpression='attribute_not_exists(pk)',
        ReturnValuesOnConditionCheckFailure='ALL_OLD'
    )
except table.meta.client.exceptions.ConditionalCheckFailedException as e:
    print('Item already exists:', e.response['Item'])
```

要进一步了解错误处理和异常，请执行以下操作：
+ [boto3 错误处理指南](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/error-handling.html)提供了有关错误处理技巧的更多信息。
+ [DynamoDB 开发人员指南中关于编程错误的部分](Programming.Errors.md)列出了您可能遇到的错误。
+ [API 参考中的常见错误部分](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/CommonErrors.html)。
+ 每个 API 操作的文档都列出了调用可能产生的错误（例如 [BatchWriteItem](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_BatchWriteItem.html)）。

## 日志记录
<a name="programming-with-python-logging"></a>

Boto3 库与 Python 的内置日志记录模块集成，用于跟踪会话期间发生的情况。要控制日志记录级别，可以配置日志记录模块：

```
import logging

logging.basicConfig(level=logging.INFO)
```

这会将根记录器配置为记录 `INFO` 及更高级别的消息。严重程度低于这些级别的日志消息将被忽略。日志记录级别包括 `DEBUG`、`INFO`、`WARNING`、`ERROR` 和 `CRITICAL`。默认值为 `WARNING`。

Boto3 中的记录器是分层的。该库使用几个不同的记录器，每个记录器对应库的不同部分。您可以分别控制每个记录器的行为：
+ **boto3**：boto3 模块的主记录器。
+ **botocore**：botocore 软件包的主记录器。
+ **botocore.auth**：用于记录请求的 AWS 签名创建过程。
+ **botocore.credentials**：用于记录凭证获取和刷新的过程。
+ **botocore.endpoint**：用于在请求通过网络发送之前记录请求的创建过程。
+ **botocore.hooks**：用于记录库中触发的事件。
+ **botocore.loaders**：用于记录加载部分 AWS 服务模型的情况。
+ **botocore.parsers**：用于在解析 AWS 服务响应之前对其进行记录。
+ **botocore.retryhandler**：用于记录 AWS 服务请求重试的处理情况（传统模式）。
+ **botocore.retries.standard**：用于记录 AWS 服务请求重试的处理情况（标准模式或自适应模式）。
+ **botocore.utils**：用于记录库中的其它活动。
+ **botocore.waiter**：用于记录 Waiter 的功能，后者轮询 AWS 服务直到达到特定状态。

其它库也记录。在内部，boto3 使用第三方 urllib3 进行 HTTP 连接处理。如果延迟很重要，您可以通过查看 urllib3 何时建立新连接或关闭空闲连接来查看其日志，以确保您的池得到充分利用。
+ **urllib3.connectionpool**：用于记录连接池处理事件。

以下代码段将端点和连接池活动的大部分记录设置为 `INFO` 和 `DEBUG`：

```
import logging

logging.getLogger('boto3').setLevel(logging.INFO)
logging.getLogger('botocore').setLevel(logging.INFO)
logging.getLogger('botocore.endpoint').setLevel(logging.DEBUG)
logging.getLogger('urllib3.connectionpool').setLevel(logging.DEBUG)
```

## 事件钩子
<a name="programming-with-python-event-hooks"></a>

Botocore 在其执行的各个阶段都会发出事件。您可以为这些事件注册处理程序，这样无论何时发出事件，您的处理程序都会被调用。这使您无需修改内部结构即可扩展 botocore 的行为。

例如，假设您要跟踪应用程序中的任何 DynamoDB 表上每次调用 `PutItem` 操作的情况。每次在关联的会话上调用 `PutItem` 操作时，您都可以在 `'provide-client-params.dynamodb.PutItem'` 事件上注册以捕获并记录。示例如下：

```
import boto3
import botocore
import logging

def log_put_params(params, **kwargs):
    if 'TableName' in params and 'Item' in params:
        logging.info(f"PutItem on table {params['TableName']}: {params['Item']}")

logging.basicConfig(level=logging.INFO)

session = boto3.Session()
event_system = session.events

# Register our interest in hooking in when the parameters are provided to PutItem
event_system.register('provide-client-params.dynamodb.PutItem', log_put_params)

# Now, every time you use this session to put an item in DynamoDB,
# it will log the table name and item data.
dynamodb = session.resource('dynamodb')
table = dynamodb.Table('YourTableName')
table.put_item(
    Item={
        'pk': '123',
        'sk': 'cart#123',
        'item_data': 'YourItemData',
        # ... more attributes ...
    }
)
```

在处理程序中，您甚至可以通过编程方式操作参数来改变行为：

```
params['TableName'] = "NewTableName"
```

有关事件的更多信息，请参阅[事件的 botocore 文档](https://botocore.amazonaws.com/v1/documentation/api/latest/topics/events.html)和[事件的 boto3 文档](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/events.html)。

## 分页和分页工具
<a name="programming-with-python-pagination"></a>

某些请求（例如查询和扫描）会限制针对单个请求返回的数据大小，并要求您重复请求才能显示后续页面。

您可以使用 `limit` 参数控制每页可读取的最大项目数。例如，如果您想要读取最后 10 个项目，则可以使用 `limit` 仅检索最后 10 个项目。请注意，限制是在应用任何筛选条件之前应从表中读取多少项目。筛选后无法确切指定想要检索 10 个项目；只有在切实检索到 10 个项目后，您才能控制预先筛选的数量并检查客户端。不管限制如何，每个响应始终有 1 MB 的最大大小。

如果响应包含 `LastEvaluatedKey`，则表示响应已结束，因为它达到了计数或大小限制。此密钥是针对该响应评估的最后一个密钥。您可以检索此 `LastEvaluatedKey` 并将其作为 `ExclusiveStartKey` 传递给后续调用，以便从该起点读取下一个数据块。如果未返回 `LastEvaluatedKey`，则表示没有其它与查询或扫描匹配的项目。

以下是一个简单的示例（使用资源接口，但客户端接口具有相同的模式），它每页最多读取 100 个项目，并循环操作，直到读取完所有项目。

```
import boto3

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('YourTableName')

query_params = {
    'KeyConditionExpression': Key('pk').eq('123') & Key('sk').gt(1000),
    'Limit': 100
}

while True:
    response = table.query(**query_params)

    # Process the items however you like
    for item in response['Items']:
        print(item)

    # No LastEvaluatedKey means no more items to retrieve
    if 'LastEvaluatedKey' not in response:
        break

    # If there are possibly more items, update the start key for the next page
    query_params['ExclusiveStartKey'] = response['LastEvaluatedKey']
```

为方便起见，boto3 可以用分页工具为您执行此操作。但是，它仅适用于客户端接口。以下是为使用分页工具而重写的代码：

```
import boto3

dynamodb = boto3.client('dynamodb')

paginator = dynamodb.get_paginator('query')

query_params = {
    'TableName': 'YourTableName',
    'KeyConditionExpression': 'pk = :pk_val AND sk > :sk_val',
    'ExpressionAttributeValues': {
        ':pk_val': {'S': '123'},
        ':sk_val': {'N': '1000'},
    },
    'Limit': 100
}

page_iterator = paginator.paginate(**query_params)

for page in page_iterator:
    # Process the items however you like
    for item in page['Items']:
        print(item)
```

有关更多信息，请参阅[分页工具指南](https://botocore.amazonaws.com/v1/documentation/api/latest/topics/events.html)和 [DynamoDB.Paginator.Query 的 API 参考](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/paginator/Query.html)。

**注意**  
分页工具也有自己的配置设置，名为 `MaxItems`、`StartingToken` 和 `PageSize`。要使用 DynamoDB 进行分页，则应忽略这些设置。

## Waiter
<a name="programming-with-python-waiters"></a>

通过 Waiter 可以等待某件事完成后再继续操作。目前，它们仅支持等待创建或删除表。在后台，Waiter 操作每 20 秒为您检查一次，最多 25 次。您可以自己做，但是在编写自动化时使用 Waiter 会很舒服。

以下代码演示了如何等待创建特定表：

```
# Create a table, wait until it exists, and print its ARN
response = client.create_table(...)
waiter = client.get_waiter('table_exists')
waiter.wait(TableName='YourTableName')
print('Table created:', response['TableDescription']['TableArn']
```

有关更多信息，请参阅 [Waiter 指南](https://boto3.amazonaws.com/v1/documentation/api/latest/guide/clients.html#waiters)和 [Waiter 参考](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#waiters)。

# 使用 JavaScript 对 Amazon DynamoDB 进行编程
<a name="programming-with-javascript"></a>

本指南为想要结合 JavaScript 使用 Amazon DynamoDB 的程序员提供了指导。了解适用于 JavaScript 的 AWS SDK、可用的抽象层、配置连接、处理错误、定义重试策略、管理 keep-alive 等。

**Topics**
+ [关于 适用于 JavaScript 的 AWS SDK](#programming-with-javascript-about)
+ [使用适用于 JavaScript 的 AWS SDK V3](#programming-with-javascript-using-the-sdk)
+ [访问 JavaScript 文档](#programming-with-javascript-documentation)
+ [抽象层](#programming-with-javascript-abstraction-layers)
+ [使用编组实用程序函数](#programming-with-javascript-using-marshall-utility)
+ [读取项目](#programming-with-javascript-reading-items)
+ [有条件写入](#programming-with-javascript-conditional-writes)
+ [分页](#programming-with-javascript-pagination)
+ [指定配置](#programming-with-javascript-config)
+ [Waiter](#programming-with-javascript-waiters)
+ [错误处理](#programming-with-javascript-error-handling)
+ [日志记录](#programming-with-javascript-logging)
+ [注意事项](#programming-with-javascript-considerations)

## 关于 适用于 JavaScript 的 AWS SDK
<a name="programming-with-javascript-about"></a>

适用于 JavaScript 的 AWS SDK 支持使用浏览器脚本或 Node.js 访问 AWS 服务。本文档重点介绍最新版本的 SDK（V3）。适用于 JavaScript 的 AWS SDK V3 由 AWS 作为 [GitHub 上托管的开源项目](https://github.com/aws/aws-sdk-js-v3)进行维护。问题和特征请求是公开的，您可以在 GitHub 存储库的问题页面上进行访问。

JavaScript V2 与 V3 类似，但存在语法差异。V3 更加模块化，可以更轻松地发布较小的依赖项，并且具有一流的 TypeScript 支持。建议使用最新版本的 SDK。

## 使用适用于 JavaScript 的 AWS SDK V3
<a name="programming-with-javascript-using-the-sdk"></a>

您可以使用节点程序包管理器将 SDK 添加到 Node.js 应用程序中。以下示例展示了如何添加最常用的 SDK 包来使用 DynamoDB。
+ `npm install @aws-sdk/client-dynamodb`
+ `npm install @aws-sdk/lib-dynamodb`
+ `npm install @aws-sdk/util-dynamodb`

安装程序包会添加对 package.json 项目文件依赖项部分的引用。您可以选择使用更新的 ECMAScript 模块语法。有关这两种方法的更多详细信息，请参阅“注意事项”部分。

## 访问 JavaScript 文档
<a name="programming-with-javascript-documentation"></a>

通过以下资源开始学习 JavaScript 文档：
+ 访问[开发人员指南](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/welcome.html)，获取核心 JavaScript 文档。安装说明位于**设置**部分。
+ 访问 [API 参考](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/introduction/)文档，浏览所有可用的类和方法。
+ 适用于 JavaScript 的 SDK 除了 DynamoDB 之外还支持许多 AWS 服务。使用以下步骤查找 DynamoDB 的特定 API 覆盖范围：

  1. 从**服务**中选择 **DynamoDB 和库**。这记录了低级别客户端。

  1. 选择 **lib-dynamodb**。这记录了高级别客户端。这两个客户端代表两个不同的抽象层，您可以选择使用。有关抽象层的更多信息，请参阅以下部分。

## 抽象层
<a name="programming-with-javascript-abstraction-layers"></a>

适用于 JavaScript 的 SDK V3 有一个低级别客户端（`DynamoDBClient`）和一个高级别客户端（`DynamoDBDocumentClient`）。

**Topics**
+ [低级别客户端（`DynamoDBClient`）](#programming-with-javascript-low-level-client)
+ [高级别客户端（`DynamoDBDocumentClient`）](#programming-with-javascript-high-level-client)

### 低级别客户端（`DynamoDBClient`）
<a name="programming-with-javascript-low-level-client"></a>

低级别客户端不对底层线路议提供额外抽象。它使您可以完全控制通信的各个方面，但是由于没有抽象，您必须做一些使用 DynamoDB JSON 格式提供项目定义之类的事情。

如以下示例所示，使用这种格式，必须明确说明数据类型。*S* 表示字符串值，*N* 表示数字值。线路上的数字始终以标记为数字类型的字符串发送，以确保精度没有损失。低级别 API 调用有命名模式，如 `PutItemCommand` 和 `GetItemCommand`。

以下示例使用的是 `Item` 使用 DynamoDB JSON 定义的低级别客户端：

```
const { DynamoDBClient, PutItemCommand } = require("@aws-sdk/client-dynamodb");

const client = new DynamoDBClient({});

async function addProduct() {
  const params = {
    TableName: "products",
    Item: {
      "id": { S: "Product01" },
      "description": { S: "Hiking Boots" },
      "category": { S: "footwear" },
      "sku": { S: "hiking-sku-01" },
      "size": { N: "9" }
    }
  };

  try {
    const data = await client.send(new PutItemCommand(params));
    console.log('result : ' + JSON.stringify(data));
  } catch (error) {
    console.error("Error:", error);
  }
}
addProduct();
```

### 高级别客户端（`DynamoDBDocumentClient`）
<a name="programming-with-javascript-high-level-client"></a>

高级别 DynamoDB 文档客户端提供了内置的便利特征，例如无需手动编组数据，并允许使用标准 JavaScript 对象直接读取和写入数据。[`lib-dynamodb` 文档](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-lib-dynamodb/)列出了相关优点。

要实例化 `DynamoDBDocumentClient`，请构造一个低级别 `DynamoDBClient`，然后使用 `DynamoDBDocumentClient` 对其进行封装。这两个程序包的函数命名约定略有不同。例如，低级别使用 `PutItemCommand`，而高级别使用 `PutCommand`。不同的名称允许两组函数共存于同一个上下文中，从而允许您在同一个脚本中混合使用这两组函数。

```
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { DynamoDBDocumentClient, PutCommand } = require("@aws-sdk/lib-dynamodb");

const client = new DynamoDBClient({});

const docClient = DynamoDBDocumentClient.from(client);

async function addProduct() {
  const params = {
    TableName: "products",
    Item: {
      id: "Product01",
      description: "Hiking Boots",
      category: "footwear",
      sku: "hiking-sku-01",
      size: 9,
    },
  };

  try {
    const data = await docClient.send(new PutCommand(params));
    console.log('result : ' + JSON.stringify(data));
  } catch (error) {
    console.error("Error:", error);
  }
}

addProduct();
```

当您使用 `GetItem`、`Query` 或 `Scan` 等 API 操作读取项目时，使用模式是一致的。

## 使用编组实用程序函数
<a name="programming-with-javascript-using-marshall-utility"></a>

您可以使用低级别客户端，并自行编组或解组数据类型。实用程序包 [util-dynamodb](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/Package/-aws-sdk-util-dynamodb/) 有一个接受 JSON 并生成 DynamoDB JSON 的 `marshall()` 实用程序函数，还有一个执行反向操作的 `unmarshall()` 函数。以下示例使用低级别客户端，数据编组由 `marshall()` 调用处理。

```
const { DynamoDBClient, PutItemCommand } = require("@aws-sdk/client-dynamodb");
const { marshall } = require("@aws-sdk/util-dynamodb");

const client = new DynamoDBClient({});

async function addProduct() {
  const params = {
    TableName: "products",
    Item: marshall({
      id: "Product01",
      description: "Hiking Boots",
      category: "footwear",
      sku: "hiking-sku-01",
      size: 9,
    }),
  };

  try {
    const data = await client.send(new PutItemCommand(params));
  } catch (error) {
    console.error("Error:", error);
  }
}
addProduct();
```

## 读取项目
<a name="programming-with-javascript-reading-items"></a>

要从 DynamoDB 中读取单个项目，请使用 `GetItem` API 操作。与 `PutItem` 命令类似，您可以选择使用低级别客户端，也可以选择使用高级别 Document 客户端。以下示例演示了如何使用高级别 Document 客户端检索项目。

```
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const { DynamoDBDocumentClient, GetCommand } = require("@aws-sdk/lib-dynamodb");

const client = new DynamoDBClient({});

const docClient = DynamoDBDocumentClient.from(client);

async function getProduct() {
  const params = {
    TableName: "products",
    Key: {
      id: "Product01",
    },
  };

  try {
    const data = await docClient.send(new GetCommand(params));
    console.log('result : ' + JSON.stringify(data));
  } catch (error) {
    console.error("Error:", error);
  }
}

getProduct();
```

使用 `Query` API 操作读取多个项目。您可以使用低级别客户端或 Document 客户端。以下示例使用高级别 Document 客户端。

```
const { DynamoDBClient } = require("@aws-sdk/client-dynamodb");
const {
  DynamoDBDocumentClient,
  QueryCommand,
} = require("@aws-sdk/lib-dynamodb");

const client = new DynamoDBClient({});

const docClient = DynamoDBDocumentClient.from(client);

async function productSearch() {
  const params = {
    TableName: "products",
    IndexName: "GSI1",
    KeyConditionExpression: "#category = :category and begins_with(#sku, :sku)",
    ExpressionAttributeNames: {
      "#category": "category",
      "#sku": "sku",
    },
    ExpressionAttributeValues: {
      ":category": "footwear",
      ":sku": "hiking",
    },
  };

  try {
    const data = await docClient.send(new QueryCommand(params));
    console.log('result : ' + JSON.stringify(data));
  } catch (error) {
    console.error("Error:", error);
  }
}

productSearch();
```

## 有条件写入
<a name="programming-with-javascript-conditional-writes"></a>

DynamoDB 写入操作可以指定逻辑条件表达式，该表达式的计算结果必须为 true 才能继续写入。如果条件的计算结果不是 true，则写入操作会引发异常。条件表达式可以检查项目是否已经存在，或者其属性是否符合某些约束。

`ConditionExpression = "version = :ver AND size(VideoClip) < :maxsize" `

当条件表达式失败时，您可以使用 `ReturnValuesOnConditionCheckFailure` 请求错误响应中包含不满足条件的项目，以推断问题出在哪里。有关更多详细信息，请参阅[使用 Amazon DynamoDB 处理高并发场景中的条件写入错误](https://aws.amazon.com/blogs/database/handle-conditional-write-errors-in-high-concurrency-scenarios-with-amazon-dynamodb/)。

```
try {
      const response = await client.send(new PutCommand({
          TableName: "YourTableName",
          Item: item,
          ConditionExpression: "attribute_not_exists(pk)",
          ReturnValuesOnConditionCheckFailure: "ALL_OLD"
      }));
  } catch (e) {
      if (e.name === 'ConditionalCheckFailedException') {
          console.log('Item already exists:', e.Item);
      } else {
          throw e;
      }
  }
```

[JavaScript SDK V3 文档](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/javascript_dynamodb_code_examples.html)和 [DynamoDB-SDK-Examples GitHub 存储库](https://github.com/aws-samples/aws-dynamodb-examples/tree/master/examples/SDK/node.js)中提供了更多显示 JavsScript SDK V3 使用情况其它方面的代码示例。

## 分页
<a name="programming-with-javascript-pagination"></a>

**Topics**
+ [使用 `paginateScan` 便捷方法](#using-the-paginatescan-convenience-method)

诸如 `Scan` 或 `Query` 之类的读取请求可能会返回数据集中的多个项目。如果您使用 `Limit` 参数执行 `Scan` 或 `Query`，那么一旦系统读取许多项目，就会发送部分响应，您需要分页才能检索其它项目。

系统每次请求最多只能读取 1 MB 的数据。如果包含 `Filter` 表达式，系统仍将从磁盘读取最多 1 MB 的数据，但会返回与筛选条件匹配的相应 MB 的项目。筛选操作可能会针对一个页面返回 0 个项目，但在搜索用尽之前仍需要进一步分页。

您应该在响应中查找 `LastEvaluatedKey`，并在后续请求中将其用作 `ExclusiveStartKey` 参数，才能继续检索数据。如以下示例所示，这用作书签。

**注意**  
样本在首次迭代时传递一个空 `lastEvaluatedKey` 作为 `ExclusiveStartKey`，这是允许的。

使用 `LastEvaluatedKey` 的示例：

```
const { DynamoDBClient, ScanCommand } = require("@aws-sdk/client-dynamodb");

const client = new DynamoDBClient({});

async function paginatedScan() {
  let lastEvaluatedKey;
  let pageCount = 0;

  do {
    const params = {
      TableName: "products",
      ExclusiveStartKey: lastEvaluatedKey,
    };

    const response = await client.send(new ScanCommand(params));
    pageCount++;
    console.log(`Page ${pageCount}, Items:`, response.Items);
    lastEvaluatedKey = response.LastEvaluatedKey;
  } while (lastEvaluatedKey);
}

paginatedScan().catch((err) => {
  console.error(err);
});
```

### 使用 `paginateScan` 便捷方法
<a name="using-the-paginatescan-convenience-method"></a>



SDK 提供了名为 `paginateScan` 和 `paginateQuery` 的便捷方法，这些方法可以为您完成这项工作，并在后台重复请求。使用标准 `Limit` 参数指定每次请求可读取的最大项目数。

```
const { DynamoDBClient, paginateScan } = require("@aws-sdk/client-dynamodb");

const client = new DynamoDBClient({});

async function paginatedScanUsingPaginator() {
  const params = {
    TableName: "products",
    Limit: 100
  };

  const paginator = paginateScan({client}, params);

  let pageCount = 0;

  for await (const page of paginator) {
    pageCount++;
    console.log(`Page ${pageCount}, Items:`, page.Items);
  }
}

paginatedScanUsingPaginator().catch((err) => {
  console.error(err);
});
```

**注意**  
除非表很小，否则不建议定期执行全表扫描。

## 指定配置
<a name="programming-with-javascript-config"></a>

**Topics**
+ [超时配置](#programming-with-javascript-config-timeouts)
+ [keep-alive 配置](#programming-with-javascript-config-keep-alive)
+ [重试配置](#programming-with-javascript-config-retries)

设置 `DynamoDBClient` 时，您可以通过将配置对象传递给构造函数来指定各种配置覆盖。例如，如果调用上下文或要使用的端点 URL 尚不知道要连接的区域，则可以指定要连接的区域。如果您希望出于开发目的选择 DynamoDB Local 实例，这会很有用。

```
const client = new DynamoDBClient({
  region: "eu-west-1",
  endpoint: "http://localhost:8000",
});
```

### 超时配置
<a name="programming-with-javascript-config-timeouts"></a>

DynamoDB 使用 HTTPS 进行客户端-服务器通信。您可以通过提供 `NodeHttpHandler` 对象来控制 HTTP 层的某些方面。例如，您可以调整密钥超时值 `connectionTimeout` 和 `requestTimeout`。`connectionTimeout` 是客户端在尝试建立连接时，在放弃之前等待的最大持续时间（以毫秒为单位）。

`requestTimeout` 定义了发送请求后客户端将等待响应的时间，也以毫秒为单位。两者的默认值均为零，这意味着超时已禁用，如果响应未到达，客户端的等待时间将没有限制。您应该将超时设置为合理的值，以便在出现网络问题时，请求将出错并可以启动新的请求。例如：

```
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { NodeHttpHandler } from "@smithy/node-http-handler";

const requestHandler = new NodeHttpHandler({
  connectionTimeout: 2000,
  requestTimeout: 2000,
});

const client = new DynamoDBClient({
  requestHandler
});
```

**注意**  
提供的示例使用 [Smithy](https://smithy.io/2.0/index.html) 导入。Smithy 是一种用于定义服务和 SDK 的语言，是开源的，由 AWS 维护。

除了配置超时值外，您还可以设置最大套接字数，这样可以增加每个源的并发连接数。开发人员指南包含[有关配置 `maxSockets` 参数的详细信息](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html)。

### keep-alive 配置
<a name="programming-with-javascript-config-keep-alive"></a>

使用 HTTPS 时，第一个请求总是需要一些往来通信才能建立安全连接。HTTP Keep-Alive 允许后续请求重用已经建立的连接，从而提高请求的效率并降低延迟。JavaScript V3 默认启用 HTTP Keep-Alive。

空闲连接可以保持活动状态的时间是有限制的。如果您有一个空闲的连接，但希望下次请求使用已经建立的连接，可以考虑定期发送请求，比如每分钟发送一次。

**注意**  
请注意，在较旧的 SDK V2 中，keep-alive 默认处于关闭状态，这意味着每个连接在使用后都会立即关闭。如果使用 V2，则您可以覆盖此设置。

### 重试配置
<a name="programming-with-javascript-config-retries"></a>

当 SDK 收到错误响应且 SDK 确定错误可以恢复时，例如节流异常或临时服务异常，它将会重试。您作为调用方，看不到这种情况的发生，只是您可能会注意到，请求花了更长时间才成功完成。

默认情况下，适用于 JavaScript 的 SDK V3 总共会发出 3 个请求，然后才放弃并将错误传递到调用上下文。您可以调整这些重试的次数和频率。

`DynamoDBClient` 构造函数接受一个 `maxAttempts` 设置，该设置限制了将要发生的尝试次数。以下示例将值从默认的 3 提高到总计 5。如果您将其设置为 0 或 1，则表示您不想进行任何自动重试，而是想在 catch 块中自己处理任何可恢复的错误。

```
const client = new DynamoDBClient({
  maxAttempts: 5,
});
```

您还可以使用自定义重试策略来控制重试的时间。为此，请导入 `util-retry` 实用程序包并创建一个自定义回退函数，该函数根据当前的重试计数计算重试之间的等待时间。

下面的示例表明，如果第一次尝试失败，最多可以尝试 5 次，延迟时间为 15、30、90 和 360 毫秒。自定义回退函数 ` calculateRetryBackoff` 通过接受重试尝试次数（首次重试从 1 开始）来计算延迟，并返回等待该请求的毫秒数。

```
const { ConfiguredRetryStrategy } = require("@aws-sdk/util-retry");

const calculateRetryBackoff = (attempt) => {
  const backoffTimes = [15, 30, 90, 360];
  return backoffTimes[attempt - 1] || 0;
};

const client = new DynamoDBClient({
  retryStrategy: new ConfiguredRetryStrategy(
    5, // max attempts.
    calculateRetryBackoff // backoff function.
  ),
});
```

## Waiter
<a name="programming-with-javascript-waiters"></a>

DynamoDB 客户端包含两个有用的 [Waiter 函数](https://awscli.amazonaws.com/v2/documentation/api/latest/reference/dynamodb/wait/index.html#cli-aws-dynamodb-wait)，当您希望代码等待表修改完成后再继续执行时，可以在创建、修改或删除表时使用这些函数。例如，您可以部署表，调用 `waitUntilTableExists` 函数，代码将会阻塞，直到表变为 **ACTIVE** 状态。Waiter 函数每 20 秒在内部使用 `describe-table` 轮询一次 DynamoDB 服务。

```
import {waitUntilTableExists, waitUntilTableNotExists} from "@aws-sdk/client-dynamodb";

… <create table details>

const results = await waitUntilTableExists({client: client, maxWaitTime: 180}, {TableName: "products"});
if (results.state == 'SUCCESS') {
  return results.reason.Table
}
console.error(`${results.state} ${results.reason}`);
```

仅当 `waitUntilTableExists` 特征可以执行显示表状态为 **ACTIVE** 的 `describe-table` 命令时，该特征才会返回控制权。这样可以确保您能够使用 `waitUntilTableExists` 等待创建完成以及诸如添加 GSI 索引之类的修改完成，这些修改可能需要一些时间才能应用，然后表才会恢复为 **ACTIVE** 状态。

## 错误处理
<a name="programming-with-javascript-error-handling"></a>

在此处的早期示例中，我们已经广泛地发现了所有错误。但是，在实际应用中，区分各种错误类型并实现更精确的错误处理非常重要。

DynamoDB 错误响应包含元数据，其中包括错误名称。您可以捕获错误，然后与错误条件中可能的字符串名称进行匹配，来确定如何继续。对于服务器端错误，您可以利用错误类型由 `@aws-sdk/client-dynamodb` 程序包导出的 `instanceof` 运算符，来高效地管理错误处理。

需要注意的是，这些错误只有在所有重试都用尽后才会显示。如果重试错误并最终成功调用，则从代码的角度来看，没有错误，只是延迟略有增加。重试将在 Amazon CloudWatch 图表中显示为失败的请求，例如节流请求或错误请求。如果客户端达到最大重试次数，它将放弃并引发异常。客户端以此表明它不会重试。

下面是一个代码段，用于捕获错误并根据返回的错误类型采取行动。

```
import {
  ResourceNotFoundException
  ProvisionedThroughputExceededException,
  DynamoDBServiceException,
} from "@aws-sdk/client-dynamodb";

try {
  await client.send(someCommand);
} catch (e) {
    if (e instanceof ResourceNotFoundException) {
      // Handle ResourceNotFoundException
    } else if (e instanceof ProvisionedThroughputExceededException) {
      // Handle ProvisionedThroughputExceededException
    } else if (e instanceof DynamoDBServiceException) {
      // Handle DynamoDBServiceException
    } else {
      // Other errors such as those from the SDK
      if (e.name === "TimeoutError") {
        // Handle SDK TimeoutError.
      } else {
        // Handle other errors.
      }
    }
}
```

有关常见错误字符串，请参阅《DynamoDB 开发人员指南》**中的[DynamoDB 错误处理](Programming.Errors.md)。任何特定 API 调用可能出现的确切错误都可以在该 API 调用的文档中找到，例如[查询 API 文档](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Query.html)。

错误的元数据包括其它属性，具体视错误而定。对于 ` TimeoutError`，元数据包括尝试次数和 `totalRetryDelay`，如下所示。

```
{
  "name": "TimeoutError",
  "$metadata": {
    "attempts": 3,
    "totalRetryDelay": 199
  }
}
```

如果您管理自己的重试策略，则需要区分节流和错误：
+ **节流**（由 ` ProvisionedThroughputExceededException` 或 `ThrottlingException` 表示）表示服务运行正常，它会通知您已超出 DynamoDB 表或分区的读取或写入容量。每过一毫秒，就会有多一点的读取或写入容量可用，因此您可以快速重试，例如每 50 毫秒重试一次，尝试访问新释放的容量。

   使用节流，您并不特别需要指数回退，因为节流属于轻量级，DynamoDB 可以返回，而且不会向您收取每次请求的费用。指数回退会将更长的延迟分配给已经等待最长时间的客户端线程，从统计学上讲，将超越 p50 和 p99。
+ **错误**（由 ` InternalServerError` 或 `ServiceUnavailable` 等表示）表示服务存在暂时性问题，可能是整个表，也可能只是您正在读取或写入的分区。使用错误，您可以在重试前暂停更长时间，例如 250 毫秒或 500 毫秒，并使用抖动来错开重试。

## 日志记录
<a name="programming-with-javascript-logging"></a>

开启日志记录功能以获取有关 SDK 正在执行的操作的更多详细信息。您可以在 `DynamoDBClient` 上设置参数，如以下示例所示。更多日志信息将显示在控制台中，包括状态码和已用容量等元数据。如果您在终端窗口中本地运行代码，则日志会显示于此。如果您在 AWS Lambda 中运行代码，并且设置了 Amazon CloudWatch Logs，则控制台输出将写入于此。

```
const client = new DynamoDBClient({
  logger: console
});
```

您还可以挂钩到内部 SDK 活动，并在某些事件发生时执行自定义日志记录。以下示例使用客户端的 `middlewareStack` 拦截从 SDK 发送的每个请求，并在请求发生时将其记录下来。

```
const client = new DynamoDBClient({});

client.middlewareStack.add(
  (next) => async (args) => {
    console.log("Sending request from AWS SDK", { request: args.request });
    return next(args);
  },
  {
    step: "build",
    name: "log-ddb-calls",
  }
);
```

`MiddlewareStack` 提供了用于观察和控制 SDK 行为的强大钩子。有关更多信息，请参阅博客 [Introducing Middleware Stack in Modular 适用于 JavaScript 的 AWS SDK](https://aws.amazon.com/blogs/developer/middleware-stack-modular-aws-sdk-js/)。

## 注意事项
<a name="programming-with-javascript-considerations"></a>

在您的项目中实施适用于 JavaScript 的 AWS SDK 时，需要考虑以下其它因素。

**模块系统**  
该 SDK 支持两个模块系统，CommonJS 和 ES (ECMAScript)。CommonJS 使用 `require` 函数，而 ES 使用 `import` 关键字。  

1. **Common JS** – `const { DynamoDBClient, PutItemCommand } = require("@aws-sdk/client-dynamodb");`

1. **ES (ECMAScript** – `import { DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";`
项目类型指示了要使用的模块系统，并在 package.json 文件的类型部分中指定。默认为 CommonJS。使用 `"type": "module"` 指示 ES 项目。如果您有一个使用 CommonJS 程序包格式的现有 Node.JS 项目，您仍然可以通过使用 .mjs 扩展名命名函数文件，来使用更现代的 SDK V3 Import 语法添加函数。这将允许将代码文件视为 ES (ECMAScript)。

**异步操作**  
您将看到许多使用回调和承诺来处理 DynamoDB 操作结果的代码示例。在现代 JavaScript 中，不再需要这种复杂性，开发人员可以利用更简洁、可读性更好的异步/等待语法执行异步操作。

**Web 浏览器运行时**  
使用 React 或 React Native 构建的网络和移动开发人员可以在他们的项目中使用适用于 JavaScript 的 SDK。在较早的 SDK V2 中，Web 开发人员必须将完整的 SDK 加载到浏览器中，并引用托管在 https://sdk.amazonaws.com/js/ 上的 SDK 图片。  
在 V3 中，您可以使用 Webpack 将所需的 V3 客户端模块和所有必需的 JavaScript 函数捆绑到一个 JavaScript 文件中，然后将其添加到 HTML 页面 `<head>` 的脚本标签中，如 SDK 文档的[浏览器脚本入门](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/getting-started-browser.html)部分所述。

**DAX 数据面板操作**  
适用于 JavaScript 的 SDK V3 支持 Amazon DynamoDB Streams Accelerator（DAX）数据面板操作。

# 使用 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)。

# DynamoDB 错误处理
<a name="Programming.Errors"></a>

 本节描述运行时系统错误，以及如何处理它们。它还描述特定于 Amazon DynamoDB 的错误消息和代码。有关适用于所有 AWS 服务的常见错误列表，请参阅[访问管理](https://docs.aws.amazon.com/amazondynamodb/latest/APIReference/CommonErrors.html)

**Topics**
+ [错误组成部分](#Programming.Errors.Components)
+ [事务性错误](#Programming.Errors.TransactionalErrors)
+ [错误消息和代码](#Programming.Errors.MessagesAndCodes)
+ [应用程序中的错误处理](#Programming.Errors.Handling)
+ [错误重试和指数回退](#Programming.Errors.RetryAndBackoff)
+ [批处理操作和错误处理](#Programming.Errors.BatchOperations)

## 错误组成部分
<a name="Programming.Errors.Components"></a>

程序发送请求后，DynamoDB 会尝试处理该请求。如果请求成功，DynamoDB 将返回一个 HTTP 成功状态代码 (`200 OK`)，以及所请求操作的结果。

如果请求失败，DynamoDB 会返回一个错误。每个错误包含三个部分：
+ HTTP 状态代码（如 `400`）。
+ 异常名称（如 `ResourceNotFoundException`）。
+ 错误消息（如 `Requested resource not found: Table: tablename not found`）。

AWS SDK 负责将错误传播到应用程序，以便您能执行适当操作。例如，在 Java 程序中，您可以编写 `try-catch` 逻辑以处理 `ResourceNotFoundException`。

如果您使用的不是 AWS SDK，将需要解析来自 DynamoDB 的低级响应内容。下面是一个此类响应的示例。

```
HTTP/1.1 400 Bad Request
x-amzn-RequestId: LDM6CJP8RMQ1FHKSC1RBVJFPNVV4KQNSO5AEMF66Q9ASUAAJG
Content-Type: application/x-amz-json-1.0
Content-Length: 240
Date: Thu, 15 Mar 2012 23:56:23 GMT

{"__type":"com.amazonaws.dynamodb.v20120810#ResourceNotFoundException",
"message":"Requested resource not found: Table: tablename not found"}
```

## 事务性错误
<a name="Programming.Errors.TransactionalErrors"></a>

有关事务性错误的信息，请参阅 [DynamoDB 中的事务冲突处理](transaction-apis.md#transaction-conflict-handling)

## 错误消息和代码
<a name="Programming.Errors.MessagesAndCodes"></a>

下面是 DynamoDB 返回的异常列表，按 HTTP 状态代码分组。如果*确定重试？*为*是*，则可以在此提交相同请求。如果*确定重试？*为*否*，则需要先解决客户端问题，然后再提交新请求。

### HTTP 状态代码 400
<a name="Programming.Errors.MessagesAndCodes.http400"></a>

HTTP `400` 状态代码表示与请求相关的问题，例如身份验证失败、缺少必需的参数或者超出预置表吞吐量。必须先解决应用程序中存在的问题，然后再重新提交请求。

**AccessDeniedException **  
消息：*访问被拒绝。*  
客户端未正确签名请求。如果使用 AWS SDK，系统将自动为您签署请求；否则，请参阅《AWS 一般参考》**中的[签名版本 4 签名流程](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html)。  
确定重试？ 否

**ConditionalCheckFailedException**  
消息：*有条件请求失败。*  
指定条件的计算结果为 false。例如，您可能已尝试对项目执行有条件更新，但属性的实际值与条件预期值不匹配。  
确定重试？ 否

**IncompleteSignatureException**  
消息：*请求签名不符合 AWS 标准*  
请求签名未包含所有必需的部分。如果使用 AWS SDK，系统将自动为您签署请求；否则，请参阅《AWS 一般参考》**中的[签名版本 4 签名流程](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html)。  
确定重试？ 否

**ItemCollectionSizeLimitExceededException**  
消息：*超过集合大小。*  
对于包含全局二级索引的表，具有相同分区键值的项目组超过 10 GB 最大大小限制。有关项目集的更多信息，请参见 [本地二级索引中的项目集合](LSI.md#LSI.ItemCollections)。  
确定重试？ 是

**LimitExceededException**  
消息：*指定订阅者的操作过多。*  
并发控制层面操作过多。处于 `CREATING`、`DELETING` 或 `UPDATING` 状态的表和索引的累计数量不能超过 500。  
确定重试？ 是

**MissingAuthenticationTokenException**  
消息：*请求必须包含有效(已注册)的 AWS 访问密钥 ID。*  
请求未包含所需的授权标头，或请求的格式不正确。请参阅 [DynamoDB 低级 API](Programming.LowLevelAPI.md)。  
确定重试？ 否

**ProvisionedThroughputExceededException**  
消息：*已超过表或一个或更多全局二级索引的最大允许预置吞吐量。要查看预置吞吐量与占用吞吐量的性能指标，请打开 [Amazon CloudWatch 控制台](https://console.aws.amazon.com/cloudwatch/home)。*  
错误包括 `ThrottlingReason` 字段的列表，这些字段按照格式 `ResourceType+OperationType+LimitType` 提供有关发生节流情况的具体背景信息（例如 `TableReadProvisionedThroughputExceeded`），错误中还包括受影响资源的 ARN。您可以利用这些信息来确定哪些资源受到限制（表或索引）、触发节流的操作类型（读取或写入）以及超过的特定限额（在本例中为预置容量）。有关诊断和解决节流问题的更多信息，请参阅[诊断节流](throttling-diagnosing-workflow.md)。  
示例：您的请求速率过高。DynamoDB 的 AWS SDK 自动重试收到此异常的请求。除非重试队列太长以致无法完成，否则请求最终都会成功。请使用[错误重试和指数回退](#Programming.Errors.RetryAndBackoff)降低请求频率，  
确定重试？ 是

**ReplicatedWriteConflictException**  
消息：*One or more items in this request are being modified by a request in another Region.*  
示例：已请求对多区域强一致性（MRSC）全局表中的项目执行写入操作，而该表当前正由另一个区域中的请求进行修改。  
确定重试？ 是

**RequestLimitExceeded**  
消息：*吞吐量超出您的账户的当前吞吐量限制。要请求提高限制，请联系 AWS Support：[https://aws.amazon.com/support](https://aws.amazon.com/support)*。  
错误包括 `ThrottlingReason` 字段的列表，这些字段按照格式 `ResourceType+OperationType+LimitType` 提供有关发生节流情况的具体背景信息（例如 `TableWriteAccountLimitExceeded` 或 `IndexReadAccountLimitExceeded`），错误中还包括受影响资源的 ARN。您可以利用这些信息确定哪些资源受到限制（表或索引）、触发节流的操作类型（读取或写入），以及您是否超过了账户级别的服务配额。有关诊断和解决节流问题的更多信息，请参阅[诊断节流](throttling-diagnosing-workflow.md)。  
示例：按需请求的速率超出允许的账户吞吐量，该表无法进一步扩展。  
确定重试？ 是

**ResourceInUseException**  
消息：*您尝试更改的资源正在使用中。*  
示例：您尝试重新创建现有表，或删除目前处于 `CREATING` 状态的表。  
确定重试？ 否

**ResourceNotFoundException **  
消息：*未找到请求的资源。*  
示例：您正在请求的表不存在，或过早进入 `CREATING` 状态。  
确定重试？ 否

**ThrottlingException**  
消息：*请求速率超出允许吞吐量。*  
此异常将作为 AmazonServiceException 响应返回，并带有 THROTTLING\$1EXCEPTION 状态代码。如果过快执行[控制层面](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.API.html#HowItWorks.API.ControlPlane) API 操作，则可能会返回此异常。  
对于使用按需模式的表，如果请求速率过高，则可能会针对任何[数据层面](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.API.html#HowItWorks.API.DataPlane) API 操作返回此异常。要了解有关按需扩展的更多信息，请参阅[初始吞吐量和扩展属性](on-demand-capacity-mode.md#on-demand-capacity-mode-initial)。  
错误包括 `ThrottlingReason` 字段的列表，这些字段按照格式 `ResourceType+OperationType+LimitType` 提供有关发生节流情况的具体背景信息（例如 `TableReadKeyRangeThroughputExceeded` 或 `IndexWriteMaxOnDemandThroughputExceeded`），错误中还包括受影响资源的 ARN。您可以利用此信息来确定哪些资源受到限制（表或索引）、触发节流的操作类型（读取或写入）以及超过的特定限额（分区限额还是按需最大吞吐量）。有关诊断和解决节流问题的更多信息，请参阅[诊断节流](throttling-diagnosing-workflow.md)。  
确定重试？ 是

**UnrecognizedClientException**  
消息：*访问密钥 ID 或安全令牌无效。*  
请求签名错误。最有可能的原因是 AWS 访问密钥 ID 或密钥无效。  
确定重试？ 是

**ValidationException**  
消息：根据遇到的特定错误而变化  
有多种原因可能导致此错误，例如缺少必需参数，值超出范围，或者数据类型不匹配。错误消息包含有关导致错误的请求特定部分的详细信息。  
确定重试？ 否

### HTTP 状态代码 5xx
<a name="Programming.Errors.MessagesAndCodes.http5xx"></a>

HTTP `5xx` 状态代码表示必须由 AWS 解决的问题。这可能是临时错误，在这种情况下，可以重试请求直到成功。否则，请转至 [AWS Service Health Dashboard](https://status.aws.amazon.com/)，查看是否存在与服务相关的任何操作问题。

有关更多信息，请参阅[如何解决 Amazon DynamoDB 中的 HTTP 5xx 错误？](https://aws.amazon.com/premiumsupport/knowledge-center/dynamodb-http-5xx-errors/)

**InternalServerError (HTTP 500)**  
DynamoDB 无法处理您的请求。  
确定重试？ 是  
处理项目时可能遇到内部服务器错误。这些是表的生命周期中的预期错误。可立即重试所有失败的请求。  
当您收到写入操作的状态代码 500 时，该操作可能已成功或失败。如果写入操作是 `TransactWriteItem` 请求，那么可以重试该操作。如果写入操作是单项写入请求，例如 `PutItem`、`UpdateItem` 或 `DeleteItem`，那么您应用程序应该在重试操作之前读取项目的状态，和/或使用 [DynamoDB 条件表达式 CLI 示例](Expressions.ConditionExpressions.md) 以确保项目在重试后保持正确的状态，无论之前的操作是成功还是失败。如果写入操作要求幂等性，请使用 [`TransactWriteItem`](transaction-apis.md#transaction-apis-txwriteitems)，它通过自动指定 `ClientRequestToken` 消除多次尝试执行同一操作的歧义，从而支持幂等性请求。

**ServiceUnavailable (HTTP 503)**  
DynamoDB 当前不可用。（这应是一种暂时状态。）  
确定重试？ 是

## 应用程序中的错误处理
<a name="Programming.Errors.Handling"></a>

为了让应用程序平稳运行，需要添加逻辑以抓取和响应错误。典型的方法包括使用 `try-catch` 块或 `if-then` 语句。

AWS SDK 执行自己的重试和检查错误。如果使用某个 AWS SDK 时遇到错误，错误代码和错误描述可帮助您纠正错误。

您还应该会在响应中看到 `Request ID`。如果需要使用 AWS 支持诊断问题，则 `Request ID` 会很有用。

## 错误重试和指数回退
<a name="Programming.Errors.RetryAndBackoff"></a>

网络上的大量组件（例如 DNS 服务器、交换机、负载均衡器等）都可能在某个指定请求生命周期中的任一环节出现问题。在联网环境中，处理这些错误响应的常规技术是在客户应用程序中实施重试。此方法提高应用程序的可靠性。

每个 AWS SDK 都会自动实现重试逻辑。可以修改重试参数以满足您的需求。例如，考虑一个需要 fail-fast 策略的 Java 应用程序，并且在出错时不允许重试。利用 适用于 Java 的 AWS SDK，您可以使用 `ClientConfiguration` 类并提供值为 `maxErrorRetry` 的 `0` 来禁用重试。有关更多信息，请参阅适用于您编程语言的 AWS SDK 文档。

如果您没有使用 AWS SDK，则应当对收到服务器错误 (5xx) 的原始请求执行重试。但是，客户端错误（4xx，不是 `ThrottlingException` 或 `ProvisionedThroughputExceededException`）表示您需要对请求本身进行修改，先修正了错误然后再重试。有关解决特定节流场景的详细建议，请参阅 [DynamoDB 节流故障排除](TroubleshootingThrottling.md)部分。

除了简单重试之外，每个 AWS SDK 还实施指数回退算法来实现更好的流程控制。指数回退的原理是对于连续错误响应，重试等待间隔越来越长。例如，第一次重试最多等待 50 毫秒，第二次重试最多等待 100 毫秒，第三次重试最多等待 200 毫秒，依此类推。但是，如果过段时间后，请求仍然失败，则出错的原因可能是因为请求大小超出预置吞吐量，而不是请求速率的问题。您可以设置最大重试次数，在大约一分钟的时候停止重试。如果请求失败，请检查您的预置吞吐量选项。

**注意**  
AWS SDK 实施自动重试逻辑和指数回退。

大多数指数回退算法会利用抖动（随机延迟）防止连续的冲突。由于在这些情况下不会尝试避免此类冲突，因此无需使用此随机数字。但是，如果使用并发客户端，抖动可帮助您更快地成功执行请求。有关更多信息，请参阅有关[指数回退和抖动](http://www.awsarchitectureblog.com/2015/03/backoff.html)的博文。

## 批处理操作和错误处理
<a name="Programming.Errors.BatchOperations"></a>

DynamoDB 低级 API 支持批量读取和写入操作。`BatchGetItem` 从一个或多个表中读取项目，`BatchWriteItem` 在一个或多个表中放置或删除项目。这些批量操作作为其他非批量 DynamoDB 操作的包装得以实现。换句话说，`BatchGetItem` 为批处理中的每个项目调用 `GetItem` 一次。同样，`BatchWriteItem` 根据需要为批处理中的每个项目调用 `DeleteItem` 或 `PutItem`。

批量操作可以容忍批处理中的个别请求失败。例如，假设一个 `BatchGetItem` 请求读取五个项目。即使某些底层 `GetItem` 请求失败，这也不会导致整个 `BatchGetItem` 操作失败。但是，如果所有五个读取操作都失败，则整个 `BatchGetItem` 失败。

批量操作会返回有关各失败请求的信息，以便您诊断问题并重试操作。对于 `BatchGetItem`，在响应的 `UnprocessedKeys` 值中会返回有问题的表和主键。对于 `BatchWriteItem`，在 `UnprocessedItems` 中返回类似信息。

造成读取失败或写入失败的最可能原因是*限制*。对于 `BatchGetItem`，原因是批量请求中的一个或多个表没有足够的预置读取容量来支持操作。对于 `BatchWriteItem`，原因是一个或多个表没有足够的预置写入容量。

如果 DynamoDB 返回了任何未处理的项目，应对这些项目重试批量操作。然而，*我们强烈建议您使用指数回退算法*。如果立即重试批量操作，底层读取或写入请求仍然会由于各表的限制而失败。如果使用指数回退延迟批量操作，批处理中的各请求成功的可能性更大。

**注意**  
一些 AWS SDK 提供更高级别的客户端，可以自动处理未处理的项目重试，因此您无需自行实施此重试逻辑。例如：  
**Java**：在执行批量操作时，适用于 Java 的 AWS SDK v2 中的 [DynamoDB 增强型客户端](DynamoDBEnhanced.md)和 v1 中的 [DynamoDBMapper](DynamoDBMapper.md) 都会自动重试未处理的项目。
**Python**：对于批量写入操作，boto3 表资源 `batch_writer` 隐式处理未处理的项目重试。有关更多信息，请参阅 [使用表资源 batch\$1writer](programming-with-python.md#programming-with-python-batch-writer)。
如果您使用的底层客户端或 SDK 不提供此行为，则必须按照上述方式自行实施重试逻辑。

# 结合使用 DynamoDB 与 AWS SDK
<a name="sdk-general-information-section"></a>

AWS 软件开发工具包（SDK）适用于许多常用编程语言。每个软件开发工具包都提供 API、代码示例和文档，使开发人员能够更轻松地以其首选语言构建应用程序。


| SDK 文档 | 代码示例 | 
| --- | --- | 
| [适用于 C\$1\$1 的 AWS SDK](https://docs.aws.amazon.com/sdk-for-cpp) | [适用于 C\$1\$1 的 AWS SDK 代码示例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/cpp) | 
| [AWS CLI](https://docs.aws.amazon.com/cli) | [AWS CLI 代码示例](https://docs.aws.amazon.com/code-library/latest/ug/cli_2_code_examples.html) | 
| [适用于 Go 的 AWS SDK](https://docs.aws.amazon.com/sdk-for-go) | [适用于 Go 的 AWS SDK 代码示例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/gov2) | 
| [适用于 Java 的 AWS SDK](https://docs.aws.amazon.com/sdk-for-java) | [适用于 Java 的 AWS SDK 代码示例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javav2) | 
| [适用于 JavaScript 的 AWS SDK](https://docs.aws.amazon.com/sdk-for-javascript) | [适用于 JavaScript 的 AWS SDK 代码示例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/javascriptv3) | 
| [适用于 Kotlin 的 AWS SDK](https://docs.aws.amazon.com/sdk-for-kotlin) | [适用于 Kotlin 的 AWS SDK 代码示例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/kotlin) | 
| [适用于 .NET 的 AWS SDK](https://docs.aws.amazon.com/sdk-for-net) | [适用于 .NET 的 AWS SDK 代码示例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/dotnetv3) | 
| [适用于 PHP 的 AWS SDK](https://docs.aws.amazon.com/sdk-for-php) | [适用于 PHP 的 AWS SDK 代码示例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/php) | 
| [AWS Tools for PowerShell](https://docs.aws.amazon.com/powershell) | [AWS Tools for PowerShell 代码示例](https://docs.aws.amazon.com/code-library/latest/ug/powershell_5_code_examples.html) | 
| [适用于 Python (Boto3) 的 AWS SDK](https://docs.aws.amazon.com/pythonsdk) | [适用于 Python (Boto3) 的 AWS SDK 代码示例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/python) | 
| [适用于 Ruby 的 AWS SDK](https://docs.aws.amazon.com/sdk-for-ruby) | [适用于 Ruby 的 AWS SDK 代码示例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/ruby) | 
| [适用于 Rust 的 AWS SDK](https://docs.aws.amazon.com/sdk-for-rust) | [适用于 Rust 的 AWS SDK 代码示例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/rustv1) | 
| [适用于 SAP ABAP 的 AWS SDK](https://docs.aws.amazon.com/sdk-for-sapabap) | [适用于 SAP ABAP 的 AWS SDK 代码示例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/sap-abap) | 
| [适用于 Swift 的 AWS SDK](https://docs.aws.amazon.com/sdk-for-swift) | [适用于 Swift 的 AWS SDK 代码示例](https://github.com/awsdocs/aws-doc-sdk-examples/tree/main/swift) | 

有关特定于 DynamoDB 的示例，请参阅[适用于使用 AWS SDK 的 DynamoDB 的代码示例](service_code_examples.md)。

**示例可用性**  
找不到所需的内容？ 通过使用此页面底部的**提供反馈**链接请求代码示例。