DynamoDBMapper on a table containing multiple Java types

2k views Asked by At

I have a single DynamoDB table which contains more than one type of Logical Entity. My table stores "Employees" and "Organizations" and creates a many-to-many relationship between the two of them.

I am struggling with how to use DynamoDBMapper to model both the entities and my table. Particularly when trying to write queries that will return both Employees and Organizations.

In my Java code, I've started by representing these entities using two classes.

Employee.java

@DynamoDBTable(tableName = "workplaces")
public class Employee() {
  @DynamoDBHashKey(attributeName = "pk")
  public String employeeId;

  @DynamoDBRangeKey(attributeName = "sk")
  public String sortKey

  // Other attributes specific to employees, as well as getters and setters
}

And Organization.java:

@DynamoDBTable(tableName = "workplaces")
public class Organization() {
  @DynamoDBHashKey(attributeName = "pk")
  public String organizationId;

  @DynamoDBRangeKey(attributeName = "sk")
  public String sortKey

  // Other attributes specific to organizations, as well as getters and setters
}

One of my query access patterns is, "Retrieve an organization's details and all of its employees". I have designed my table schema in a way which allows me to retrieve all of these items within a single query.

I'm struggling with how to write this query in Java using DynamoDbMapper. Both DynamoDBQueryExpression and the mapper.query() function require a class to instantiate and hydrate. Since my query result set will contain both types of objects, I don't think I can supply these functions with either Employee.class or Organization.class.

My idea was to just try supplying Object.class, but that doesn't work because DynamoDBMapper expects the supplied class to include the DynamoDB annotations.

Test.java:

DynamoDBMapper mapper = new DynamoDBMapper();
DynamoDBQueryExpression<Object> queryExpression = new DynamoDBQueryExpression<Object>()
  .withHashKeyValues("blah")
  .withRangeKeyCondition("blah");
List<Object> queryResult = mapper.query(Object.class, queryExpression);

I am thinking that the only way around this is to create a "master" class which truly represents all objects in my table, something like:

@DynamoDBTable(tableName = "workplaces")
public class WorkplaceItem() {
  // All attributes from both the Employee and Organization classes
}

Then, all of my queries would be done on the WorkplaceItem class, and I'd be responsible for adding some business logic to convert a WorkplaceItem into a more specific Employee or Organization within the java code.

Is this the right approach? It would be a substantial change to my codebase so I'm curious if there is a better way to accomplish what I want before I start making this change.

1

There are 1 answers

1
Duke Silver On BEST ANSWER

Answering my own question here.

DynamoDBMapper cannot be used for my purpose, but I should still keep the individual classes and not create a single master class.

From this article by AWS: https://aws.amazon.com/blogs/database/amazon-dynamodb-single-table-design-using-dynamodbmapper-and-spring-boot/

AttributeValue liftPK = new AttributeValue("LIFT#" + liftNumber);
QueryRequest queryRequest = new QueryRequest()
        .withTableName("SkiLifts")
        .withKeyConditionExpression("PK = :v_pk")
        .withExpressionAttributeValues(Map.of(":v_pk", liftPK));
QueryResult queryResult = amazonDynamoDB.query(queryRequest);

The results of this query can contain items of different types of objects, both LiftDynamicStats and LiftStaticStats objects. The DynamoDBMapper class isn’t suited to implement this query because its typed methods don’t allow for a query result that contains different types of objects. However, for this access pattern it is important to retrieve the data set containing different types of objects with just one query to DynamoDB. Because the QueryRequest and QueryResult classes are able to deal with query results containing different types of data objects, using the QueryRequest and QueryResult classes is the best alternative for implementing this query.