Can Spring Data REST's QueryDSL integration be used to perform more complex queries? Can Spring Data REST's QueryDSL integration be used to perform more complex queries? spring spring

Can Spring Data REST's QueryDSL integration be used to perform more complex queries?


I think you should be able to get this to work using the following customization:

bindings.bind(user.dateOfBirth).all((path, value) -> {  Iterator<? extends LocalDate> it = value.iterator();  return path.between(it.next(), it.next());});

The key here is to use ?dateOfBirth=…&dateOfBirth= (use the property twice) and the ….all(…) binding which will give you access to all values provided.

Make sure you add the @DateTimeFormat annotation to the dateOfBirth-property of User so that Spring is able to convert the incoming Strings into LocalDate instances correctly.

The lambda currently gets a Collection<? extends T> which makes untangling the individual elements a bit more pain that it needs to be, but I think we can change this in a future release to rather expose a List.


As it was posted in some comment I also had the need to have different behaviour according to the field name creationDateFrom and creationDateTo. In order to make it work I did the following:

First I added the @QueryEntity annotation and two more fields to my entity class. The fields were annotated with:

  • @Transient so the fields are not persisted
  • @Getter(value =AccessLevel.PRIVATE) as we are using Lombok, the annotation hides thefield from the response body
  • @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) takes care of the format for parsing thedate on the url query parameter

@QueryEntity@Entitypublic class MyEntity implements Serializable {  ...  @Column(updatable = false)  @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)  private Date creationDate;  @Transient  @Getter(value = AccessLevel.PRIVATE)  @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)  private Date creationDateTo;  @Transient  @Getter(value = AccessLevel.PRIVATE)  @DateTimeFormat(iso = DateTimeFormat.ISO.DATE)  private Date creationDateFrom;  ...}  

Then I changed the way of generating the querydsl classes from JPAAnnotationProcessor to QuerydslAnnotationProcessor. This way fields annotated with @Transient are still generated on QMyEntity but are not persisted. Plugin configuration in pom:

<plugin>    <groupId>com.mysema.maven</groupId>    <artifactId>apt-maven-plugin</artifactId>    <version>1.1.3</version>    <executions>        <execution>            <phase>generate-sources</phase>            <goals>                <goal>process</goal>            </goals>            <configuration>                <outputDirectory>target/generated-sources/annotations</outputDirectory>                <processor>com.querydsl.apt.QuerydslAnnotationProcessor</processor>            </configuration>        </execution>    </executions></plugin>

Finally I extended the QuerydslBinderCustomizer and customized the bindings related with the creationDateFrom and creationDateTo but applying the right logic over creationDate

@Overridedefault void customize(QuerydslBindings bindings, QMyEntity root) {    bindings.bind(root.creationDateFrom).first((path, value) ->                                                 root.creationDate.after(value));    bindings.bind(root.creationDateTo).first((path, value) ->                                               root.creationDate.before(value));}

With all of this you can do date range queries using one, both or none of the criterias:

http://localhost:8080/myentities?creation_date_to=2017-05-08http://localhost:8080/myentities?creation_date_from=2017-01-01http://localhost:8080/myentities?creation_date_from=2017-01-01&creation_date_to=2017-05-08


This is what I used for a generic binding for all date fields, always expecting 2 values, from and to.

bindings.bind(Date.class).all((final DateTimePath<Date> path, final Collection<? extends Date> values) -> {    final List<? extends Date> dates = new ArrayList<>(values);    Collections.sort(dates);    if (dates.size() == 2) {        return path.between(dates.get(0), dates.get(1));    }    throw new IllegalArgumentException("2 date params(from & to) expected for:" + path + " found:" + values);});

This is for datetime fields. For a date field, when getting a single parameter, path.eq() makes sense I guess.