我目前正在构建一个REST API,我希望客户端可以轻松地过滤特定实体的大多数属性。将QueryDSL与Spring Data REST(an example by Oliver Gierke)结合使用可以让我轻松达到我想要的90%,允许客户端通过组合引用属性的查询参数进行过滤(例如{{ 1}})。
我甚至可以通过实现/users?firstName=Dennis&lastName=Laumen
接口来自定义查询参数和实体属性之间的映射(例如,用于不区分大小写的搜索或部分字符串匹配)。这一切都很棒,但我也希望客户能够使用范围过滤某些类型。例如,对于像出生日期这样的属性,我想做类似以下的事情,QuerydslBinderCustomizer
。基于数字的属性/users?dateOfBirthFrom=1981-1-1&dateOfBirthTo=1981-12-31
也是如此。我觉得这应该可以使用/users?idFrom=100&idTo=200
接口,但这两个库之间的集成没有得到非常广泛的记录。
总结一下,这可能是使用Spring Data REST和QueryDSL吗?如果是这样,怎么做?
答案 0 :(得分:28)
我认为您应该能够使用以下自定义功能实现此目的:
bindings.bind(user.dateOfBirth).all((path, value) -> {
Iterator<? extends LocalDate> it = value.iterator();
return path.between(it.next(), it.next());
});
这里的关键是使用?dateOfBirth=…&dateOfBirth=
(使用属性两次)和….all(…)
绑定,这将使您可以访问所提供的所有值。
确保将@DateTimeFormat
注释添加到dateOfBirth
- User
的属性中,以便Spring能够将传入的Strings
转换为LocalDate
个实例正确。
lambda目前得到一个Collection<? extends T>
,这使得对各个元素的解开需要更多的痛苦,但我认为我们可以在将来的版本中更改它,而不是暴露List
。
答案 1 :(得分: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;
...
}
然后我改变了从JPAAnnotationProcessor
到QuerydslAnnotationProcessor
生成querydsl类的方法。这种方式使用@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>
最后,我扩展了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));
}
通过所有这些,你可以使用一个,两个或没有标准进行日期范围查询:
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
答案 2 :(得分:5)
这是我用于所有日期字段的通用绑定,总是期望2个值,来自和。
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);
});
这适用于日期时间字段。对于日期字段,当获取单个参数时,我猜path.eq()
是有意义的。