2

I have to convert the incoming parameter value to Repository interface into desired format, is it possible to do it. My Domain Class,

@DynamoDBTable(tableName = "test")
public class Test implements Serializable{

    @Id
    private String id;
    private String name;
    private String date;

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

    @DynamoDBAttribute(attributeName = "name")
    public String getName() {
        return name;
    }

    @DynamoDBAttribute(attributeName = "date")
    @JsonSerialize(using = StringDateSerializer.class)
    public String getDate() {
        return date;
    }
    public void setId(String id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    @JsonDeserialize(using = StringDateDeserializer.class)
    public void setDate(String date) {
        this.date = date;
    }

}

And my repository interface,

@EnableScan
@RestResource(path="test", rel="test")
public interface TestRepository extends PagingAndSortingRepository<Test, String>{

    @RestResource(path="testsearch", rel="test")
    public Page<Test> findByNameAndDateLessThan(@Param("name") String name, @Param("date") String date, Pageable pageable);

}

Here I have to convert the incoming date String to time using getTime() method of Java. Is it possible to achieve this without using controller and am not interested in sending from client side because timezone problem may occur.

My Convertors:

public class StringDateSerializer extends JsonSerializer<String> {

    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");

    @Override
    public void serialize(String time, JsonGenerator gen,
            SerializerProvider provider) throws IOException,
            JsonProcessingException {
        Date date = new Date(Long.parseLong(time));
        String formattedDate = dateFormat.format(date);
        gen.writeString(formattedDate);
    }

}

public class StringDateDeserializer extends JsonDeserializer<String> {

    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");

