如何在QueryDSL Web上使用不同的运算符?

时间:2018-01-22 09:11:41

标签: spring spring-boot spring-data-jpa querydsl

我在Spring Boot项目中使用QueryDSL并计划使用Spring的Web支持(current query dsl web docs)。问题是,我无法找到使用不同运营商的任何信息。如何定义not equalsmatches regex操作?乍一看,它只是将您的?fieldname=value格式GET请求转换为您在存储库中设置的预定义操作。我可以以允许同一字段的多个操作的方式扩展它吗?

示例:

目前,我可以通过传递URL参数来获取QueryDsl Predicate,例如?user.company.id=1

@Controller
class UserController {

  @Autowired UserRepository repository;

  @RequestMapping(value = "/", method = RequestMethod.GET)
  Page<User> getUsers(@QuerydslPredicate(root = User.class) Predicate predicate,    
          Pageable pageable) {

    return repository.findAll(predicate, pageable);
  }
}

但是,由于我链接的文档状态,我只能为某个字段定义单个操作。如果我想要用户,user.lastName从哪里开始并且仍然可以查询完全匹配的内容,该怎么办? (?lastName=Xyz,contains?lastName=Xyz,equals也许)

QuerydslBinderCustomizer定义每个字段的操作,但您只能定义如何处理该特定字段,不可能添加多个操作。

也许我不能用QueryDSL做到这一点,但是一般在Spring启动时如何将过滤器应用于搜索查询?

3 个答案:

答案 0 :(得分:3)

我正在做那样的事情。虽然我尝试做更复杂的动作时遇到了一些限制。我在一些步骤中做了什么:

  • 创建一个扩展MyBinderCustomizer<T extends EntityPath<?>>的新界面QuerydslBinderCustomizer<QUser>(请注意用户的Q,您需要QueryDSL自动生成的类而不是您的实体。)
  • 实施customize方法。例如:

@Override
public default void customize(QuerydslBindings bindings, T root) {
    bindings.bind(String.class).all(MyBinderCustomizer::applyStringComparison);
}

static BooleanExpression applyStringComparison(Path<String> path, Collection<? extends String> strings) {
    BooleanExpression result = null;

    for (String s : strings) {
        try {
            final String[] parts = s.split(",");
            final String operator = parts[0];
            final String value = parts.length > 1 ? parts[1] : null;

            final Method method = Arrays.stream(path.getClass().getMethods())
                    .filter(m -> operator.equals(m.getName()))
                    .filter(m -> BooleanExpression.class.equals(m.getReturnType()))
                    .filter(m -> m.getParameterTypes().length == (value == null ? 0 : 1))
                    .filter(m -> value == null || m.getParameterTypes()[0].equals(String.class) || m.getParameterTypes()[0].equals(Object.class))
                    .findFirst().get();

            final BooleanExpression be;
            if (value == null) {
                be = (BooleanExpression) method.invoke(path);
            } else {
                be = (BooleanExpression) method.invoke(path, value);
            }

            result = result == null ? be : result.and(be);

        } catch (Throwable t) {
            throw new RuntimeException(t);
        }
    }

    return result;

}
  • 请注意,您应该更改值/运算符顺序,以便可以调用非值运算符,如isNull。
  • 您的存储库必须扩展MyBinderCustomizer<QUser>(再次注意Q)。

这将允许您使用这些操作:


    public BooleanExpression StringExpression.like(java.lang.String)
    public BooleanExpression StringExpression.notLike(java.lang.String)
    public BooleanExpression StringExpression.notEqualsIgnoreCase(java.lang.String)
    public BooleanExpression StringExpression.containsIgnoreCase(java.lang.String)
    public BooleanExpression StringExpression.likeIgnoreCase(java.lang.String)
    public BooleanExpression StringExpression.startsWithIgnoreCase(java.lang.String)
    public BooleanExpression StringExpression.endsWithIgnoreCase(java.lang.String)
    public BooleanExpression StringExpression.equalsIgnoreCase(java.lang.String)
    public BooleanExpression StringExpression.startsWith(java.lang.String)
    public BooleanExpression StringExpression.endsWith(java.lang.String)
    public BooleanExpression StringExpression.matches(java.lang.String)
    public BooleanExpression StringExpression.contains(java.lang.String)

    public BooleanExpression StringExpression.isEmpty()
    public BooleanExpression StringExpression.isNotEmpty()
    public BooleanExpression SimpleExpression.isNull()
    public BooleanExpression SimpleExpression.isNotNull()
    public BooleanExpression SimpleExpression.ne(java.lang.Object)
    public BooleanExpression SimpleExpression.eq(java.lang.Object)

答案 1 :(得分:0)

Spring Data QueryDSL Value Operators库扩展了Spring Data QueryDSL web support的操作符,不仅适用于字符串字段,而且还包含数字和枚举字段。它需要一些特殊的配置才能使其适用于非String字段,如here所述:

  

值运算符在基于字符串的属性/字段上似乎工作得很顺利。但是,这些运算符不适用于Number或Enum之类的非字符串值,因为默认情况下QuerydslPredicateArgumentResolver解析注释QuerydslPredicate,该注释用于注释RESTful方法(也称为RestController方法)上的搜索处理方法,根据Querydsl的指导设计原理执行强类型化,即,它尝试将从HTTP请求接收的值转换为在相应Q类中定义的确切类型。在没有值运算符的情况下,此方法效果很好,并且与Querydsl承诺允许类型安全查询的承诺保持一致,但阻碍了值运算符完成其任务的路径。

该库提供了两种方法来使运算符适用于非字符串字段:

  1. 一个从查询参数中提取运算符的过滤器,因此查询参数仍可以转换为相应的类型(使用强类型)
  2. QuerydslPredicateArgumentResolver中替换ConversionService,以便将所有查询参数都视为String(失去强类型)

这两种方法以及它们的用例和缺点都有很好的记录。

我目前正在评估方法1,因为它适合我们的用例,但是我需要扩展它以容纳DateTime字段以及一些自定义运算符。

答案 2 :(得分:0)

https://bitbucket.org/gt_tech/spring-data-querydsl-value-operators/src/master/

这里的文档说:

QuerydslPredicateArgumentResolver使用ConversionService进行类型转换。由于String到Enum或String到Integer的转换是Spring依赖项注入的核心,因此不建议更改这些默认的内置转换器(永远不要这样做)。该库提供了BeanPostProcessor和ServletFilter的实验性组合,可以在目标应用程序的上下文中对其进行显式配置,以禁用QuerydslPredicateArgumentResolver尝试的强类型转换。

因此,要实现此目的,您需要将其添加到应用程序上下文中:

/**
 * Note the use of delegate ConversionService which comes handy for types like
 * java.util.Date for handling powerful searches natively with Spring data.
 * @param factory QuerydslBindingsFactory instance
 * @param conversionServiceDelegate delegate ConversionService
 * @return
 */
@Bean
public QuerydslPredicateArgumentResolverBeanPostProcessor querydslPredicateArgumentResolverBeanPostProcessor(
        QuerydslBindingsFactory factory, DefaultFormattingConversionService conversionServiceDelegate) {
    return new QuerydslPredicateArgumentResolverBeanPostProcessor(factory, conversionServiceDelegate);
}

让我知道是否有人成功实现了这种实验性功能。