michaellavelle / spring-data-dynamodb

Simplifies the development of creating an Amazon DynamoDB-based Java data access layer
https://derjust.github.io/spring-data-dynamodb/
Apache License 2.0
169 stars 284 forks source link

DynamoDBRangeKey causes java.lang.IllegalArgumentException: No method or field annotated by @DynamoDBHashKey within type #75

Closed nicsl0s closed 8 years ago

nicsl0s commented 8 years ago

I'm trying to implement a service method to always return the latest item from a dynamoDB table by id. I'm using spring boot 1.2.7. with spring-data-dynamodb 1.0.2.RELEASE. My DynamoDB table class looks like this:

@DynamoDBTable(tableName = "test")
public class TestEvent {

    private String id;
    private String replyDateTime;
    private Integer sampleSize;

    @DynamoDBHashKey(attributeName = "Id")
    public String getId() {
        return id;
    }

    public void setId(final String id) {
        this.id = id;
    }

    @DynamoDBRangeKey(attributeName = "ReplyDateTime")
    public String getReplyDateTime() {
        return replyDateTime;
    }

    public void setReplyDateTime(final String replyDateTime) {
        this.replyDateTime = replyDateTime;
    }

    @DynamoDBAttribute(attributeName = "SampleSize")
    public Integer getSampleSize() {
        return sampleSize;
    }

    public void setSampleSize(final Integer sampleSize) {
        this.sampleSize = sampleSize;
    }
}

My repos look like this:

public interface TestEventRepository extends CrudRepository<TestEvent, String>, TestEventCustomRepository {
    public List<TestEvent> findById(String id);
    public List<TestEvent> findByIdIn(List<String> ids);
}

public interface TestEventCustomRepository {
    public List<TestEvent> findLatestById(String id);
}

public class TestEventCustomRepositioryImpl implements TestEventCustomRepository {

    @Autowired
    private DynamoDBMapper dynamoDBMapper;

    @Override
    public List<TestEvent> findLatestById(final String id) {

        final TestEvent e = new TestEvent();
        e.setId(id);

        final DynamoDBQueryExpression<TestEvent> queryExpression =
                new DynamoDBQueryExpression<TestEvent>()
                .withHashKeyValues(e)
                .withLimit(1);
        queryExpression.setScanIndexForward(true);

        return dynamoDBMapper.query(TestEvent.class, queryExpression);
    }

Whenever I the application it, I get error:

...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testEventRepository': Invocation of init method failed; 
nested exception is java.lang.IllegalArgumentException: No field annotated with interface org.springframework.data.annotation.Id found!
...

I changed my DynamoDB table class to add the @Id annotation

@DynamoDBTable(tableName = "test")
public class TestEvent {

    @Id
    private String id;
    private String replyDateTime;
    private Integer sampleSize;

    @DynamoDBHashKey(attributeName = "Id")
    public String getId() {
        return id;
    }

    public void setId(final String id) {
        this.id = id;
    }

    @DynamoDBRangeKey(attributeName = "ReplyDateTime")
    public String getReplyDateTime() {
        return replyDateTime;
    }

    public void setReplyDateTime(final String replyDateTime) {
        this.replyDateTime = replyDateTime;
    }

    @DynamoDBAttribute(attributeName = "SampleSize")
    public Integer getSampleSize() {
        return sampleSize;
    }

    public void setSampleSize(final Integer sampleSize) {
        this.sampleSize = sampleSize;
    }
}

Now get the following error:

Caused by: java.lang.IllegalArgumentException: No method or field annotated by @DynamoDBHashKey within type java.lang.String!

How to solve this?

derjust commented 8 years ago

You are using as primary key a RANGE+HASH. You need to do the following

You can find an example here in Playlist / PlaylistId: https://github.com/michaellavelle/spring-data-dynamodb/tree/master/src/test/java/org/socialsignin/spring/data/dynamodb/domain/sample

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 your 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.

nicsl0s commented 8 years ago

Ok, that helped. thx