【问题标题】:custom sql queries in JPA Criteria Predicate, StringPath, QuerydslJPA Criteria Predicate、StringPath、Querydsl 中的自定义 sql 查询
【发布时间】:2020-10-06 20:23:58
【问题描述】:

我有一个带有 hibernate 5.4.12、Java 11 和 Postgres 的 Spring Boot 项目。

我正在尝试使用 JPA 和 Querydsl 构建自定义排序/过滤机制,here 是一个可供参考的博客。

我们有一个 gin 索引列,用于 postgres 的全文搜索功能。在 jpa 存储库中,我可以轻松查询该列,如下所示

@Query(value = "select * from products where query_token @@ plainto_tsquery(:query)", nativeQuery = true)
Page<Product> findAllByTextSearch(@Param("query") String query, Pageable pageable);

我知道 JPA 标准或 querydsl API 尚不支持 fts 查询(我可能错了)。既然正常的过滤逻辑会通过criteria API,那么如何在criteria API中添加fts能力呢?有没有办法将自定义本机查询添加为谓词或 StringPath 或任何其他 Qtype 路径?

更新

我的SearchPredicate 班级

public class SearchPredicate<E extends Enum<E>> {

  private SearchCriteria<E> searchCriteria;

  public <T> BooleanExpression getPredicate(Class<T> entityClass, String entityName) {
    PathBuilder<T> entityPath = new PathBuilder<>(entityClass, entityName);

    switch (searchCriteria.getPathType()) {
      case String:
        StringPath stringPath = entityPath.getString(searchCriteria.getKey());
        return stringPath.eq(searchCriteria.getStringValue());

      case Enum:
        return entityPath.getEnum(searchCriteria.getKey(), searchCriteria.getEnumClass())
            .eq(Enum.valueOf(searchCriteria.getEnumClass(), searchCriteria.getStringValue()));

      case Float:
        NumberPath<Float> floatPath = entityPath.getNumber(searchCriteria.getKey(), Float.class);
        Float floatValue = Float.parseFloat(searchCriteria.getStringValue());
        return floatPath.eq(floatValue);

      case Integer:
        NumberPath<Integer> integerPath = entityPath.getNumber(searchCriteria.getKey(), Integer.class);
        Integer integerValue = Integer.parseInt(searchCriteria.getStringValue());
        return integerPath.eq(integerValue);
    }
    return null;
  }
}

我的SearchCriteria 班级

public class SearchCriteria<E extends Enum<E>> {
  private String key;
  private Object value;
  private PathType pathType;
  private Class<E> enumClass;

  public String getStringValue() {
    return value.toString();
  }
}

还有我的 PathType 枚举

public enum PathType {
  String, Enum, Integer, Float;
}

在这些相同的行中,我假设/期待一些文本搜索的内容,例如

case Search:
  FtsPath ftsPath = entityPath.getFtsPath("query_token");
  return ftsPath.search("some search string")

【问题讨论】:

    标签: spring postgresql hibernate jpa querydsl


    【解决方案1】:

    您应该首先通过为您的 ORM 注册一个自定义函数来使 @@ 运算符可用。然后您可以在您的 JPQL 查询中执行 plainto_tsquery(query_token, :query)。如何注册自定义函数取决于您使用的 ORM。假设您使用 Hibernate,您可能最好使用 MetadataContributor SPI,因为通过 Dialect 注册的函数在底层 SQL 渲染 AFAIK 方面的灵活性较低。

    然后,如果您想在 QueryDSL 中使用它,您必须创建一个自定义 Operator 并在 JPQLTemplates 的子类中为该 Operator 注册一个 Template。或者,您可以使用简单的TemplateExpression 绕过Operation 表达式:Expressions.booleanTemplate("plainto_tsquery({0}, {1})", QProduct.product.queryToken, query),它返回一个谓词。

    【讨论】:

    • 感谢您的回答。它看起来确实很有希望,但是,它有点理论化。我对框架相当陌生,不熟悉您上面提到的类和功能。感激不尽,如果你能用一些实现来解释它。另外,您的行Alternatively, you can bypass...,它是不同的解决方案还是只是前面解释的扩展?因为,最好能通过Template解决,不用注册任何东西。 (仅供参考,我正在使用休眠)
    • 您在这里混合使用了三个框架以及数据库供应商特定的方言函数。这使得制作一个完整的例子变得相当困难。如果您分享您的代码,我可以发布所需的更改。但是,对于 QueryDSL 部分:使用“模板”将表达式(哪些操作)呈现为 JPQL 片段。您可以在使用的模板中为新添加的运算符定义模板,或者使用模板表达式绕过模板,这基本上是 JPQL 的一个片段,带有一些基本的变量替换。
    • 我已经添加了几类到目前为止我已经实现的内容供您参考。希望能帮助到你。此外,添加了一个伪代码案例语句作为理想的实现。
    猜你喜欢
    • 2019-03-13
    • 1970-01-01
    • 1970-01-01
    • 2019-03-10
    • 2017-04-06
    • 2021-02-14
    • 2021-10-02
    • 2019-04-18
    • 1970-01-01
    相关资源
    最近更新 更多