DynamoDBMapper Class - Amazon DynamoDB

DynamoDBMapper Class

The DynamoDBMapper class is the entry point to Amazon DynamoDB. It provides access to a DynamoDB endpoint and enables you to access your data in various tables. It also enables you to perform various create, read, update, and delete (CRUD) operations on items, and run queries and scans against tables. This class provides the following methods for working with DynamoDB.

For the corresponding Javadoc documentation, see DynamoDBMapper in the AWS SDK for Java API Reference.

save

Saves the specified object to the table. The object that you want to save is the only required parameter for this method. You can provide optional configuration parameters using the DynamoDBMapperConfig object.

If an item that has the same primary key does not exist, this method creates a new item in the table. If an item that has the same primary key exists, it updates the existing item. If the partition key and sort key are of type String and are annotated with @DynamoDBAutoGeneratedKey, they are given a random universally unique identifier (UUID) if left uninitialized. Version fields that are annotated with @DynamoDBVersionAttribute are incremented by one. Additionally, if a version field is updated or a key generated, the object passed in is updated as a result of the operation.

By default, only attributes corresponding to mapped class properties are updated. Any additional existing attributes on an item are unaffected. However, if you specify SaveBehavior.CLOBBER, you can force the item to be completely overwritten.

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

If you have versioning enabled, the client-side and server-side item versions must match. However, the version does not need to match if the SaveBehavior.CLOBBER option is used. For more information about versioning, see Optimistic locking with version number.

load

Retrieves an item from a table. You must provide the primary key of the item that you want to retrieve. You can provide optional configuration parameters using the DynamoDBMapperConfig object. For example, you can optionally request strongly consistent reads to ensure that this method retrieves only the latest item values as shown in the following Java statement.

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

By default, DynamoDB returns the item that has values that are eventually consistent. For information about the eventual consistency model of DynamoDB, see Read consistency.

delete

Deletes an item from the table. You must pass in an object instance of the mapped class.

If you have versioning enabled, the client-side and server-side item versions must match. However, the version does not need to match if the SaveBehavior.CLOBBER option is used. For more information about versioning, see Optimistic locking with version number.

query

Queries a table or a secondary index. You can query a table or an index only if it has a composite primary key (partition key and sort key). This method requires you to provide a partition key value and a query filter that is applied on the sort key. A filter expression includes a condition and a value.

Assume that you have a table, Reply, that stores forum thread replies. Each thread subject can have zero or more replies. The primary key of the Reply table consists of the Id and ReplyDateTime fields, where Id is the partition key and ReplyDateTime is the sort key of the primary key.

Reply ( Id, ReplyDateTime, ... )

Assume that you created a mapping between a Reply class and the corresponding Reply table in DynamoDB. The following Java code uses DynamoDBMapper to find all replies in the past two weeks for a specific thread subject.

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

The query returns a collection of Reply objects.

By default, the query method returns a "lazy-loaded" collection. It initially returns only one page of results, and then makes a service call for the next page if needed. To obtain all the matching items, iterate over the latestReplies collection.

Note that calling the size() method on the collection will load every result in order to provide an accurate count. This can result in a lot of provisioned throughput being consumed, and on a very large table could even exhaust all the memory in your JVM.

To query an index, you must first model the index as a mapper class. Suppose that the Reply table has a global secondary index named PostedBy-Message-Index. The partition key for this index is PostedBy, and the sort key is Message. The class definition for an item in the index would look like the following.

@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. }

The @DynamoDBTable annotation indicates that this index is associated with the Reply table. The @DynamoDBIndexHashKey annotation denotes the partition key (PostedBy) of the index, and @DynamoDBIndexRangeKey denotes the sort key (Message) of the index.

Now you can use DynamoDBMapper to query the index, retrieving a subset of messages that were posted by a particular user. You do not need to specify the index name if you do not have conflicting mappings across tables and indexes and the mappings are already made in the mapper. The mapper will infer based on the primary key and sort key. The following code queries a global secondary index. Because global secondary indexes support eventually consistent reads but not strongly consistent reads, you must specify 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);

The query returns a collection of PostedByMessage objects.

queryPage

Queries a table or secondary index and returns a single page of matching results. As with the query method, you must specify a partition key value and a query filter that is applied on the sort key attribute. However, queryPage returns only the first "page" of data, that is, the amount of data that fits in 1 MB

scan

Scans an entire table or a secondary index. You can optionally specify a FilterExpression to filter the result set.

Assume that you have a table, Reply, that stores forum thread replies. Each thread subject can have zero or more replies. The primary key of the Reply table consists of the Id and ReplyDateTime fields, where Id is the partition key and ReplyDateTime is the sort key of the primary key.

