Has anyone made the change to the new namespaces (com.amazonaws.services.dynamodbv2
) and interfaces for DynamoDB in the AWS Java SDK 1.4.2 (and later)? The release of Local Secondary Indices apparently necessitated breaking changes as per the 1.4.2 release notes.
Has anyone found a guide detailing what changed and what needs to happen to migrate existing code? I am trying to decide when is best to make this change for an existing code base.
The new dynamodbv2 namespace of DynamoDB introduces the following incompatible changes (in that they are not simply additive, and require code changes to switch to the new namespace):
It is possible to migrate your code to the new Java API incrementally, if desired. If you plan to add functionality to your code that queries Local Secondary Indexes, or creates tables with local secondary indexes, you will need to use the new API for that part of your code.
If you create a table with Local Secondary Indexes with the new API, you can still use your existing code in the dynamodb namespace to perform all of the existing operations on that table. As an example, PutItem with the dynamodb namespace client will work against tables created using the dynamodbv2 client, as well as the other way around.
DynamoDB AWS Java 1.4.1 => 1.4.2 (non-exhaustive) migration steps
Well, I bit the bullet and did it. Here's my experience.
First, change the DynamoDB namespace:
com.amazonaws.services.dynamodb
=> com.amazonaws.services.dynamodbv2
The first thing you notice is there are types missing. The most major is Key is gone. Good riddance as it was too generic of a name.
It is now replaced with Map which makes sense as keys become a lot more malleable with Local Secondary Indexes (LSI).
Unfortunately, working with maps and generics in general sucks in Java (see Bonus at bottom).
Gone are the fluent interfaces of withHashKeyElement
/ withRangeKeyElement
.
Next, very carefully find/replace the DynamoDB types which were replaced by generic collections:
com.amazonaws.services.dynamodb.model.Key
=> Map<String, AttributeValue>
com.amazonaws.services.dynamodb.model.BatchResponse
=> List<Map<String, AttributeValue>>
com.amazonaws.services.dynamodb.model.KeySchema
=> List<KeySchemaElement>
Next, find everything that broke. This is a very manual process that requires knowing your code base and the SDK quite well. Specifically, you must know your key schema very well as it's all strings from here on. Luckily in my situation, the first two tasks were about 90% of the changes:
new Map<String, AttributeValue>
, it is an indication that Key
was formerly there.QueryRequest
merged withHashKeyValue(AttributeValue)
withRangeKeyCondition(Condition)
into the mega withKeyConditions(Map<String,Condition>)
This method is the core change for LSI lets you specify things other than hash/range. What it accepts is more restrictive than the interface, but this is logical when you consider DynamoDB only lets you query indexed attributes.DynamoDBQueryExpression
became generic and changed its interface (not sure why).KeySchemaElement
no longer needs AttributeType
, but now needs KeyType
Finally, Compile and regression test your entire stack.
P.S. While I was performing all this, version 1.4.4.1 was just released to Maven Central.
Bonus
Since Maps are the common solution to Java's lack of loosely typed classes, they are everywhere. A little helper lib can really go a long way toward building these less verbosely. Here are some bits of my helper:
public class MakeJavaSuckLess { // TODO: find a shorter class name
public static final float MAX_LOAD_FACTOR = 1.0f;
/**
* Builds a mutable Map from an interlaced sequence of key-value pairs
* where keys are strings and values are VType objects
* @param pairs
* @return
*/
public static <VType> Map<String, VType> asMap(Object... pairs) {
return mergeMaps(null, pairs);
}
/**
* Builds a mutable Map from an interlaced sequence of key-value pairs
* where keys are strings and values are VType objects
* @param pairs
* @return
*/
public static <VType> Map<String, VType> mergeMaps(Map<String, VType> map, Object... pairs) {
final int length = (pairs != null) ? pairs.length/2 : 0;
if (map == null) {
// max out the load factor since most of these don't change
map = new HashMap<String, VType>(length, MAX_LOAD_FACTOR);
}
for (int i=0; i<length; i++) {
String key = asString(pairs[2*i]);
@SuppressWarnings("unchecked")
VType value = (VType)pairs[2*i+1];
map.put(key, value);
}
return map;
}
}
Now creating your DynamoDB keys is slightly less ugly:
Map<String, AttributeValue> key = MakeJavaSuckLess.asMap("hashKey", new AttributeValue("hashVal"), "rangeKey", new AttributeValue("rangeVal"));
Type erasure in Java's generics makes this again uglier in places. You will sometimes need to explicitly specify VType
:
new GetItemRequest().withKey(MakeJavaSuckLess.<AttributeValue>asMap("hashKey", new AttributeValue("hashVal"), "rangeKey", new AttributeValue("rangeVal")));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With