Amazon DynamoDB
Developer Guide (API Version 2012-08-10)
« PreviousNext »
View the PDF for this guide.Go to the AWS Discussion Forum for this product.Go to the Kindle Store to download this guide in Kindle format.Did this page help you?  Yes | No |  Tell us about it...

Using the Object Persistence Model with Amazon DynamoDB

The AWS SDK for Java provides a high-level API, the Object Persistence model, which enables you to map your client-side classes to the Amazon DynamoDB tables. The individual object instances then map to items in a table. The Object Persistence model provides the DynamoDBMapper class that provides an entry point to Amazon DynamoDB. This class provides you with a connection to the Amazon DynamoDB database and enables you to access your tables, perform various create, read, update and delete (CRUD) operations, and execute queries.

To map your classes to tables, the Object Persistence model provides a set of annotation types.

Note

The Object Persistence model enables you to perform data operations such as save items, update items, delete items, and query the tables. However, the model does not provide API to create, update, or delete tables. Only the low-level API enables you to create, update, and delete tables. For more information, see Working with Tables Using the AWS SDK for Java Low-Level API.

For example, consider a ProductCatalog table that has Id as the hash primary key.

ProductCatalog(Id, ...)

You can map a class in your client application to the ProductCatalog table as shown in the following Java code example. The code snippet defines a CatalogItem class and uses the annotations that are defined by Amazon DynamoDB to establish the mapping.

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

In the preceding code snippet, the @DynamoDBTable annotation type maps the CatalogItem class to the ProductCatalog table. You can store individual class instances as items in the table. In the class definition, the @DynamoDBHashKey annotation type maps the Id property to the primary key.

By default, the class properties map to the same name attributes in the table. The properties Title and ISBN map to the same name attributes in the table. If you define a class property name that does not match a corresponding item attribute name, then you must explicitly add the @DynamoDBAttribute annotation type to specify the mapping. In the preceding example, the @DynamoDBAttribute annotation type is added to each property to ensure that the property names match exactly with the tables created in the Getting Started section, and to be consistent with the attribute names used in other code examples in this guide.

Your class definition can have properties that don't map to any attributes in the table. You identify these properties by adding the @DynamoDBIgnore annotation type. In the preceding example, the SomeProp property is marked with the @DynamoDBIgnore annotation type. When you upload a CatalogItem instance to the table, the DynamoDBContext does not include SomeProp property and also it does not return this attribute when you retrieve an item from the table.

In addition to mapping properties of the Java types such as integer and string, you can use the Object Persistence model to map any arbitrary data as long as you provide an appropriate converter to map it to the Amazon DynamoDB types. To learn about mapping arbitrary types, see Mapping Arbitrary Data Using the AWS SDK for Java Object Persistence Model.

The Object Persistence model also supports optimistic locking using a version field in the class. The @DynamoDBVersionAttribute annotation type on a property identifies it as a version field. For more information about using versioning, see Optimistic Locking With Version Number Using the AWS SDK for Java Object Persistence Model.

Amazon DynamoDB Annotations

The following table lists the annotations that Amazon DynamoDB offers for you to map your classes and properties to tables and attributes.

Note

In the following table, only the DynamoDBTable and the DynamoDBHashKey are the required tags.

Declarative Tag (Annotation)Description
@DynamoDBTable

Identifies the target table in Amazon DynamoDB. For example, the following Java code snippet defines a class Developer and maps it to the People table in Amazon DynamoDB.

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

This annotation can be inherited or overridden.

  • The @DynamoDBTable annotation can be inherited. Any new class that inherits from the Developer class also maps to the People table. For example, assume that you create a Lead class that inherits from the Developer class. Because you mapped the Developer class to the People table, the Lead class objects are also stored in the same table.

  • The @DynamoDBTable can also be overridden. Any new class that inherits from the Developer class by default maps to the same People table. However, you can override this default mapping. For example, if you create a class that inherits from the Developer class, you can explicitly map it to another table by adding the @DynamoDBTable annotation as shown in the following Java code snippet.

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

@DynamoDBIgnore

Indicates to the DynamoDBMapper instance that the associated property should be ignored. When saving data to the table, the DynamoDBMapper does not save this property to the table.

@DynamoDBAttribute

Maps a property to a table attribute. By default, each class property maps to an item attribute with the same name. However, if the names are not the same, using this tag you can map a property to the attribute. In the following Java snippet, the DynamoDBAttribute maps the BookAuthors property to the Authors attribute name in the table.

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

The DynamoDBMapper uses Authors as the attribute name when saving the object to the table.

@DynamoDBHashKey

