

# 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 客户端。