Use Hash Range keys - derjust/spring-data-dynamodb GitHub Wiki
Overview
Using a Range
and Hash
as a combined key requires a little bit more code to leverage AWS SDK for DynamoDB and Spring-Data:
In the entity have a field annotated with @Id
referencing a custom "key" class - and exposes the attributes as direct getters/setter with the @DynamoDBHashKey
/ @DynamoDBRangeKey
annotation including the column configuration.
The background for this mumbojumbo is that Spring-Data requires to have a dedicated entity representing the key. As the Repository.find()
method takes exactly one argument - T
from the specific derived repository.
Therefore the two fields, which constitute the key, have to be wrapped up in a single entity - the "key class" depicted above.
This also applies to delete
operations: If a combined key is used, also the Repository.deleteById()
method must be used.
Example
Domain class
@DynamoDBTable
public class Playlist {
@Id
private PlaylistId playlistId;
// Any other field and their getter/setter + @DynamoDBAttribute annotation
@DynamoDBHashKey(attributeName = "UserName")
public String getUserName() {
return playlistId != null ? playlistId.getUserName() : null;
}
public void setUserName(String userName) {
if (playlistId == null) {
playlistId = new PlaylistId();
}
playlistId.setUserName(userName);
}
@DynamoDBRangeKey(attributeName = "PlaylistName")
public String getPlaylistName() {
return playlistId != null ? playlistId.getPlaylistName() : null;
}
public void setPlaylistName(String playlistName) {
if (playlistId == null) {
playlistId = new PlaylistId();
}
playlistId.setPlaylistName(playlistName);
}
Key class
The key class itself has only the hash and range member fields including the @DynamoDBHashKey
/ @DynamoDBRangeKey
annotation again - this time without table column configuration.
public class PlaylistId implements Serializable {
private static final long serialVersionUID = 1L;
private String userName;
private String playlistName;
@DynamoDBHashKey
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
@DynamoDBRangeKey
public String getPlaylistName() {
return playlistName;
}
public void setPlaylistName(String playlistName) {
this.playlistName = playlistName;
}
}
Repository interface
- This also means that the generic for the Repository have to be updated to the new "key" class.
public interface PlaylistRepository extends CrudRepository<Playlist, PlaylistId> {
}
An example can be found in this test case with the entity class Playlist / ID class PlaylistId.
Common errors
DynamoDBMappingException: not supported; requires @DynamoDBTyped or @DynamoDBTypeConverted
Chances are that the @Id
field has get
ters/set
ters: DynamoDBMapper
now can't figure out how to serialize the Key
class.
The @Id
field does not need get
ters/set
ters - please remove them: Spring-Data uses the annotated field directly - and DynamoDBMapper
(should) care only for the @DynamoDBHashKey
/@DynamoDBRangeKey
properties
org.springframework.data.annotation.Id
found!
No field annotated with interface This error message is the bottom line if something is not properly annotated.
org.socialsignin.spring.data.dynamodb.exception.BatchDeleteException: Processing of entities failed!; nested exception is com.amazonaws.services.dynamodbv2.model.AmazonDynamoDBException: The provided key element does not match the schema
This exception is thrown if a custom repository method is added in the style of deleteByHashAndRangePropery(hashProperty, rangeProperty)
. To delete an entity via its combined key, the (built-in) deleteById(keyClass)
method must be called.
java.lang.NullPointerException at org.socialsignin.spring.data.dynamodb.repository.support.DynamoDBEntityMetadataSupport.getPropertyNameForAccessorMethod
In the entity class, you may not have fields for the hash and range key fields.
Check list
- You need a specialized class for your primary key. It needs
- to implement
Serializable
(only before Spring 5.0/Spring-Data 2.0!) and - will have the fields, getters, and setters for your hash key field and range key field.
- to implement
- In your entity class, instead of the hash key field and range key field, you have a field that is an instance of the specialized key class mentioned above. You must annotate that field with Spring's
@Id
annotation, but do not provide aget
ter orset
ter. - Also in your entity class, do provide a getter and setter for your hash key field and range key field, even though your entity doesn't have those fields. You pass those through to the corresponding method on your primary key field.
- Make sure to lazy-create your primary key field in the hash key and range key setters in the entity.
- The hash key and range key getters in both your entity class and primary key class have to be annotated with
@DynamoDBHashKey
and@DynamoDBRangeKey
, respectively. Yes, this is redundant. - Make sure that your repo uses the primary key class for the
ID
class, otherwise you'll get another weird error about something not being an instance of the right class.
Do all of the above and the hash-key/range-ky combo will work properly.