

# 用于 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;
        }
    }
}
```