Spring @QuerydslPredicate 和 QuerydslBinderCustomizer:是否可以将默认条件注入到从请求参数生成的谓词中?

Spring @QuerydslPredicate and QuerydslBinderCustomizer: is it possible to infuse default criteria into predicate generated from request params?

我正在使用 Spring Data JPAQueryDsl (v.4.2.2),Java 8. 我可以显式构造搜索谓词并将它们传递给存储库方法。但是,当查询的实体具有多个属性时,我喜欢在 web/REST 控制器的方法参数上使用 @QuerydslPredicate 注释的想法,并且我希望能够灵活地过滤其中任何一个的搜索。所以,像这样的东西通常效果很好:

    @GetMapping("/accounts/summaries")
    public PageDto<AccountSummaryDto> getAccountSummaries(@QuerydslPredicate(root = AccountSummary.class) Predicate accountSearchPredicate,
            @RequestParam(name = "pageIndex", defaultValue = "0") int pageIndex,
            @RequestParam(name = "pageSize", defaultValue = "25") int pageSize,
            @RequestParam(name = "sortBy", defaultValue = "id") String sortBy,
            @RequestParam(name = "sortOrder", defaultValue = "desc") String sortOrder) {

         // delegating to web-agnostic service that:
         // - creates Pageable pageRequest,     
         // - calls accountSummaryRepository.findAll(predicate, pageRequest), 
         // - constructs custom PageDto wrapper, etc. 
         return accountService.retrieveAccountSummaries(accountSearchPredicate, pageIndex, pageSize, sortBy, sortOrder); 
    }

我的 Spring Data JPA 存储库界面看起来与此类似:

    public interface AccountSummarySearchRepository
        extends JpaRepository<AccountSummary, Integer>, QuerydslPredicateExecutor<AccountSummary>, QuerydslBinderCustomizer<QAccountSummary > {

    @Override
    default void customize(QuerydslBindings bindings, QAccountSummary acctSummary) {
        bindings.bind(acctSummary.customer.firstName).first((path, value) -> path.isNull().or(path.startsWithIgnoreCase(value))) ;
        bindings.bind(acctSummary.customer.lastName).first((path, value) -> path.isNull().or(path.startsWithIgnoreCase(value))) ;
        
        // etc.

        // default binding for String properties to be case insensitive "contains" match
        bindings.bind(String.class).first(
                (StringPath path, String value) -> path.isNull().or(path.containsIgnoreCase(value)));
    }

我的问题:

The bindings in the customize method are set using the entity field paths and the values of the request parameters that match those paths. If the parameter is not specified, is there a way to bind the path to some constant value or a value obtained dynamically?

例如,我想始终 ONLY 检索 属性 deleted 设置为 false 的实体 - 不强制客户端将其作为查询参数传递?同样,我可能想为每个查询动态设置其他默认查找值。例如,我可能想“只检索那些 assignedTo == [current user ID available on a ThreadLocal]...

的帐户

以下将不起作用

        bindings.bind(acctSummary.deleted).first((path, value) -> path.eq(false));

因为它显然期望 第一次出现 path/value 对 for deleted=... 在谓词中(通过@QuerydslPredicate 注释。我不想将其作为参数传递,因为请求者甚至不需要知道此类字段的存在。

是否有一种简单的方法可以将通过 @QuerydslPredicate 注释自动填充的 Predicate 实例与未在 Web 请求中明确传递的任何其他 implicit/default 条件相结合?这可以在 customize 方法中完成吗?我想,一种(非常丑陋的)方法是在过滤器中拦截 HTTP 请求——在它被 Spring-QueryDsl 框架处理之前——并用添加了参数的新请求替换它?那将是一个糟糕的解决方案,我觉得必须有更好的方法来通过框架本身提供的一些 hook/capability 来做到这一点。

不幸的是,Spring QueryDsl 支持似乎没有全面的文档 - 除了一些非常简单的示例。

感谢您的帮助!

回答我自己的问题...我希望在框架中找到一个钩子,我可以在其中添加代码以增强自动生成的谓词,其标准适用于我所有的查询 - 在它到达控制器方法之前,但无法弄清楚。覆盖 QuerydslPredicateArgumentResolver 似乎不是一个好的或必要的选择。而且,坦率地说,我得出的结论是,这从一开始就不是一个好主意。似乎对搜索条件的任何修改都应该以更明显的方式进行 - 在业务层中。所以我决定简单地更新服务方法中的谓词:

public PageDto<AccountSummaryDto> retrieveByPredicate(Predicate predicate, int pageIndex, int pageSize, String sortBy, String sortOrder) {
     Pageable pageRequest = PageRequest.of(pageIndex, pageSize, Sort.Direction.fromString(sortOrder), sortBy);

     QAccountSummary accountSummary = QAccountSummary.accountSummary; //QueryDsl auto-generated query type for AccountSummary (path root)
     
     // construct new enhanced search predicate w/added criteria common for all queries 
     // using original predicate generated by framework from request params as base
     BooleanBuilder updatedPredicate = new BooleanBuilder(predicate)
        .and(accountSummary.somethingNested.id.eq(SomeThreadContext.getSomethingId()))
        .and(accountSummary.deleted.eq(false))
        .and(accountSummary.someProperty.eq("xyz"));

     Page<accountSummary> page = summarySearchRepository.findAll(updatedPredicate, pageRequest);
     return toAccountSummaryPageDto(page); // custom method that converts results to page DTO w/entity dots and page stats
}

如果需要更多逻辑来动态生成额外的搜索条件,如果希望在多个搜索方法中使用它,可以将更新谓词的构造提取到服务上的单独私有方法中。and/or。