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

雨燕双飞 提交于 2019-11-27 06:38:09

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 the field from the response body
  • @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) takes care of the format for parsing the date on the url query parameter

@QueryEntity
@Entity
public 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

@Override
default 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-08
http://localhost:8080/myentities?creation_date_from=2017-01-01
http://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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!