Tuesday, November 19, 2013

Use DynamoDBMapper to Work with Amazon DynamoDB from Java

Articles & Tutorials>Use DynamoDBMapper to Work with Amazon DynamoDB from Java
Tips on using the DynamoDBMapper utility, part of the AWS SDK for Java, to model objects in Amazon DynamoDB.

Details

Submitted By:Zach Musgrave
AWS Products Used:AWS SDK for Java, Amazon DynamoDB
Language(s):English
Created On:October 2, 2012 9:36 PM GMT
Last Updated:August 21, 2013 5:42 PM GMT

Use DynamoDBMapper to Work with Amazon DynamoDB from Java

DynamoDBMapper, a new feature of the AWS SDK for Java, makes it easier to access Amazon DynamoDB tables from Java. Sometimes, working with databases from an object-oriented language like Java can be a frustrating experience. You often want to represent the rows in your database tables as instances of your domain classes, but you don't want to write a bunch of boilerplate code to transform database results into objects and vice versa. For traditional database servers such as MySQL and Oracle, you can use tools such as Hibernate to automate this aspect of development, but for newer NoSQL databases, you often have to either roll your own framework or rely on the community to provide one. DynamoDBMapper solves this problem for Amazon DynamoDB.

The Problem: Re-inventing the wheel

A typical Amazon DynamoDB response looks something like this:
{
  "Item":
    {"friends":{"SS":["Lynda, Aaron"]},
     "status":{"S":"online"},
     "id":{"N":"123456"}
    },
  "ConsumedCapacityUnits": 1
 }
Let's say that you want to map these attributes to a domain class, so you write a simple Plain Old Java Object (POJO).
public class User {
    
    private Integer id;
    private Set friends;
    private String status;
    
    public Integer getId() { return id; }    
    public void setId(Integer id) { this.id = id; }
    
    public Set getFriends() { return friends; }    
    public void setFriends(Set friends) { this.friends = friends; }
    
    public String getStatus() { return status; }        
    public void setStatus(String status) { this.status = status; }    
}
 
Then, to get instances of this class into and out of the database, you have to write the following code:
public void save(AmazonDynamoDB dynamo) {
    Map item = new HashMap();
    item.put("id", new AttributeValue().withN(String.valueOf(getId())));
    if ( getFriends() != null && !getFriends().isEmpty() )
        item.put("friends", new AttributeValue().withSS(getFriends()));
    if ( getStatus() != null && getStatus().length() > 0 )
        item.put("status", new AttributeValue().withS(getStatus()));
    dynamo.putItem(new PutItemRequest().withTableName("users").withItem(item));
}

public static User load(AmazonDynamoDB dynamo, Integer id) {
    GetItemResult result = dynamo.getItem(new GetItemRequest().withTableName("mytable").withKey(
            new Key().withHashKeyElement(new AttributeValue().withN(String.valueOf(id)))));
    if ( result.getItem() == null )
        return null;

    User user = new User();
    user.setId(id);
    if ( result.getItem().get("status") != null )
        user.setStatus(result.getItem().get("status").getS());
    if ( result.getItem().get("friends") != null )
        user.setFriends(new HashSet(result.getItem().get("friends").getSS()));

    return user;
}
This process is time-consuming and error prone, especially as the number of domain classes grows. You shouldn't have to write this kind of code to interact with a database.

The Solution: Introducing DynamoDBMapper

The AWS SDK for Java provides DynamoDBMapper, a high-level interface that automates the process of getting your objects into Amazon DynamoDB and back out again. To use DynamoDBMapper, annotate your class to tell Amazon DynamoDB how your class properties map to Amazon DynamoDB attributes. For the example above, this would look like:
@DynamoDBTable(tableName = "users")
public class User {
    
    private Integer id;
    private Set friends;
    private String status;
    
    @DynamoDBHashKey
    public Integer getId() { return id; }
    public void setId(Integer id) { this.id = id; }
    