Maps a class property to the hash attribute of the table. The property must be one of the supported String or Numeric type and cannot be a collection type.

Assume that you have a table, ProductCatalog, that has Id as the primary key. The following Java code snippet defines a CatalogItem class and maps its Id property to the primary key of the ProductCatalog table using the @DynamoDBHashKey tag.

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

Maps a class property to the range key attribute of the table. If the primary key is made of both the hash and range key attributes, you can use this tag to map your class field to the range attribute. For example, assume that you have a Reply table that stores replies for forum threads. Each thread can have many replies. So the primary key of this table is both the ThreadId and ReplyDateTime. The ThreadId is the hash attribute and ReplyDateTime is the range attribute. The following Java code snippet defines a Reply class and maps it to the Reply table. It uses both the @DynamoDBHashKey and @DynamoDBRangeKey tags to identify class properties that map to the primary key.

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

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

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

   // Additional properties go here. 
}
@DynamoDBAutoGeneratedKey

Marks a hash key or range key property as being auto-generated. The Object Persistence Model will generate a random UUID when saving these attributes. Only String properties can be marked as auto-generated keys.

The following snippet demonstrates using auto-generated keys.

@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 String 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());
    }
}                         
@DynamoDBVersionAttributeIdentifies a class property for storing an optimistic locking version number. DynamoDBMapper assigns a version number to this property when it saves a new item, and increments it each time you update the item. Only number scalar types are supported. For more information about data type, see Amazon DynamoDB Data Types. For more information about versioning, see Optimistic Locking With Version Number Using the AWS SDK for Java Object Persistence Model.

DynamoDBMapper Class

The DynamoDBMapper class is the entry point to Amazon DynamoDB. It provides a connection to Amazon DynamoDB and enables you to access your data in various tables, perform various CRUD operations on items, and execute queries and scans against tables. This class provides the following key operations for you to work with Amazon DynamoDB.

MethodDescription
save

Saves the specified object to the table. The object that you wish 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. String hash and range keys annotated with @DynamoDBAutoGeneratedKey are given a random universally unique identifier (UUID) if left uninitialized. Version fields annotated with @DynamoDBVersionAttribute will be 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.

mapper.save(obj, new DynamoDBMapperConfig(DynamoDBMapperConfig.SaveBehavior.CLOBBER));

If you have versioning enabled, then 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 Using the AWS SDK for Java Object Persistence Model.

load

Retrieves an item from a table. You must provide the primary key of the item that you wish 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.

CatalogItem item = mapper.load(CatalogItem.class, item.getId(), 
                new DynamoDBMapperConfig(DynamoDBMapperConfig.ConsistentReads.CONSISTENT)); 

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

delete

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

If you have versioning enabled, then 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 Using the AWS SDK for Java Object Persistence Model.

query

Enables the querying of a table. You can query a table only if its primary key is made of both a hash and a range attribute. This method requires you to provide a hash attribute value and a query filter that is applied on the range attribute. 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 0 or more replies. The primary key of the Reply table consists of the Id and ReplyDateTime fields, where Id is the hash attribute and ReplyDateTime is the range attribute of the primary key.

Reply ( Id, ReplyDateTime, ... )

Now, assume that you created an Object Persistence model that includes a Reply class that maps to the table.

The following Java code snippet uses the DynamoDBMapper instance to query the table to find all replies in the past two weeks for a specific thread subject.

String forumName = "Amazon DynamoDB";
String forumSubject = "DynamoDB Thread 1";
String hashKey = 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);


Condition rangeKeyCondition = new Condition()
    .withComparisonOperator(ComparisonOperator.GT.toString())
    .withAttributeValueList(new AttributeValue().withS(twoWeeksAgoStr.toString()));

Reply replyKey = new Reply();
replyKey.setId(hashKey);

DynamoDBQueryExpression<Reply> queryExpression = new DynamoDBQueryExpression<Reply>()
    .withHashKeyValues(replyKey)
    .withRangeKeyCondition("ReplyDateTime", rangeKeyCondition);

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

The query returns a collection of Reply objects.

Note

If your table's primary key is made of only a hash attribute, then you cannot use the query method. Instead, you can use the load method and provide the hash attribute to retrieve the item.

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, you only need to iterate over the latestReplies collection.

scan

Scans an entire table. You can specify optional condition filters items based on one or more Condition instances, and you can specify a filter expression for any item attributes.

Assume that you have a table, Thread, that stores forum thread information including Subject (part of the composite primary key) and if the thread is answered.

Thread ( ForumName, Subject, ..., Answered )

