Spring Data REST的QueryDSL集成可用于执行更复杂的查询吗?

Den*_*men 27 spring querydsl spring-data spring-data-jpa spring-data-rest

我目前正在构建一个REST API,我希望客户端可以轻松地过滤特定实体的大多数属性.使用QueryDSL与结合春季数据REST(由奥利弗·基尔克一个例子),让我很容易地通过允许客户通过组合是指性质(如查询参数进行过滤得到我想要的东西90% /users?firstName=Dennis&lastName=Laumen).

我甚至可以通过实现QuerydslBinderCustomizer接口来自定义查询参数和实体属性之间的映射(例如,用于不区分大小写的搜索或部分字符串匹配).这一切都很棒,但我也希望客户能够使用范围过滤某些类型.例如关于像出生日期这样的财产,我想做类似下面的事情,/users?dateOfBirthFrom=1981-1-1&dateOfBirthTo=1981-12-31.基于数字的属性也是如此/users?idFrom=100&idTo=200.我觉得这应该可以使用QuerydslBinderCustomizer界面,但这两个库之间的集成没有得到非常广泛的记录.

总结一下,这可能使用Spring Data REST和QueryDSL吗?如果是这样,怎么样?

Oli*_*ohm 28

我认为您应该能够使用以下自定义来实现此功能:

bindings.bind(user.dateOfBirth).all((path, value) -> {

  Iterator<? extends LocalDate> it = value.iterator();
  return path.between(it.next(), it.next());
});
Run Code Online (Sandbox Code Playgroud)

这里的关键是使用?dateOfBirth=…&dateOfBirth=(使用属性两次)和….all(…)绑定,这将使您可以访问所提供的所有值.

确保将@DateTimeFormat注释添加到dateOfBirth-property中,User以便Spring能够正确地将传入Strings转换为LocalDate实例.

lambda目前获得了一个Collection<? extends T>能够解决单个元素需要更多痛苦的问题,但是我认为我们可以在将来的版本中更改它,而不是暴露一个List.

  • @ Oliver-gierke - 是否有计划让QuerydslPredicateArgumentResolver最终从URL本身构造谓词?如果QuerydslPredicateArgumentResolver可以从这样的查询字符串构造谓词,那么通常会过于严格地设置谓词行为:<endpoint> /products/?price.greaterThan=100&barcode.contains=ABC&saleEndDate.between = 2017-08-10,2017-08-15 (6认同)

Jor*_*e C 12

由于它在一些评论中发布,我还需要根据字段名称creationDateFrom和不同的行为creationDateTo.为了使它工作,我做了以下:

首先,我将@QueryEntity注释和另外两个字段添加到我的实体类中.这些字段注释为:

  • @Transient 所以字段不会持久化
  • @Getter(value = AccessLevel.PRIVATE) 当我们使用Lombok时,注释会隐藏响应主体中的字段
  • @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) 负责解析url查询参数上的日期格式

@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;

  ...
}  
Run Code Online (Sandbox Code Playgroud)

然后我改变了生成querydsl类的JPAAnnotationProcessor方法QuerydslAnnotationProcessor.这种方式注释的字段@Transient仍然生成QMyEntity但不持久.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>
Run Code Online (Sandbox Code Playgroud)

最后,我扩展QuerydslBinderCustomizer并定制了与之相关的绑定creationDateFrom,creationDateTo但是应用了正确的逻辑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));
}
Run Code Online (Sandbox Code Playgroud)

通过所有这些,您可以使用一个,两个或不使用任何标准来进行日期范围查询:

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
Run Code Online (Sandbox Code Playgroud)


gaz*_*zal 5

这就是我用于所有日期字段的通用绑定的方式,总是期望from和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);
});
Run Code Online (Sandbox Code Playgroud)

这用于日期时间字段。对于日期字段,当获取单个参数时,path.eq()我猜很有意义。