We should support reading/writing base types using the DynamoDB Enhanced Client.
Potential syntax/example (not yet implemented):
@DynamoDbBean
@DynamoDbSubtypes({Employee.class, Customer.class})
public abstract class Person {
@DynamoDBHashKey
private long id;
private String name;
}
@DynamoDbBean
private class Employee extends Person {
}
@DynamoDbBean
private class Customer extends Person {
}
DynamoDbEnhancedClient client = DynamoDbEnhancedClient.create();
DynamoDbTable<Person> people = client.table("people", TableSchema.fromBean(Person.class));
Employee bob = new Employee();
bob.setId(1);
bob.setName("Bob the Builder");
Customer lfh = new Customer();
lfh.setId(2);
lfh.setName("Low Flying Hawk");
people.putItem(bob);
people.putItem(lfh);
assertThat(people.getItem(Key.builder().partitionValue(1).build())).isInstanceOf(Employee.class);
assertThat(people.getItem(Key.builder().partitionValue(2).build())).isInstanceOf(Customer.class);
This would be super useful!
Started taking a look at designing this. If we were to implement the proposal above as it's written we'd need to insert our own type metadata into the attribute map before storing it in the DDB table so when we read it back out we had something there that would tell us if it's an Employee or a Customer. Personally I think I prefer the idea of having an explicit property on the base class that can be used to store this information that is fully under the control of the application. My idea goes something like this:
@DynamoDbPolymorphic
public abstract class Animal {
private final String animalAttribute;
protected Animal(Builder b) {
this.animalAttribute = b.animalAttribute;
}
@DynamoDbSubTypeAttribute({
@SubType(propertyValue = "CAT", subType = Cat.class),
@SubType(propertyValue = "DOG", subType = Dog.class)})
public abstract Species species();
public String animalAttribute() {
return this.animalAttribute;
}
public static abstract class Builder {
private String animalAttribute;
public Builder color(String color) {
this.animalAttribute = color;
return this;
}
}
}
Implementations of Animal would follow the normal DynamoDb annotated class pattern (in this case they would most likely be @DynamoDbImmutable) and could make valid TableSchema by themselves.
I see this issue had some thumbs up, so it's good to see people are interested in this. Any notions or bias of how you'd like to see us implement it?
Here's an alternative idea that's closer to the original proposal. In this case the only difference from that proposal is we're requiring the application to explicitly designate the name of the string attribute in the dynamoDb record that will be used to store the type information. This information will not be unmarshalled into any properties and does not require a property to actually exist that models it.
@DynamoDbSubtypes(dynamoDbAttribute = species", subtypes = {
@Subtype(attributeValue = "CAT", subType = Cat.class),
@Subtype(attributeValue = "DOG", subType = Dog.class)})
public abstract class Animal {
private final String animalAttribute;
protected Animal(Builder b) {
this.animalAttribute = b.animalAttribute;
}
public String animalAttribute() {
return this.animalAttribute;
}
public static abstract class Builder {
private String animalAttribute;
public Builder color(String color) {
this.animalAttribute = color;
return this;
}
}
}
Here's an alternative idea that's closer to the original proposal. In this case the only difference from that proposal is we're requiring the application to explicitly designate the name of the string attribute in the dynamoDb record that will be used to store the type information. This information will not be unmarshalled into any properties and does not require a property to actually exist that models it.
@DynamoDbSubtypes(dynamoDbAttribute = species", subtypes = { @Subtype(attributeValue = "CAT", subType = Cat.class), @Subtype(attributeValue = "DOG", subType = Dog.class)}) public abstract class Animal { private final String animalAttribute; protected Animal(Builder b) { this.animalAttribute = b.animalAttribute; } public String animalAttribute() { return this.animalAttribute; } public static abstract class Builder { private String animalAttribute; public Builder color(String color) { this.animalAttribute = color; return this; } } }
I personally like this one as it seems more similar to Jackson object mappers annotations
I second trying to make it close to the Jackson Method since it has been proven out.
We use it and have had no issues with the flexibility. We have used it to marshal to classes based on string and enum properties which would be nice for the DynamoDb mapper as well.
For Context a snippet from the Jackson Example from https://www.tutorialspoint.com/jackson_annotations/jackson_annotations_jsonsubtypes.htm
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME,
include = As.PROPERTY, property = "type") @JsonSubTypes({
@JsonSubTypes.Type(value = Square.class, name = "square"),
@JsonSubTypes.Type(value = Circle.class, name = "circle")
})
{
"type" : "circle",
"name" : "CustomCircle",
"radius" : 1.0
}
Is this implemented in dynamodb-enhanced? If yes, any references?
Most helpful comment
Here's an alternative idea that's closer to the original proposal. In this case the only difference from that proposal is we're requiring the application to explicitly designate the name of the string attribute in the dynamoDb record that will be used to store the type information. This information will not be unmarshalled into any properties and does not require a property to actually exist that models it.