Writing less code when using the AWS SDK for Java

Today we have a guest post by David Yanacek from the Amazon DynamoDB team.


The AWS SDK for Java provides a convenient set of methods for building request objects. This set of methods, known as a fluent interface, can save you from repeatedly retyping the request variable name, and can even make your code more readable. But what about maps? Services like Amazon DynamoDB use java.util.Map objects throughout their API, which do not lend themselves naturally to this builder pattern. Fortunately, the Google Guava open source library offers some classes that make it possible to build maps in a way that is compatible with the SDK’s fluent interface. In this post, we show how using Google Guava’s collection classes can make it easier to use services like Amazon DynamoDB with the low-level Java SDK. 

First, let’s look at some code that uses the bean interface—not the fluent interface—for making a PutItem call to DynamoDB. This example puts an item into the “ProductCatalog” described in the Amazon DynamoDB Developer Guide, using a conditional write so that DynamoDB makes the change only if the item already exists and has the price of “26.00”. The example also asks DynamoDB to return the previous copy of the item.

// Construct the new item to put
Map<String, AttributeValue> item = new HashMap<String, AttributeValue>();

AttributeValue id = new AttributeValue();
id.setN("104");
item.put("Id", id);

AttributeValue title = new AttributeValue("Book 104 Title");
item.put("Title", title);

AttributeValue isbn = new AttributeValue("111-1111111111");
item.put("ISBN", isbn);

AttributeValue price = new AttributeValue();
price.setN("25");
item.put("Price", price);

List<String> authorList = new ArrayList<String>();
authorList.add("Bob");
authorList.add("Alice");
AttributeValue authors = new AttributeValue();
authors.setSS(authorList);
item.put("Authors", authors);

// Construct a map of expected current values for the conditional write
Map<String, ExpectedAttributeValue> expected = new HashMap<String, ExpectedAttributeValue>();

ExpectedAttributeValue expectedPrice = new ExpectedAttributeValue();
AttributeValue currentPrice = new AttributeValue();
currentPrice.setN("26");
expectedPrice.setValue(currentPrice);
expected.put("Price", expectedPrice);

// Construct the request
PutItemRequest putItemRequest = new PutItemRequest();
putItemRequest.setTableName("ProductCatalog");
putItemRequest.setItem(item);
putItemRequest.setExpected(expected);
putItemRequest.setReturnValues(ReturnValue.ALL_OLD);

// Make the request
PutItemResult result = dynamodb.putItem(putItemRequest);

That’s a lot of code for doing something as simple as putting an item into a DynamoDB table. Let’s take that same example and switch it over to using the built-in fluent style interface:

// Construct the new item to put
Map<String, AttributeValue> item = new HashMap<String, AttributeValue>();
item.put("Id", new AttributeValue().withN("104"));
item.put("Title", new AttributeValue("Book 104 Title"));
item.put("ISBN", new AttributeValue("111-1111111111"));
item.put("Price", new AttributeValue().withN("25"));
item.put("Authors", new AttributeValue()
    .withSS(Arrays.asList("Author1", "Author2")));

// Construct a map of expected current values for the conditional write
Map<String, ExpectedAttributeValue> expected = new HashMap<String, ExpectedAttributeValue>();
expected.put("Price", new ExpectedAttributeValue()
    .withValue(new AttributeValue().withN("26")));

// Make the request 
PutItemResult result = dynamodb.putItem(new PutItemRequest()
    .withTableName("ProductCatalog")
    .withItem(item)
    .withExpected(expected)
    .withReturnValues(ReturnValue.ALL_OLD));
 

That’s a lot shorter. You may have noticed that this code also used a method Arrays.asList(), which ships with the JDK, for constructing the authors list. Wouldn’t it be nice if the JDK came with something like that for building maps? Fortunately, Google Guava exposes several Map subclasses, and provides simple Builder utilities for each. Let’s use ImmutableMap.Builder to make the code even more compact:

// Make the request
PutItemResult result = dynamodb.putItem(new PutItemRequest()
    .withTableName("ProductCatalog")
    .withItem(new ImmutableMap.Builder<String, AttributeValue>()
        .put("Id", new AttributeValue().withN("104"))
        .put("Title", new AttributeValue("Book 104 Title"))
        .put("ISBN", new AttributeValue("111-1111111111"))
        .put("Price", new AttributeValue().withN("25"))
        .put("Authors", new AttributeValue()
            .withSS(Arrays.asList("Author1", "Author2")))
        .build())
    .withExpected(new ImmutableMap.Builder<String, ExpectedAttributeValue>()
        .put("Price", new ExpectedAttributeValue()
            .withValue(new AttributeValue().withN("26")))
        .build())
    .withReturnValues(ReturnValue.ALL_OLD));

 

And that’s it! We hope this approach saves you some typing and makes your code more readable. And if you want even less code, take a look at the DynamoDBMapper class, which allows you to interact with DynamoDB with your own objects directly. For more details, see the earlier blog posts Storing Java objects in Amazon DynamoDB tables and Using Custom Marshallers to Store Complex Objects in Amazon DynamoDB, or the topic Using the Object Persistence Model with Amazon DynamoDB in the Amazon DynamoDB Developer Guide.

Comments