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中的标签

Spring--Spring入门