Spring JPA条件谓词、StringPath、Querydsl中的自定义sql查询

Spring JPA条件谓词、StringPath、Querydsl中的自定义sql查询,spring,postgresql,hibernate,jpa,querydsl,Spring,Postgresql,Hibernate,Jpa,Querydsl,我有一个带有hibernate 5.4.12、Java 11和Postgres的Spring boot项目 我正在尝试使用JPA和Querydsl构建一个自定义排序/过滤机制,这是一个供参考的博客 我们有一个gin索引列,用于postgres的全文搜索功能。在jpa存储库中,我可以很容易地查询该列,如下所示 @Query(value = "select * from products where query_token @@ plainto_tsquery(:query)", nativeQue

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

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

我们有一个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);
我的
搜索条件

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;
  }
}
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();
  }
}
在这些相同的行中,我也在假设/期待文本搜索

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

您应该首先为ORM注册一个自定义函数,使
@
操作符可用。然后您可以在JPQL查询中执行
plainto\u tsquery(query\u token,:query)
。如何注册自定义函数取决于您使用的ORM。假设您使用Hibernate,您可能最好使用
MetadataContributor
SPI,因为通过
方言注册的函数在底层SQL呈现AFAIK方面灵活性较低


然后,如果您想在QueryDSL中使用它,您必须创建一个自定义的
操作符
,并在
JPQLTemplates
的子类中为该
操作符
注册一个
模板。或者,您可以使用一个简单的
TemplateExpression
expressions.booleanstemplate(“plainto_tsquery({0},{1})”,QProduct.product.queryToken,query)绕过
操作
表达式,它返回一个谓词。

您应该首先为ORM注册一个自定义函数,使
@
操作符可用。然后您可以在JPQL查询中执行
plainto\u tsquery(query\u token,:query)
。如何注册自定义函数取决于您使用的ORM。假设您使用Hibernate,您可能最好使用
MetadataContributor
SPI,因为通过
方言注册的函数在底层SQL呈现AFAIK方面灵活性较低


然后,如果您想在QueryDSL中使用它,您必须创建一个自定义的
操作符
,并在
JPQLTemplates
的子类中为该
操作符
注册一个
模板。或者,您可以使用一个简单的
TemplateExpression
expressions.booleanstemplate(“plainto_tsquery({0},{1})”,QProduct.product.queryToken,query)
绕过
操作表达式,它返回一个谓词。

谢谢您的回答。它看起来确实很有希望,但是,它有点理论性。我对这个框架相当陌生,不熟悉上面提到的类和特性。谢谢,如果你能用一些实现来解释的话。另外,您的行
或者,您可以绕过…
,这是一个不同的解决方案还是只是前面解释的扩展?因为,如果只需通过
模板
,而不注册任何内容,就可以解决问题,这将是最好的。(仅供参考,我使用的是hibernate)您在这里混合使用了三种框架,再加上数据库供应商特有的方言功能。这使得编写一个完整的例子相当困难。如果您共享您的代码,我可以发布所需的更改。但是,对于QueryDSL部分:表达式(哪些操作)使用“模板”呈现给JPQL片段。您可以在使用的模板中为新添加的运算符定义模板,也可以通过使用模板表达式绕过模板,模板表达式基本上是JPQL的一个片段,带有一些基本的变量替换。我添加了一些到目前为止实现的类,供您参考。希望能有帮助。另外,添加了一个伪代码case语句,这将是一个理想的实现。它看起来确实很有希望,但是,它有点理论性。我对这个框架相当陌生,不熟悉上面提到的类和特性。谢谢,如果你能用一些实现来解释的话。另外,您的行
或者,您可以绕过…
,这是一个不同的解决方案还是只是前面解释的扩展?因为,如果只需通过
模板
,而不注册任何内容,就可以解决问题,这将是最好的。(仅供参考,我使用的是hibernate)您在这里混合使用了三种框架,再加上数据库供应商特有的方言功能。这使得编写一个完整的例子相当困难。如果您共享您的代码,我可以发布所需的更改。但是,对于QueryDSL部分:表达式(哪些操作)使用“模板”呈现给JPQL片段。您可以在使用的模板中为新添加的运算符定义模板,也可以通过使用模板表达式绕过模板,模板表达式基本上是JPQL的一个片段,带有一些基本的变量替换。我添加了一些到目前为止实现的类,供您参考。希望能有帮助。另外,添加了一个伪代码case语句,这将是一个理想的实现。
case Search:
  FtsPath ftsPath = entityPath.getFtsPath("query_token");
  return ftsPath.search("some search string")