问题:同时使用QuerydslBinderCustomizer和@QueryInit会有问题。

huangapple go评论80阅读模式
英文:

Problem using QuerydslBinderCustomizer and @QueryInit at the same time

问题

给出以下类:

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 来能够访问第四层或更多层次,像这样:

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.querydslquerydsl-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();

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

   return repository.findAll(predicateDepartment, pageable);
}

更新于 2020 年 10 月 16 日

我现在面临一个奇怪的情况。
经过多次测试,我成功地在不使用服务中的 PathInits 配置的情况下使一切正常工作,只使用了实体中的 @QueryInit 和存储库中的 customize()

我以为库的更新会纠正这种情况。
但在编译和运行了几次后(改变了其他内容但没有改变这个配置),customize() 不再被调用。
list() 方法在不触发 customize() 的情况下被调用。

所以…我在服务中插入了 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);
}
英文:

Giving the following classes

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

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

public class Customer {
    ...

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

I need to use @QueryInit on project to be able to access the 4th level or more, like this:

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

At the same time, I need to use QuerydslBinderCustomizer so I can customize the filter behavior

public interface DepartmentRepository
		extends CrudRepository&lt;Department, UUID&gt;, 
		PagingAndSortingRepository&lt;Department, UUID&gt;,
		QuerydslPredicateExecutor&lt;Department&gt;, 
		QuerydslBinderCustomizer&lt;QDepartment&gt; {

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

When I use the @QueryInit, the customize is not called and my customization doesn't work

I've already tried to update the the last Querydsl version 4.4.0 and add the com.mysema.querydsl querydsl-apt but does not work anyway


Currently I'm using the predicate on service level, like this

public Page&lt;Department&gt; 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);
}

So I think I can't configure the EntityPath inside the customize(), because it's called when the repository is called


The solution applyed to my code:

public Page&lt;Department&gt; 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(&quot;*.*&quot;, &quot;project.customer.company&quot;)));
---end change---
   predicateDepartment.and(
       QDepartment.department.project.customer.company.id.eq(idCompany)
   );

   return repository.findAll(predicateDepartment, pageable);
}

----Update 10/16/2020----

I'm facing a weird behavior now.
After many tests I was able to make everything works right without using the PathInits configuration on my service, only using the @QueryInit in my Entity and the customize() in my Repository...

I thought the lib update was responsible to correct the situation.
But after compiling and running a couple of times (changing other things but this config), the customize() has not been called anymore.
The list() method is called without trigger the customize().

So.. I made a simple insert of the PathInit config code in my service and now the customize() has been called again.

The code was this, and the customize() is not called on repository

public Page&lt;Department&gt; 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);
}

So I changed to this, and now the customize() is called on repository:

public Page&lt;Department&gt; 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);
}

The qTest variable is initialized but is not used anywhere, but its affecting the behavior even so.
It looks like the new QDepartment(...) is the key to force the customize() to be called.

--- Another update, more info ---

The code above works only if I'm on debug mode.

--- Final solution ---

All I had to do to solve this problem is to put the binding to the Repository bindings = DepartmentRepository.class on my Controller.

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

答案1

得分: 1

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

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

    // 使用 initializedRoot.project.customer.company
}
英文:

Probably spring-data ignores the default QueryInits declared in the QueryDSL static metamodel. Nothing prevents you from wrapping an EntityPath expression in another EntityPath expression with more initialized paths:

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(&quot;*.*&quot;, &quot;project.customer.company&quot;)));

    // use initializedRoot.project.customer.company
}

答案2

得分: 0

在这里找到了解决方案
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);
}
英文:

Got the solution here
https://github.com/Cepr0/sb-querydsl-sd-demo

To have the customize to work you need to bind the controller Predicate to the Repository

bindings = DepartmentRepository.class

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

huangapple
  • 本文由 发表于 2020年10月14日 20:04:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/64352848.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定