    @DynamoDBAttribute
    public Set getFriends() { return friends; }
    public void setFriends(Set friends) { this.friends = friends; }
    
    @DynamoDBAttribute
    public String getStatus() { return status; }
    public void setStatus(String status) { this.status = status; }
}

Now you can get your objects into and out of Amazon DynamoDB with just a few lines of code:
User newUser = new User();
newUser.setId(100);
newUser.setFriends(new HashSet(Arrays.asList("Frank", "Jeremy", "Linda")));
newUser.setStatus("pending");
AmazonDynamoDB dynamo = new AmazonDynamoDBClient(awsCredentials);
DynamoDBMapper mapper = new DynamoDBMapper(dynamo);

// save a new item
mapper.save(newUser);

...
 
// update the existing item
newUser.setStatus("active");
newUser.getFriends().remove("Jeremy");
mapper.save(newUser);

...

// delete the item
User user = mapper.load(User.class, new Integer(100));
mapper.delete(user);

Advanced Features

DynamoDBMapper supports a number of features to make it easier to work with Amazon DynamoDB.

Auto-generated Keys

If we modify our previous example to change the key property to a String, we can tell DynamoDBMapper to automatically generate a UUID on our behalf when we save a new item to Amazon DynamoDB.
@DynamoDBTable(tableName = "users")
public class User {
    
    private String id;
   
    ...
        
    @DynamoDBHashKey
    @DynamoDBAutoGeneratedKey
    public String getId() { return id; }    
    public void setId(String id) { this.id = id; }
    
    ...
}

User newUser = new User();
newUser.setFriends(new HashSet(Arrays.asList("Frank", "Jeremy", "Linda")));
newUser.setStatus("pending");
mapper.save(newUser);
System.out.println(newUser.getId());  // 849e8ab0-5438-11e1-b86c-0800200c9a66

Optimistic Locking

For highly concurrent systems, DynamoDBMapper helps you implement optimistic locking using Amazon DynamoDB's updateItem API with an Expected clause. This allows you to detect when another process has modified an item you're working on and allows you to retry the transaction with the updated data.
@DynamoDBTable(tableName = "users")
public class User {
    
    private Integer version;
   
    ...
        
    @DynamoDBVersionAttribute
    public Integer getVersion() { return version; }    
    public void setVersion(Integer version) { this.version = version; }
    
    ...
}

User user = mapper.load(User.class, userId);
System.out.println(user.getVersion()); // 8
user.setStatus("pending");
mapper.save(user);
System.out.println(user.getVersion()); // 9

...

public void addFriend(String userId, String friend) {
    User user = mapper.load(User.class, userId);
    user.getFriends().add(friend);
    try {
        mapper.save(user);
    } catch (ConditionalCheckFailedException e) {
        // Another process updated this item after we loaded it, so try again with the newest data
        addFriend(userId, friend);
    }
}

Custom Marshallers

Out of the box, DynamoDBMapper only knows how to store numeric, byte array, and string types. However, you can easily write an adapter to store any type that can be represented as a String. For example, if you want to store a POJO inside your POJO, DynamoDBMapper provides a generic JSON marshaller that stores nested objects as their basic JSON representations.
public class Profile {
    Date dateOfBirth;
    String favoriteColor;
    
    public Date getDateOfBirth() { return dateOfBirth; }    
    public void setDateOfBirth(Date dateOfBirth) { this.dateOfBirth = dateOfBirth; }
    
    public String getFavoriteColor() { return favoriteColor; }    
    public void setFavoriteColor(String favoriteColor) { this.favoriteColor = favoriteColor; }        
}

public class ProfileMarshaller extends JsonMarshaller { }

@DynamoDBTable(tableName = "users")
public class User {
    
    private Profile profile;
   
    ...
        
    @DynamoDBAttribute
    @DynamoDBMarshalling(marshallerClass = ProfileMarshaller.class)
    public Profile getProfile() { return profile; }
    public void setProfile(Profile profile) { this.profile = profile; }
    
