Changes in the DynamoDB mapping/document APIs from version 1 to version 2
This topic details the changes in the Java SDK's high-level APIs for Amazon DynamoDB from version 1.x (v1) to the Amazon SDK for Java 2.x (v2). We first cover the object-to-table mapping API and then discuss the document API for working with JSON-style documents.
High-level changes
The names of the mapping client in each library differ in v1 and v2:
-
v1 - DynamoDBMapper
-
v2 - DynamoDB Enhanced Client
You interact with the two libraries in much the same way: you instantiate a mapper/client and then supply a Java POJO to APIs that read and write these items to DynamoDB tables. Both libraries also offer annotations for the class of the POJO to direct how the client handles the POJO.
Notable differences when you move to v2 include:
-
V2 and v1 use different method names for the low-level DynamoDB operations. For example:
v1 v2 load getItem save putItem batchLoad batchGetItem -
V2 offers multiple ways to define table schemas and map POJOs to tables. You can choose from the use of annotations or a schema generated from code using a builder. V2 also offers mutable and immutable versions of schemas.
-
With v2, you specifically create the table schema as one of the first steps, whereas in v1, the table schema is inferred from the annotated class as needed.
-
V2 includes the Document API client
in the enhanced client API, whereas v1 uses a separate API . -
All APIs are available in synchronous and asynchronous versions in v2.
See the DynamoDB mapping section in this guide for more detailed information on the v2 enhanced client.
Import dependencies
v1 | v2 |
---|---|
|
|
In v1, a single dependency includes both the low-level DynamoDB API and the
mapping/document API, whereas in v2, you use the dynamodb-enhanced
artifact
dependency to access the mapping/document API. The dynamodb-enhanced
module
contains a transitive dependency on the low-level dynamodb
module.
API changes
Create a client
Use case | v1 | v2 |
---|---|---|
Normal instantiation |
|
|
Minimal instantiation |
|
|
With attribute transformer* |
|
|
*Extensions in v2 correspond roughly to attribute transformers in v1. The Use extensions section contains more information on extensions in v2.
Establish mapping to DynamoDB table/index
In v1, you specify a DynamoDB table name through a bean annotation. In v2, a factory
method, table()
, produces an instance of DynamoDbTable
that
represents the remote DynamoDB table. The first parameter of the table()
method is the DynamoDB table name.
Use case | v1 | v2 |
---|---|---|
Map the Java POJO class to the DynamoDB table |
|
|
Map to a DynamoDB secondary index |
The section in the DynamoDB Developer Guide that discusses the v1 |
The Use secondary indices section in this guide provides more information. |
Table operations
This section describes operation APIs that differ between v1 and v2 for most standard use cases.
In v2, all operations that involve a single table are called on the
DynamoDbTable
instance, not on the enhanced client. The enhanced client
contains methods that can target multiple tables.
In the table named Table operations below, a POJO
instance is referred to as item
or as a specific type such as
customer1
. For the v2 examples the instances named, table
is the result of previously calling enhancedClient.table()
that returns a
reference to the DynamoDbTable
instance.
Note that most v2 operations can be called with a fluent consumer pattern even when not shown. For example,
Customer customer = table.getItem(r → r.key(key));
or
Customer customer = table.getItem(r → r.key(k -> k.partitionValue("id").sortValue("email")))
For v1 operations, the table contains some of the commonly used forms and not all
overloaded forms. For example, the load()
method has the following
overloads:
mapper.load(Customer.class, hashKey)
mapper.load(Customer.class, hashKey, rangeKey)
mapper.load(Customer.class, hashKey, config)
mapper.load(Customer.class, hashKey, rangeKey, config)
mapper.load(item)
mapper.load(item, config)
The table shows the commonly used forms:
mapper.load(item) mapper.load(item, config)
Table operations | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Use case | v1 | DynamoDB operation | v2 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Write a Java POJO to a DynamoDB table |
In v1, |
PutItem , UpdateItem |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Read an item from a DynamoDB table to a Java POJO |
|
GetItem |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Delete an item from a DynamoDB table |
|
DeleteItem |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Query a DynamoDB table or secondary index and return a paginated list |
|
Query |
Use the returned |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Query a DynamoDB table or secondary index and return a list |
|
Query |
Use the returned |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Scan a DynamoDB table or secondary index and return a paginated list |
|
Scan |
Use the returned |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Scan a DynamoDB table or secondary index and return a list |
|
Scan |
Use the returned |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Read multiple items from multiple tables in a batch |
|
BatchGetItem |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Write multiple items to multiple tables in a batch |
|
BatchWriteItem |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Delete multiple items from multiple tables in a batch |
|
BatchWriteItem |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Write/delete multiple items in a batch |
|
BatchWriteItem |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Carry out a transactional write |
|
TransactWriteItems |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Carry out a transactional read |
|
TransactGetItems |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Get count of matching items of a scan or query |
|
Query , Scan with
Select.COUNT |
Not supported | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Create a table in DynamoDB corresponding to the POJO class |
The previous statement generates a low-level create table request;
users must call |
CreateTable |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Perform a parallel scan in DynamoDB |
|
Scan with Segment and
TotalSegments parameters |
Users are required to handle the worker threads and call
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Integrate Amazon S3 with DynamoDB to store intelligent S3 links |
|
- |
Not supported because it couples Amazon S3 and DynamoDB. |
Map classes and properties
In both v1 and v2, you map classes to tables using bean-style annotations. V2 also offers other ways to define schemas for specific use cases, such as working with immutable classes.
Bean annotations
The following table shows the equivalent bean annotations for a specific use case
that are used in v1 and v2. A Customer
class scenario is used to illustrate
parameters.
Annotations—as well as classes and enumerations—in v2 follow camel case convention and use 'DynamoDb', not 'DynamoDB'.
Use case | v1 | v2 |
---|---|---|
Map class to table |
|
The table name is
defined when calling the DynamoDbEDnhancedClient#table()
method. |
Designate a class member as a table attribute |
|
|
Designate a class member is a hash/partition key |
|
|
Designate a class member is a range/sort key |
|
|
Designate a class member is a secondary index hash/partition key |
|
|
Designate a class member is a secondary index range/sort key |
|
|
Ignore this class member when mapping to a table |
|
|
Designate a class member as an auto-generated UUID key attribute |
|
The extension that provides this is not loaded by default; you must add the extension to client builder. |
Designate a class member as an auto-generated timestamp attribute |
|
The extension that provides this is not loaded by default; you must add the extension to client builder. |
Designate a class member as an auto-incremented version attribute |
|
The extension that provides this is auto-loaded. |
Designate a class member as requiring a custom conversion |
|
|
Designate a class member to be stored as a different attribute type |
|
No equivalent |
Designate a class that can be serialized to a DynamoDB document (JSON-style document) or sub-document |
|
No direct equivalent annotation. Use the Enhanced Document API. |
V2 additional annotations
Use case | v1 | v2 |
---|---|---|
Designate a class member not to be stored as a NULL attribute if the Java value is null | N/A |
|
Designate a class member to be an empty object if all attributes are null | N/A |
|
Designate special update action for a class member | N/A |
|
Designate an immutable class | N/A |
|
Designate a class member as an auto-incremented counter attribute | N/A |
The extension that provides this functionality is auto-loaded. |
Configuration
In v1, you generally control specific behaviors by using an instance of
DynamoDBMapperConfig
. You can supply the configuration object either when
you create the mapper or when you make a request. In v2, configuration is specific to the
request object for the operation.
Use case | v1 | Default in v1 | v2 |
---|---|---|---|
|
|||
Batch load retry strategy |
|
retry failed items | |
Batch write retry strategy |
|
retry failed items | |
Consistent reads |
|
EVENTUAL |
By default, consistent reads is false for read operations. Override with
.consistentRead(true) on the request object. |
Conversion schema with sets of marshallers/unmarshallers |
Static implementations provide backwards compatibility with older versions. |
V2_COMPATIBLE |
Not applicable. This is a legacy feature that refers to how the earliest versions of DynamoDB (v1) stored data types, and this behavior will not be preserved in the enhanced client. An example of behavior in DynamoDB v1 is storing booleans as Number instead of as Boolean. |
Table names |
Static implementations provide backwards compatibility with older versions |
use annotation or guess from class |
The table name is defined when calling the
|
Pagination load strategy |
Options are: LAZY_ |
LAZY_LOADING |
Iteration only is the default. The other v1 options are not supported. |
Request metric collection |
|
null |
Use metricPublisher() in
ClientOverrideConfiguration when building the standard DynamoDB
client. |
Save behavior |
Options are |
UPDATE |
In v2, you call
|
Type converter factory |
|
standard type converters |
Set on the bean by using
|
Per-operation configuration
In v1, some operations, such as query()
, are highly configurable through
an “expression” object submitted to the operation. For example:
DynamoDBQueryExpression<Customer> emailBwQueryExpr = new DynamoDBQueryExpression<Customer>() .withRangeKeyCondition("Email", new Condition() .withComparisonOperator(ComparisonOperator.BEGINS_WITH) .withAttributeValueList( new AttributeValue().withS("my"))); mapper.query(Customer.class, emailBwQueryExpr);
In v2, instead of using a configuration object, you set parameters on the request object by using a builder. For example:
QueryEnhancedRequest emailBw = QueryEnhancedRequest.builder() .queryConditional(QueryConditional .sortBeginsWith(kb -> kb .sortValue("my"))).build(); customerTable.query(emailBw);
Conditionals
In v2, conditional and filtering expressions are expressed using an
Expression
object, which encapsulates the condition and the mapping of
names and filters.
Use case | Operations | v1 | v2 |
---|---|---|---|
Expected attribute conditions | save(), delete(), query(), scan() |
|
Deprecated; use ConditionExpression instead. |
Condition expression | delete() |
|
|
Filter expression | query(), scan() |
|
|
Condition expression for query | query() |
|
|
Type conversion
Default converters
In v2, the SDK provides a set of default converters for all common types. You can
change type converters both at the overall provider level as well as for a single
attribute. You can find a list of the available converters in the AttributeConverter
Set a custom converter for an attribute
In v1, you can annotate a getter method with @DynamoDBTypeConverted
to
specify the class that converts between the Java attribute type and a DynamoDB attribute
type. For instance, a CurrencyFormatConverter
that converts between a Java
Currency
type and DynamoDB String can be applied as shown in the following
snippet.
@DynamoDBTypeConverted(converter = CurrencyFormatConverter.class)
public Currency getCurrency() { return currency; }
The v2 equivalent of the previous snippet is shown below.
@DynamoDbConvertedBy(CurrencyFormatConverter.class)
public Currency getCurrency() { return currency; }
Note
In v1, you can apply the annotation to the attribute itself , a type or a user-defined annotation, v2 supports applying the annotation it only to the getter.
Add a type converter factory or provider
In v1, you can provide your own set of type converters, or override the types you
care about by adding a type converter factory to the configuration. The type converter
factory extends DynamoDBTypeConverterFactory
, and overrides are done by
getting a reference to the default set and extending it. The following snippet
demonstrates how to do this.
DynamoDBTypeConverterFactory typeConverterFactory =
DynamoDBTypeConverterFactory.standard().override()
.with(String.class, CustomBoolean.class, new DynamoDBTypeConverter<String, CustomBoolean>() {
@Override
public String convert(CustomBoolean bool) {
return String.valueOf(bool.getValue());
}
@Override
public CustomBoolean unconvert(String string) {
return new CustomBoolean(Boolean.valueOf(string));
}}).build();
DynamoDBMapperConfig config =
DynamoDBMapperConfig.builder()
.withTypeConverterFactory(typeConverterFactory)
.build();
DynamoDBMapper mapperWithTypeConverterFactory = new DynamoDBMapper(dynamo, config);
V2 provides similar functionality through the @DynamoDbBean
annotation.
You can provide a single AttributeConverterProvider
or a chain of ordered
AttributeConverterProvider
s. Note that if you supply your own chain of
attribute converter providers, you will override the default converter provider and must
include it in the chain to use its attribute converters.
@DynamoDbBean(converterProviders = {
ConverterProvider1.class,
ConverterProvider2.class,
DefaultAttributeConverterProvider.class})
public class Customer {
...
}
The section on attribute conversion in this guide contains a complete example for v2.
Document API
The Document API supports working with JSON-style documents as single items in a DynamoDB table. The v1 Document API has a corresponding API in v2, but instead of using a separate client for the document API as in v1, v2 incorporates document API features in the DynamoDB enhanced client.
In v1, the Item
EnhancedDocument
The table below compares the differences between the Document APIs in v1 and v2.
Use case | v1 | v2 |
---|---|---|
Create a document client |
|
|
Reference a table |
|
|
Work with semi-structured data | ||
Put item |
|
|
Get item |
|
|
Work with JSON items | ||
Convert a JSON structure to use it with the Document API |
|
|
Put JSON |
|
|
Read JSON |
|
|
API reference and guides for document APIs
v1 | v2 | |
---|---|---|
API reference | Javadoc |
Javadoc |
Documentation guide | Amazon DynamoDB Developer Guide | Enhanced Document API (this guide) |
FAQ
Q. Does optimistic locking with a version number work the same way in v2 as in v1?
A. The behavior is similar, but v2 does not does not automatically add conditions for the delete operations. You must add condition expressions manually if you want to control the delete behavior.