DynamoDBMapper 类 - Amazon DynamoDB
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

DynamoDBMapper 类

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

有关相应的 Javadoc 文档,请参阅Amazon SDK for JavaAPI 参考DynamoDBMapper

save

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

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

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

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

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

load

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

DynamoDBMapperConfig config = DynamoDBMapperConfig.builder() .withConsistentReads(DynamoDBMapperConfig.ConsistentReads.CONSISTENT).build(); CatalogItem item = mapper.load(CatalogItem.class, item.getId(), config);

默认情况下,DynamoDB 所返回的项目的值采用最终一致性。有关 DynamoDB 的最终一致性模式的信息,请参阅读取一致性

删除

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

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

查询

查询表或二级索引。只有当表或索引具有复合主键(分区键和排序键)时,您才能对其执行查询。此方法需要您提供分区键值和应用于排序键的查询筛选条件。筛选条件表达式包括一个条件和一个值。

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

Reply ( Id, ReplyDateTime, ... )

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

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表示索引的排序键(消息)。

现在,您可以使用 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

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

scan

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

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

Reply ( Id, ReplyDateTime, ... )

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

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。此索引的映射器类显示在 查询 部分。它使用 @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

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

parallelScan

对整个表或二级索引执行并行扫描。您可以指定表的几个逻辑分段,并指定用于筛选结果的扫描表达式。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);

有关说明 parallelScan 用法的 Java 代码示例,请参阅DynamoDBMapper 查询和扫描操作

batchSave

调用一次或多次 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

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

以下 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

调用一次或多次 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

调用一次或多次 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

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

有关特定于交易的异常列表,请参阅TransactWriteItems 错误。

有关 DynamoDB 事务和提供的原子性、一致性、隔离性和持久性 (ACID) 保证的更多信息,请参阅 Amazon DynamoDB Transactions

注意

该方法不支持以下功能:

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

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

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

有关特定于交易的异常列表,请参阅TransactGetItems 错误。

有关 DynamoDB 事务和提供的原子性、一致性、隔离性和持久性 (ACID) 保证的更多信息,请参阅 Amazon DynamoDB Transactions

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

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

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

generateCreateTable请求

分析代表 DynamoDB 表的 POJO 类,并返回针对该表的 CreateTableRequest

创建 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 myS3Bucket = "myS3bucket"; String myS3Key = "productImages/book_150_cover.jpg"; item.setProductImage(mapper.createS3Link(myS3Bucket, myS3Key)); item.getProductImage().uploadFrom(new File("/file/path/book_150_cover.jpg")); mapper.save(item);

S3Link 类提供了许多用于操作 Amazon S3 中的对象的其他方法。有关更多信息,请参阅适用于 S3Link 的 Javadocs

Gets3 ClientCache

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