    ...
}

More Resources

To learn more about DynamoDBMapper, read Using the Object Persistence Model with Amazon DynamoDB. You can also visit the AWS forums to discuss DynamoDBMapper with other developers and get answers to your questions.

5 comments:

oakleyses said...

louis vuitton handbags, oakley sunglasses, louboutin, longchamp outlet, nike shoes, louis vuitton outlet stores, chanel handbags, burberry outlet, prada outlet, jordan shoes, tiffany and co, michael kors outlet, tory burch outlet, louis vuitton outlet, longchamp handbags, nike free, true religion jeans, michael kors outlet, kate spade outlet, polo ralph lauren outlet, tiffany and co, prada handbags, polo ralph lauren outlet, michael kors outlet, michael kors outlet, longchamp handbags, oakley sunglasses, ray ban sunglasses, kate spade handbags, burberry outlet, louis vuitton outlet, louboutin outlet, louboutin, coach factory outlet, air max, air max, coach outlet, gucci outlet, christian louboutin shoes, michael kors outlet, coach purses, ray ban sunglasses, michael kors outlet, louis vuitton, coach outlet store online, true religion jeans, oakley sunglasses cheap

oakleyses said...

ralph lauren, lululemon, air max, hollister, north face, nike air max, polo lacoste, vanessa bruno, timberland, vans pas cher, louboutin, louis vuitton, oakley pas cher, air max pas cher, nike roshe run, air max, true religion outlet, barbour, sac longchamp, air force, hollister, sac louis vuitton, nike free, polo ralph lauren, nike trainers, louis vuitton uk, nike roshe, sac hermes, longchamp, michael kors, sac burberry, sac guess, mulberry, new balance pas cher, converse pas cher, sac louis vuitton, hogan outlet, nike tn, north face, true religion outlet, ray ban pas cher, michael kors, air jordan, nike blazer, nike free pas cher, michael kors pas cher, abercrombie and fitch, ray ban sunglasses

oakleyses said...

mac cosmetics, mont blanc, marc jacobs, canada goose outlet, nike huarache, vans shoes, soccer jerseys, hollister, giuseppe zanotti, beats by dre, abercrombie and fitch, longchamp, insanity workout, celine handbags, bottega veneta, ghd, nfl jerseys, north face outlet, chi flat iron, ugg boots, birkin bag, ugg australia, canada goose, herve leger, ugg pas cher, rolex watches, valentino shoes, canada goose uk, canada goose, ferragamo shoes, canada goose, ugg boots, uggs outlet, north face jackets, soccer shoes, asics running shoes, new balance shoes, p90x, lululemon outlet, canada goose jackets, mcm handbags, instyler, babyliss pro, ugg, wedding dresses, jimmy choo outlet, reebok outlet, nike roshe run

oakleyses said...

parajumpers, karen millen, air max, converse, pandora charms, moncler, louboutin, moncler, links of london, lancel, juicy couture outlet, oakley, hollister, pandora charms, supra shoes, thomas sabo, canada goose, gucci, wedding dresses, timberland boots, swarovski crystal, air max, coach outlet store online, moncler, ray ban, canada goose, moncler, ugg, louis vuitton, swarovski, hollister, montre homme, moncler, hollister clothing store, ralph lauren, rolex watches, moncler outlet, moncler, iphone 6 cases, baseball bats, juicy couture outlet, toms shoes, vans, pandora jewelry, ugg, converse shoes

raybanoutlet001 said...

yeezy sneakers
basketball shoes
michael kors factory outlet
fitflops sale clearance
air jordan retro
links of london sale
cheap uggs
discount sunglasses
cheap nfl jerseys
true religion sale
michael kors handbags
http://www.raybanglasses.in.net
tiffany jewellery
ralph lauren polo shirts
nike dunks
tiffany online
oakley store online
oakley sunglasses,oakley outlet sunglasses
cheap real jordans
adidas nmd
ray ban uk,cheap ray ban sunglasses