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

Using Marshaller w/ Repository #80

Closed bbradley72 closed 4 years ago

bbradley72 commented 7 years ago

When trying to use a repository that references a java 8 zoneddatetime, I am getting an error:

Caused by: java.lang.RuntimeException: Cannot create condition for type:class java.time.ZonedDateTime property conditions must be String,Number or Boolean, or have a DynamoDBMarshaller configured at org.socialsignin.spring.data.dynamodb.repository.query.AbstractDynamoDBQueryCriteria.addAttributeValue(AbstractDynamoDBQueryCriteria.java:606) ~[spring-data-dynamodb-4.4.1.jar:na] at org.socialsignin.spring.data.dynamodb.repository.query.AbstractDynamoDBQueryCriteria.createCollectionCondition(AbstractDynamoDBQueryCriteria.java:645) ~[spring-data-dynamodb-4.4.1.jar:na] at org.socialsignin.spring.data.dynamodb.repository.query.AbstractDynamoDBQueryCriteria.withPropertyBetween(AbstractDynamoDBQueryCriteria.java:405) ~[spring-data-dynamodb-4.4.1.jar:na] at org.socialsignin.spring.data.dynamodb.repository.query.AbstractDynamoDBQueryCreator.addCriteria(AbstractDynamoDBQueryCreator.java:104) ~[spring-data-dynamodb-4.4.1.jar:na] at org.socialsignin.spring.data.dynamodb.repository.query.AbstractDynamoDBQueryCreator.and(AbstractDynamoDBQueryCreator.java:142) ~[spring-data-dynamodb-4.4.1.jar:na] at org.socialsignin.spring.data.dynamodb.repository.query.AbstractDynamoDBQueryCreator.and(AbstractDynamoDBQueryCreator.java:41) ~[spring-data-dynamodb-4.4.1.jar:na] at org.springframework.data.repository.query.parser.AbstractQueryCreator.createCriteria(AbstractQueryCreator.java:109) ~[spring-data-commons-1.12.4.RELEASE.jar:na] at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:88) ~[spring-data-commons-1.12.4.RELEASE.jar:na] at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:73) ~[spring-data-commons-1.12.4.RELEASE.jar:na] at org.socialsignin.spring.data.dynamodb.repository.query.PartTreeDynamoDBQuery.doCreateQuery(PartTreeDynamoDBQuery.java:65) ~[spring-data-dynamodb-4.4.1.jar:na] at org.socialsignin.spring.data.dynamodb.repository.query.AbstractDynamoDBQuery.doCreateQueryWithPermissions(AbstractDynamoDBQuery.java:76) ~[spring-data-dynamodb-4.4.1.jar:na] at org.socialsignin.spta.dynamodb.repository.query.AbstractDynamoDBQuery$CollectionExecution.execute(AbstractDynamoDBQuery.java:98) ~[spring-data-dynamodb-4.4.1.jar:na] at org.socialsignin.spring.data.dynamodb.repository.query.AbstractDynamoDBQuery.execute(AbstractDynamoDBQuery.java:288) ~[spring-data-dynamodb-4.4.1.jar:na]

Here is my code:

Domain:

@DynamoDBTable(tableName = "BaseDomain")
public class BaseDomain {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private String id;
    private String createdBy;
    private ZonedDateTime createdOn;
...
@DynamoDBTypeConverted(converter = ZonedDateTimeJSONConverter.class)
    public ZonedDateTime getCreatedOn() {
        return createdOn;
    }
    public void setCreatedOn(ZonedDateTime createdOn) {
        this.createdOn = createdOn;
    }

Marshaller:

public class ZonedDateTimeJSONConverter implements DynamoDBTypeConverter<Long, ZonedDateTime> {

    @Override
    public Long convert(ZonedDateTime zdt) {
        zdt.withZoneSameInstant(ZoneId.of("America/New_York"));
        return zdt.toEpochSecond();
    }

    @Override
    public ZonedDateTime unconvert(Long epochSecond) {
        return Instant.ofEpochMilli(epochSecond).atZone(ZoneId.of("America/New_York"));
    }

}

Repository:

@EnableScan
public interface BaseDomainRepository extends CrudRepository<BaseDomain, String> {

    public List<BaseDomain> findByUserIdAndCreatedOnBetween(String userId, ZonedDateTime date1, ZonedDateTime date2);
}

I am calling the repository with this:

ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/New_York"));
ZonedDateTime date1 = now.withHour(23).withMinute(59).withSecond(59).withNano(999999999).minusDays(1);
ZonedDateTime date1 = now.withHour(0).withMinute(0).withSecond(0).withNano(0).plusDays(1);
baseDomainRepository.findByUserIdAndCreatedOnBetween('testUser',date1,date2);

I am trying to find all objects with a userId and that was created on a specific day.

createdOn is being correctly saved to the dynamodb as an epoch long: { "createdBy": "testUser", "createdOn": 1477380036, "id": "ed58fffb-11de-43c4-b218-6d71fc2a6090" }

if I run a query on only the userId, then it pulls back objects without an error. However, I would like to only receive ones that were created on a specific day.

What am I doing wrong? How can I use my marshaller with this query? Or, is there a better way to handle this?

Thanks for any help you can provide.

derjust commented 7 years ago

Looks like you have done everything right.

Have you tried moving the annotation from the getter to the field itself? Lately there have been some fixes been released that look at both locations. Which version do you use? (I'm traveling so I'm quite impaired doing tests myself)

bbradley72 commented 7 years ago

Thanks for the quick reply.

I'm currently using this version: com.github.derjust:spring-data-dynamodb:4.4.1

I'll try moving it to the field and see if that works.

I would note that the marshaller works when simply reading the records when searched by userId but not createdOn. It only errors when I try and include createdOn in the findBy definition.

bbradley72 commented 7 years ago

I moved the annotation to the property and it still gave the same error:

@DynamoDBTable(tableName = "BaseDomain")
public class BaseDomain {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private String id;
    private String createdBy;
    @DynamoDBTypeConverted(converter = ZonedDateTimeJSONConverter.class)
    private ZonedDateTime createdOn;
...
    public ZonedDateTime getCreatedOn() {
        return createdOn;
    }
    public void setCreatedOn(ZonedDateTime createdOn) {
        this.createdOn = createdOn;
    }
jasiustasiu commented 7 years ago

I had the same problem. Replace @DynamoDBTypeConverted with @DynamoDBMarshalling. It's deprecated but it works in this case