    @Override
    public String deserialize(JsonParser parser, DeserializationContext context)
            throws IOException, JsonProcessingException {
        String dateReceived = parser.getText();
        Date date = null;
        try {
            date = dateFormat.parse(dateReceived);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return String.valueOf(date.getTime());
    }

}

Here I have to use, GET /test/search/test?name=xx&date=14-06-2014. I need to get all the names with date less than 14-06-2014 and left the datas with or after 14-06-2014.

While POST and GET, I have converted the incoming and outgoing string using JsonSerialize and JsonDeserialize annotations but if I want to fetch any data using finder method its not converting as I thought.

For example, If I save {"name": "Test", "date": "08-10-2014"}, in DB it will be saved by its equivalent time and If I want to search it using 08-10-2014 not the time constant. I am new to springs and I cant find a way for it. Thanks in advance.

jAddict
  • 395
  • 3
  • 6
  • 18
  • Did I understood correctly, that you can't query with date String "08-10-2014", while you have a record {"name": "Test", "date": "08-10-2014"} in your DynamoDB? – mavarazy Jun 07 '14 at 12:46
  • @mavarazy --> No I will be saving it as String only. So I can query it. Actually I will getting it as String and using this **String.valueOf(date.getTime())**, I will save it into DB and even if we save as 08-06-2014 also we can search it as we are saving it as String. But we cant perform LessThan or GreaterThan like those operations on it. So I preferred to save it as Time and While I use **GET /test/search/testsearch** I will be passing parameter **?name=test&&date=08-01-2014** and in repository **@Param** used to read data, have to use converter to convert from 08-01-2014 to equivalent time – jAddict Jun 07 '14 at 13:01
  • If you want to be able to query by date, take a look at http://stackoverflow.com/questions/14836600/querying-dynamodb-dy-date, although, as far as I know GSI is not yet supported by spring-data-dynamodb, you can make appropriate request in github. – mavarazy Jun 07 '14 at 13:13
  • Also I would try to save date in default yyyy-MM-dd'T'HH:mm:ss.SSS'Z' format, and try to query with Date in your interface (yyyy-MM-dd'T'HH:mm:ss.SSS'Z' is the default format used by DefaultDynamoDBDateMarshaller which is default Date transformer for Query builder in spring-data-dynamodb) – mavarazy Jun 07 '14 at 13:15
  • mavarazy-Yeah I know that and I need to compare dates like Names registered before and after so I am unable to query like that for example, findByDateLessThan. If I save using getTime(), I can do it easily. The one thing is, Is it possible to format @Param field value like What I did for setDate in Domain Class. I need to format the given parameter as I want. Can be string to string and dont consider as Date. I saw [here](https://github.com/spring-projects/spring-data-rest/blob/master/spring-data-rest-example/src/main/java/org/springframework/data/rest/example/jpa/PersonRepository.java) – jAddict Jun 07 '14 at 14:55
  • This is the thing I need to achieve. public Page findByNameAndDate(@Param("name") String name, @Param("date") **@JsonDeserialize(using="StringDateSerializer.class")** String date, Pageable pageable); – jAddict Jun 07 '14 at 15:42

2 Answers2

9

What's the reason you use String as the type for the date in the first place. That's quite suboptimal (to phrase it politely) API design.

Spring Data REST support the usage of @DateTimeFormat on query method parameters to turn the String base representation you get from the HTTP request into a Date. So your repository interface might look something like this:

public interface TestRepository extends PagingAndSortingRepository<Test, String>{

  public Page<Test> findByNameAndDate(@Param("name") String name, 
    @Param("date") @DateTimeFormat(iso = ISO.DATE) Date date, Pageable pageable);
}

This will cause Strings like 2014-06-08 to be turned into the appropriate Date.

Oliver Drotbohm
  • 80,157
  • 18
  • 225
  • 211
  • Thank you Oliver. Actually I need to do **findByNameAndDateLessThan** here. If I do that, for example it compares until 2014 and gives the result and unable to compare the whole date. So that thought of saving as a long and where comparison can be easy. If am wrong, please give me suggestion. – jAddict Jun 09 '14 at 03:41
  • Am using spring data dynamodb and am unable to use and convert date using **@DateTimeFormat(iso = ISO.DATE)** as parameter. Am getting this exception whenever I pass as parameter. **{ cause: null message: "Creating conditions on null property values not supported: please specify a value for 'date'" }**. But have passed date parameter and also if pass date as **2014-06-12T00:00:00.000Z** in this format, exception not arising but null object returns even record is there for 2014-06-12. Its a dynamoDB saving problem I think so. – jAddict Jun 09 '14 at 04:26
  • Any chance you add the complete stack trace to the question and update the interface declaration to what you currently have in your codebase? – Oliver Drotbohm Jun 09 '14 at 10:41
  • Have updated my question with the required things. Please have a view at it. – jAddict Jun 09 '14 at 11:11
0

If I'm understanding your issue correctly, there are two areas of concern - how Spring-Data-Rest handles date mapping, and how Spring-Data-DynamoDB handles date mapping.

With regard to Spring-Data-DynamoDB:

DynamoDB stores dates as Strings, so if you have a date attribute as part of your date model you can either represent them as Strings in your data model ( as I think you are doing currently ), or you can represent them as Dates, and configure Spring-Data-DynamoDB so that it maps the Dates to Strings. This can be done using Custom Marshallers from amazon-aws-sdk, and support has been added to handle this in the Spring Data DynamoDB module.

You can read about marshallers here : http://java.awsblog.com/post/Tx1K7U34AOZBLJ2/Using-Custom-Marshallers-to-Store-Complex-Objects-in-Amazon-DynamoDB

Note that this marshalling is separate from any mapping that you may be requiring Spring-Data-Rest to perform from JSON to objects - for this you will still need the @DateTimeFormat annotation.

If you want to represent the date as a java.util.Date in your data model, simply annotate the getter for the attribute in your domain class with @DynamoDBMarshalling, and pass in the class of marshaller you wish to use, eg:

@DynamoDBRangeKey(attributeName = "ReplyDateTime")
@DynamoDBMarshalling(marshallerClass=DefaultDynamoDBDateMarshaller.class)
public Date getReplyDateTime() {
...

DefaultDynamoDBDateMarshaller here is a support class from Spring-Data-DynamoDB, but you can implement your own for custom date/string mapping.

With this in place, you can now change your repository finder methods so they expect Date parameters rather than Strings:

public Page<Reply> findByReplyDateTimeAfter(Date replyDateTime,Pageable pageable);

Hope this helps,

Cheers,

Michael

  • Thank you Michael. Am getting an exception if I include Range key **Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'testRepository': Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: No method annotated by DynamoDBHashKey within type java.lang.String!** and **java.lang.IllegalArgumentException: No method annotated by DynamoDBHashKey within type java.lang.String! ** – jAddict Jun 09 '14 at 15:35