Reply ( Id, ReplyDateTime, ... )

If you mapped a Java class to the Reply table, you can use the DynamoDBMapper to scan the table. For example, the following Java code scans the entire Reply table, returning only the replies for a particular year.

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

By default, the scan method returns a "lazy-loaded" collection. It initially returns only one page of results, and then makes a service call for the next page if needed. To obtain all the matching items, iterate over the replies collection.

Note that calling the size() method on the collection will load every result in order to provide an accurate count. This can result in a lot of provisioned throughput being consumed, and on a very large table could even exhaust all the memory in your JVM.

To scan an index, you must first model the index as a mapper class. Suppose that the Reply table has a global secondary index named PostedBy-Message-Index. The partition key for this index is PostedBy, and the sort key is Message. A mapper class for this index is shown in the query section. It uses the @DynamoDBIndexHashKey and @DynamoDBIndexRangeKey annotations to specify the index partition key and sort key.

The following code example scans PostedBy-Message-Index. It does not use a scan filter, so all of the items in the index are returned to you.

DynamoDBScanExpression scanExpression = new DynamoDBScanExpression() .withIndexName("PostedBy-Message-Index") .withConsistentRead(false); List<PostedByMessage> iList = mapper.scan(PostedByMessage.class, scanExpression); Iterator<PostedByMessage> indexItems = iList.iterator();

scanPage

Scans a table or secondary index and returns a single page of matching results. As with the scan method, you can optionally specify a FilterExpression to filter the result set. However, scanPage only returns the first "page" of data, that is, the amount of data that fits within 1 MB.

parallelScan

Performs a parallel scan of an entire table or secondary index. You specify a number of logical segments for the table, along with a scan expression to filter the results. The parallelScan divides the scan task among multiple workers, one for each logical segment; the workers process the data in parallel and return the results.

The following Java code example performs a parallel scan on the Product table.

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

For a Java code example illustrating the usage of parallelScan, see DynamoDBMapper Query and scan operations.

batchSave

Saves objects to one or more tables using one or more calls to the AmazonDynamoDB.batchWriteItem method. This method does not provide transaction guarantees.

The following Java code saves two items (books) to the ProductCatalog table.

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

Retrieves multiple items from one or more tables using their primary keys.

The following Java code retrieves two items from two different tables.

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

Deletes objects from one or more tables using one or more calls to the AmazonDynamoDB.batchWriteItem method. This method does not provide transaction guarantees.

The following Java code deletes two items (books) from the ProductCatalog table.

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

batchWrite

Saves objects to and deletes objects from one or more tables using one or more calls to the AmazonDynamoDB.batchWriteItem method. This method does not provide transaction guarantees or support versioning (conditional puts or deletes).

The following Java code writes a new item to the Forum table, writes a new item to the Thread table, and deletes an item from the ProductCatalog table.

// 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

Saves objects to and deletes objects from one or more tables using one call to the AmazonDynamoDB.transactWriteItems method.

For a list of transaction-specific exceptions, see TransactWriteItems errors.

For more information about DynamoDB transactions and the provided atomicity, consistency, isolation, and durability (ACID) guarantees see Amazon DynamoDB Transactions.

Note

This method does not support the following:

The following Java code writes a new item to each of the Forum and Thread tables, transactionally.

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

Loads objects from one or more tables using one call to the AmazonDynamoDB.transactGetItems method.

For a list of transaction-specific exceptions, see TransactGetItems errors.

For more information about DynamoDB transactions and the provided atomicity, consistency, isolation, and durability (ACID) guarantees see Amazon DynamoDB Transactions.

The following Java code loads one item from each of the Forum and Thread tables, transactionally.

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

Evaluates the specified scan expression and returns the count of matching items. No item data is returned.

generateCreateTableRequest

Parses a POJO class that represents a DynamoDB table, and returns a CreateTableRequest for that table.

Creates a link to an object in Amazon S3. You must specify a bucket name and a key name, which uniquely identifies the object in the bucket.

To use createS3Link, your mapper class must define getter and setter methods. The following code example illustrates this by adding a new attribute and getter/setter methods to the CatalogItem class.

@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; } ... }

The following Java code defines a new item to be written to the Product table. The item includes a link to a product image; the image data is uploaded to 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);

The S3Link class provides many other methods for manipulating objects in Amazon S3. For more information, see the Javadocs for S3Link.

getS3ClientCache

Returns the underlying S3ClientCache for accessing Amazon S3. An S3ClientCache is a smart Map for AmazonS3Client objects. If you have multiple clients, an S3ClientCache can help you keep the clients organized by AWS Region, and can create new Amazon S3 clients on demand.