【问题标题】:Problem using QuerydslBinderCustomizer and @QueryInit at the same time同时使用 QuerydslBinderCustomizer 和 @QueryInit 的问题
【发布时间】:2020-10-14 12:04:10
【问题描述】:

提供以下课程

public class Department {
    ...
    @OneToOne
    @JoinColumn(name = "idDepartment")
    @QueryInit("customer.company")
    private Project project;
    ...
}

public class Project {
    ...
    @OneToOne
    @JoinColumn(name = "idCustomer")
    private Customer customer;
    ...
}

public class Customer {
    ...

    @OneToOne
    @JoinColumn(name = "idCompany")
    private Company company;
    ...
}

我需要在project 上使用@QueryInit 才能访问第4 级或更高级别,如下所示:

predicate.and(
    QDepartment.department.project.customer.company.id.eq(idCompany)
);

同时,我需要使用QuerydslBinderCustomizer,这样我才能自定义过滤行为

public interface DepartmentRepository
        extends CrudRepository<Department, UUID>, 
        PagingAndSortingRepository<Department, UUID>,
        QuerydslPredicateExecutor<Department>, 
        QuerydslBinderCustomizer<QDepartment> {

    @Override
    default public void customize(QuerydslBindings bindings, QDepartment root) {
        bindings.bind(root.version).first((path, value) -> path.goe(value));
        bindings.bind(String.class).first((StringPath path, String value) -> path.containsIgnoreCase(value));
    }
}

当我使用@QueryInit 时,customize 没有被调用,我的自定义不起作用

我已经尝试更新最后一个 Querydsl 版本 4.4.0 并添加 com.mysema.querydsl querydsl-apt 但无论如何都不起作用


目前我正在使用服务级别的谓词,像这样

public Page<Department> list(Predicate predicate, Pageable pageable){
   BooleanBuilder predicateDepartment = new BooleanBuilder(predicate);
        UUID idCompany = 
   springUserDetailsService.getUserSpring().getIdCompany();

   predicateDepartment.and(
       QDepartment.department.project.customer.company.id.eq(idCompany)
   );

   return repository.findAll(predicateDepartment, pageable);
}

所以我认为我无法在customize() 中配置EntityPath,因为它是在调用存储库时调用的


应用于我的代码的解决方案:

public Page<Department> list(Predicate predicate, Pageable pageable){
   BooleanBuilder predicateDepartment = new BooleanBuilder(predicate);
        UUID idCompany = 
   springUserDetailsService.getUserSpring().getIdCompany();

---change---
   QDepartment initalizedRoot = new QDepartment(QDepartment.department.getMetadata(),
                PathInits.getFor(QDepartment.department.getMetadata(), new PathInits("*.*", "project.customer.company")));
---end change---
   predicateDepartment.and(
       QDepartment.department.project.customer.company.id.eq(idCompany)
   );

   return repository.findAll(predicateDepartment, pageable);
}

----2020 年 10 月 16 日更新----

我现在正面临一种奇怪的行为。 经过多次测试后,我无需在我的服务上使用PathInits 配置,只在我的实体中使用@QueryInit 和在我的存储库中使用customize(),就能够使一切正常运行...

我认为 lib 更新负责纠正这种情况。 但是在编译和运行几次之后(除了这个配置改变了其他的东西),customize() 已经不再被调用了。 在不触发customize() 的情况下调用list() 方法。

所以.. 我在我的服务中简单地插入了 PathInit 配置代码,现在又调用了 customize()

代码是这样的,customize() 没有在存储库上调用

public Page<Department> list(Predicate predicate, Pageable pageable){
   BooleanBuilder predicateDepartment = new BooleanBuilder(predicate);
        UUID idCompany = 
   springUserDetailsService.getUserSpring().getIdCompany();
   predicateDepartment.and(
       QDepartment.department.project.customer.company.id.eq(idCompany)
   );

   return repository.findAll(predicateDepartment, pageable);
}

所以我改成了这个,现在customize() 在存储库上被调用:

public Page<Department> list(Predicate predicate, Pageable pageable){
   BooleanBuilder predicateDepartment = new BooleanBuilder(predicate);
        UUID idCompany = 
   springUserDetailsService.getUserSpring().getIdCompany();

   QDepartment qTest = new QDepartment(QDepartment.department.getMetadata(), PathInits.DIRECT2);

   predicateDepartment.and(
       QDepartment.department.project.customer.company.id.eq(idCompany)
   );

   return repository.findAll(predicateDepartment, pageable);
}

qTest 变量已初始化,但并未在任何地方使用,但它会影响行为。 看起来new QDepartment(...) 是强制调用customize() 的关键。

--- 另一个更新,更多信息 ---

上面的代码只有在我处于调试模式时才有效。

---最终解决方案---

解决这个问题我要做的就是将绑定到我的控制器上的存储库bindings = DepartmentRepository.class

public Page<Department> list(@QuerydslPredicate(root = Department.class, bindings = DepartmentRepository.class) Predicate predicate,
                                 @PageableDefault Pageable pageable) {
    return service.list(predicate, pageable);
}

【问题讨论】:

    标签: java spring querydsl


    【解决方案1】:

    可能spring-data 忽略了在 QueryDSL 静态元模型中声明的默认 QueryInits。没有什么能阻止您将 EntityPath 表达式包装在另一个具有更多初始化路径的 EntityPath 表达式中:

    default public void customize(QuerydslBindings bindings, QDepartment root) {
         QDepartment initalizedRoot = new QDepartment(root)
         // or
         QDepartment initalizedRoot = new QDepartment(root.getMetadata(),  PathInits.getFor(root.getMetadata(), new PathInits("*.*", "project.customer.company")));
    
        // use initializedRoot.project.customer.company
    }
    
    

    【讨论】:

    • 问题是我在服务上使用 QDepartment.project.customer.company.eq(idCompany) 并且之后调用存储库(所以我得到空指针异常,因为路径是尚未配置)
    • 我可以在课堂上使用任何注释来强制 PathInits INITS = {".", "project.customer.company"}?当我没有 QueryInit 时,PathInit INITS = ".",但是当我添加 QueryInit 时,它变成 {"", "project.customer.company"}(带有单个 * ,而不是 *.)
    • 嗯很难看到发生了什么,我实际上希望在此处初始化路径。你可以试试@PathInits({"*". "*.*", "*.*.*", "*.*.*.*", "*.*.*.*.*"})吗?
    • @PathInits 是注解吗?找不到
    • com.querydsl.core.types.dsl.PathInits,它的一个类。
    【解决方案2】:

    在这里找到解决方案 https://github.com/Cepr0/sb-querydsl-sd-demo

    要使自定义工作,您需要将控制器谓词绑定到存储库

    bindings = DepartmentRepository.class

    @GetMapping
    @ResponseBody
    public Page<Department> list(@QuerydslPredicate(root = Department.class, bindings = DepartmentRepository.class) Predicate predicate,
                                 @PageableDefault Pageable pageable) {
        return service.list(predicate, pageable);
    }
    

    【讨论】:

      猜你喜欢
      • 2019-01-19
      • 2020-01-02
      • 2013-09-07
      • 1970-01-01
      • 1970-01-01
      • 2021-07-19
      • 2019-02-20
      • 2018-05-04
      • 2011-09-10
      相关资源
      最近更新 更多