Spring @QuerydslPredicate 问题
Posted
技术标签:
【中文标题】Spring @QuerydslPredicate 问题【英文标题】:Spring @QuerydslPredicate Questions 【发布时间】:2016-06-25 10:26:50 【问题描述】:使用的库
Spring Boot 1.3.2.RELEASE
查询DSL 3.7.2
QueryDSL Maven 插件 1.1.3
休眠 4.3.11.Final
问题
目前,我有一个 Spring Boot 应用程序,它使用 Spring Data JPA(由 Hibernate 支持)具有一些基本的 CRUD 功能,并使用 Spring Data Envers 进行审计。我还有以下端点可以从中检索实体列表:
http://localhost:8080/test-app/list
现在,我想通过@QuerydslPredicate
注释来使用new QueryDSL support that Spring offers。这适用于大多数字段或子实体,但它似乎不适用于子实体的集合。文档、博客文章等似乎没有涵盖这种情况——我能找到的唯一信息是它支持简单集合的“输入”(即字符串集合等)。
所以,我的实体是这样设置的:
Person.java
@Data
@Entity
@Audited
public class Person
@Id
private long id;
private String name;
private List<Pet> pets = new ArrayList<>();
Pet.java
@Data
@Entity
@Audited
public class Pet
@Id
private long id;
private int age;
我使用com.mysema.maven:apt-maven-plugin
生成我的Q 类,它生成我的QPerson
并带有以下字段:
public final ListPath<com.test.Pet, com.test.QPet> pets = this.<com.test.Pet, com.test.QPet>createList("pets", com.test.Pet.class, com.test.QPet.class, PathInits.DIRECT2);
如果我尝试对此进行查询,则会收到异常:
查询:
http://localhost:8080/test-app/list?pets.age=5
例外:
10:21:37,523 ERROR [org.springframework.boot.context.web.ErrorPageFilter] (http-/127.0.0.1:8080-1) Forwarding to error page from request [/list] due to exception [null]: java.lang.NullPointerException
at org.springframework.util.ReflectionUtils.getField(ReflectionUtils.java:143) [spring-core-4.2.4.RELEASE.jar:4.2.4.RELEASE]
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.reifyPath(QuerydslPredicateBuilder.java:185) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.reifyPath(QuerydslPredicateBuilder.java:188) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.getPath(QuerydslPredicateBuilder.java:167) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.invokeBinding(QuerydslPredicateBuilder.java:136) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.data.querydsl.binding.QuerydslPredicateBuilder.getPredicate(QuerydslPredicateBuilder.java:111) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.data.web.querydsl.QuerydslPredicateArgumentResolver.resolveArgument(QuerydslPredicateArgumentResolver.java:106) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.data.web.querydsl.QuerydslPredicateArgumentResolver.resolveArgument(QuerydslPredicateArgumentResolver.java:48) [spring-data-commons-1.11.2.RELEASE.jar:]
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:78) [spring-web-4.2.4.RELEASE.jar:4.2.4.RELEASE]
现在这个查询看起来像是在尝试解析 propertyPath Person.pets.age
。它将Person.pets
正确识别为ListPath
,然后尝试识别CompanyAddress.addressLine1
(这似乎是正确的)。问题是,它试图使用实体路径来获取类,这是ListPath
而不是QPet
:
Field field = ReflectionUtils.findField(entityPath.getClass(), path.getSegment());
Object value = ReflectionUtils.getField(field, entityPath);
以下查询按预期工作:
http://localhost:8080/test-app/list?name=Bob
我的期望是通过使用?pets.age=5
,将构建以下谓词:
QPerson.person.pets.any().age.eq(5)
目前这可以通过 Spring 的 QuerydslPredicate 支持实现吗?还是应该从查询参数手动构建谓词?
其他问题
作为一个附加问题,QuerydslPredicate 是否可能出现以下问题。假设我在 pet 上有名和姓,我想只使用 name=Bob
运行查询:
http://localhost:8080/test-app/pet/list?name=Bob
我希望这样构建查询谓词:
final BooleanBuilder petBuilder = new BooleanBuilder();
petBuilder.and(QPet.firstName.equals("Bob").or(QPet.lastName.equals("Bob")));
这可能吗?从 QuerydslBinderCustomizer
的自定义方法来看,它似乎不是这样,因为您需要绑定 Q 类的一个字段。我猜我想做的事情不被支持。
如果这些都不可能,那么我将坚持手动创建谓词,并将其传递到存储库。
【问题讨论】:
我也找到了这方面的文档。特别是如何利用“in”功能 - docs.spring.io/spring-data/commons/docs/current/reference/html/… 我确实找到了这个答案,如果您想为给定字段提供多个匹配项,它至少可以解释如何“进入”工作 - ***.com/a/35158320/228369 【参考方案1】:您可以使用QuerydslBinderCustomizer
来实现您的目的。下面是一些可以帮助您的示例代码:
public interface PersonRepository extends JpaRepository<Job, Integer>,
QueryDslPredicateExecutor<Person>, QuerydslBinderCustomizer<QJob>
@Override
public default void customize(final QuerydslBindings bindings, final QPerson person)
bindings.bind(person.pets.any().name).first((path, value) ->
return path.eq(value);
);
【讨论】:
【参考方案2】:我遇到了同样的错误。但是我注意到使用 QuerydslAnnotationProcessor 插件(而不是 JPA 注释处理器)允许我按预期查询实体的子集合。您只需使用 @QueryEntity 注释标记所有实体类。 (JPA 注释处理器自动为@Entity 注释类生成查询类。)
在你的 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>
<dependencies>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>4.1.3</version>
</dependency>
</dependencies>
</plugin>
我相信我遇到了您遇到的异常,因为我从 JPA 注释处理器更改为 QuerydslAnnotationProcessor,出于某种我不记得的原因,并且忽略了用 @ 标记相关列表的实体类QueryEntity 注释。但是我也相信我有另一个 Spring-Data-Rest\JPA 支持的 API,它使用 2017 年 8 月构建的 JPA 注释处理器,我相信查询实体的子集合可以按预期工作。我将在今天晚些时候确认这一点,如果是这样的话,我会提供相关依赖项的版本。也许这个问题已经解决了。
【讨论】:
感谢您的帖子。我不再参与那个特定项目,所以我无法立即验证它,但我会在接下来的几天里尝试建立一个小项目,看看是否能解决问题。以上是关于Spring @QuerydslPredicate 问题的主要内容,如果未能解决你的问题,请参考以下文章
春季测试中的@QuerydslPredicate 在standaloneSetup 中失败
Spring框架系列 - Spring和Spring框架组成
你了解Spring从Spring3到Spring5的变迁吗?
Spring全家桶笔记:Spring+Spring Boot+Spring Cloud+Spring MVC
学习笔记——Spring简介;Spring搭建步骤;Spring的特性;Spring中getBean三种方式;Spring中的标签