DynamoDB Mapper Query Example in Java

Trying to figure out how to perform a DynamoDB Query on your Table using DynamoDB Mapper? This is the article for you.

To perform a query on DynamoDB with DynamoDB Mapper, there a couple of small pre-requisites:

  1. You need IAM user credentials with dynamodb:query permissions
  2. A dependency on the on the AWS SDK which includes DynamoDBMapper (I suggest Maven for Java dependency management)

Starting State

The table I’ve created for this tutorial is one called CustomerOrders, and here’s a snapshot of its contents.

Starting State – My DynamoDB CustomerOrders table

I also have a model file called CustomerOrders.java that looks like below. Keep in mind I’m using the popular library Lombok to generate boilerplate code.

@ToString
@NoArgsConstructor //You need a empty default constructor
@Getter
@Setter
@DynamoDBTable(tableName = "CustomerOrders")
public class CustomerOrder {
    @DynamoDBHashKey(attributeName = "CustomerID")
    private String customerID;

    @DynamoDBRangeKey(attributeName = "OrderID")
    private String orderID;

    @DynamoDBAttribute(attributeName = "OrderAddress")
    private String orderAddress;

    @DynamoDBAttribute(attributeName = "OrderTotal")
    private Long orderTotal;
}

Notice the custom fields, as explained below:

You may enjoy this article on DynamoDB Scan VS Query – When to Use What?

Basic Client Setup

We leverage AWSCredentialsProvider and AmazonDynamoDBClient to create the corresponding inputs for our mapper class. Afterwards, we’re ready to perform our query.

        //Specify credential details
        AWSCredentialsProvider credentials = new AWSStaticCredentialsProvider(
                new BasicAWSCredentials(System.getenv("ACCESS_KEY"),
                                        System.getenv("SECRET_ACCESS_KEY")));

        //Create client
        AmazonDynamoDB ddbClient = AmazonDynamoDBClientBuilder.standard()
                .withCredentials(credentials)
                .withRegion("us-east-1") //Remember to change your region!
                .build();

        DynamoDBMapper mapper = new DynamoDBMapper(ddbClient);

Before proceeding, make sure you either set your access key and secret access key as environment variables (this is IDE / OS specific) and specifying the region your table is located. Failure to do so will result in obscure errors that take way too long to debug.

Performing a Query in DynamoDB

Firstly, lets look at the code and then walk through whats going on here.

Performing a basic query against a HashKey is a very straightforward process. We simply instantiate an instance of our model class, in this case CustomerOrder and set its HashKey to the id that we are looking for. For example, if I set OrderId to “CUSTOMER3” as I do below, it means our query will return results with a HashKey that is CUSTOMER3.

We then formulate our expression and provide it our search object (CustomerOrder) in the HashKeyValues field. We also need to set our Limit to specify the maximum number of results we want back.

Finally, we use the mapper’s query function to execute our query against DynamoDB. We just need to set the result type to List and provide the class type we expect it to return. Printing out the response produces returns all 3 orders associated with “CUSTOMER3”, perfect!

CustomerOrder(customerID=CUSTOMER3, orderID=3, orderAddress=123 Main St, orderTotal=150.72)
CustomerOrder(customerID=CUSTOMER3, orderID=4, orderAddress=123 Main St, orderTotal=22.31)
CustomerOrder(customerID=CUSTOMER3, orderID=5, orderAddress=123 Main St, orderTotal=7.11)

You’ll have noticed this returns all results with Customer 3, but what if we want to filter our result set down to just OrderId 5 for example?

Sure we can filter the results after we get them from DynamoDB, but this adds work to our application layer and even degrade performance on the DynamoDB side. More importantly, returning all results from DynamoDB causes wasted $, because we’re paying for Read Capacity Units (or RCUs) for the 3 units, despite maybe only actually wanting 1 or 2 records.

In a small example like this, the wasted $ is trivial. However if you have a scenario where you have many many records with the same PartitionKey, this could mean filtering through thousands of results.

To be able to search for a known CustomerId and OrderId, we can leverage the RangeKey functionality.

Adding a Range Key Condition

Since we set our RangeKey to be OrderId, we can also easily search for records that also have that id by providing the RangeKeyCondition, and adding it as an attribute onto our QueryExpression. The revised code is seen below:

        CustomerOrder customerOrder = new CustomerOrder();
        customerOrder.setCustomerID("CUSTOMER3");

        // * NEW *
        Condition condition = new Condition();
        condition.withComparisonOperator(ComparisonOperator.EQ)
                .withAttributeValueList(new AttributeValue().withS("5")); //Set your search value here

        DynamoDBQueryExpression<CustomerOrder> queryExpression =
                new DynamoDBQueryExpression<CustomerOrder>()
                .withHashKeyValues(customerOrder)
                .withRangeKeyCondition("OrderID", condition) // * NEW *
                .withLimit(10);

        List<CustomerOrder> queryResult = mapper.query(CustomerOrder.class, queryExpression);

        queryResult.forEach(System.out::println);

Running our code returns the resultset only containing a single matching record matching OrderID 5, as specified by our code.

CustomerOrder(customerID=CUSTOMER3, orderID=5, orderAddress=123 Main St, orderTotal=7.11)

I hope you enjoyed this article. Check out my other DynamoDB articles here.

Exit mobile version