If you have an Object Persistence model for this table, then you can use the DynamoDBContext to scan the table. For example, the following Java code snippet filters the Thread table to retrieve all the unanswered threads. The scan condition identifies the attribute and a condition.

DynamoDBScanExpression scanExpression = new DynamoDBScanExpression();
        
Map<String, Condition> scanFilter = new HashMap<String, Condition>();
Condition scanCondition = new Condition()
    .withComparisonOperator(ComparisonOperator.EQ.toString())
    .withAttributeValueList(new AttributeValue().withN("0"));

scanFilter.put("Answered", scanCondition);
        
scanExpression.setScanFilter(scanFilter);

List<Thread> unansweredThreads = mapper.scan(Thread.class, scanExpression);

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, you only need to iterate over the unansweredThreads collection.

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

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 snippet saves two items (books) to the ProductCatalog table.

Book book1 = new Book();
book1.id = 901;
book1.productCategory = "Book";
book1.title = "Book 901 Title";

Book book2 = new Book();
book2.id = 902;
book2.productCategory = "Book";
book2.title = "Book 902 Title";

mapper.batchSave(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 snippet 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.name = "Test BatchWrite Forum";
        
// Create a Thread item to save
Thread threadItem = new Thread();
threadItem.forumName = "AmazonDynamoDB";
threadItem.subject = "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);

count

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

marshallIntoObject

Utility method to transform a result from the low-level API into a domain object.

Specifying Optional Configuration Information to the DynamoDBMapper

The Object Persistence model provides the DynamoDBMapper for you to communicate with Amazon DynamoDB. When you create a mapper instance, you can specify optional configuration information using the DynamoDBMapperConfig class. You can specify the following arguments for an instance of DynamoDBMapperConfig:

  • A DynamoDBMapperConfig.SaveBehavior enumeration value - Specifies how DynamoDBMapper should deal with attributes during save operations. Specifying the value DynamoDBMapperConfig.SaveBehavior.UPDATE will not affect unmodeled attributes on a save operation. All modeled attributes are updated. Primitive number types (byte, int, long) are set to 0. Object types are set to null. Specifying the value DynamoDBMapperConfig.SaveBehavior.CLOBBER will clear and replace all attributes, included unmodeled ones, (delete and recreate) on save. Versioned field constraints will also be disregarded. If you do not specify configuration information for your mapper instance, then the default DynamoDBMapperConfig.SaveBehavior.UPDATE is used.

  • A DynamoDBMapperConfig.ConsistentReads enumeration value - If you specify DynamoDBMapperConfig.ConsistentReads.CONSISTENT , then the DynamoDBMapper includes a strongly consistent read request. When retrieving data using the load, query, or scan operations, you can optionally add this parameter. Strongly consistent reads have implications for performance and billing; see the product detail page for more information. Instead of strongly consistent reads, you can specify eventual consistency with the DynamoDBMapperConfig.ConsistentReads.EVENTUAL enumeration value. If you do not specify configuration information for your mapper instance, then the default DynamoDBMapperConfig.ConsistentReads.EVENTUAL is used.

  • A DynamoDBMapperConfig.TableNameOverride object - Instructs DynamoDBMapper to ignore the table name specified by a class's DynamoDBTable annotation and use a different one you supply. This is useful when partitioning your data into multiple tables at run time.

You can override the default configuration object for DynamoDBMapper per operation, as needed.

Supported Data Types

This section describes the supported primitive Java data types, collections, and arbitrary data types.

Amazon DynamoDB supports the following primitive data types and primitive wrapper classes.

  • String

  • Boolean, boolean

  • Byte, byte

  • Date (as ISO8601 millisecond-precision string, shifted to UTC)

  • Calendar (as ISO8601 millisecond-precision string, shifted to UTC)

  • Long, long

  • Integer, int

  • Double, double

  • Float, float

  • BigDecimal

  • BigInteger

Amazon DynamoDB supports the Java Set collection types. If your mapped collection property is not a Set, then an exception is thrown.

The following table summarizes how the preceding Java types map to the Amazon DynamoDB types.

Java typeAmazon DynamoDB type

All number types

N (number type)

Strings

S (string type)

booleanN (number type), 0 or 1.
ByteBufferB (binary type)
DateS (string type). The Date values are stored as ISO-8601 formatted strings.
Set collection typesSS (string set) type, NS (number set) type, or BS (binary set) type.

In addition, Amazon DynamoDB supports arbitrary data types. For example, you can define your own complex types on the client. You use the DynamoDBMarshaller class and the @DynamoDBMarhsalling annotation type for the complex type to describe the mapping (Mapping Arbitrary Data Using the AWS SDK for Java Object Persistence Model).