пользовательские sql запросы в JPA Criteria Predicate, StringPath, Querydsl - PullRequest
0 голосов
/ 17 июня 2020

У меня есть проект загрузки Spring с hibernate 5.4.12, Java 11 и Postgres.

Я пытаюсь создать собственный механизм сортировки / фильтрации с использованием JPA и Querydsl, здесь - это один блог для справки.

У нас есть столбец индекса джина, который используется для функции полнотекстового поиска по postgres. В репозитории jpa я могу легко запросить столбец, как показано ниже

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

Я знаю, что запросы fts еще не поддерживаются критериями JPA или API querydsl (я могу ошибаться). Поскольку обычная фильтрация logi c будет go через API критериев, как добавить возможности fts в API критериев? Есть ли способ добавить собственный собственный запрос в качестве предиката или StringPath или любых других путей Qtype?

UPDATE

My SearchPredicate class

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;
  }
}

My SearchCriteria class

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();
  }
}

And My PathType Enum

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

В этих же строках я предполагаю / ожидаю что-то для текстового поиска, например

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

1 Ответ

1 голос
/ 17 июня 2020

Сначала вы должны сделать доступным оператор @@, зарегистрировав пользовательскую функцию для вашего ORM. Затем вы можете сделать plainto_tsquery(query_token, :query) в своем запросе JPQL. Как зарегистрировать пользовательскую функцию, зависит от используемой ORM. Предполагая, что вы используете Hibernate, вам, вероятно, лучше всего использовать MetadataContributor SPI, потому что функции, зарегистрированные через Dialect, имеют меньшую гибкость в отношении базового SQL рендеринга AFAIK.

Затем, если вы хотите чтобы использовать это в QueryDSL, вам нужно будет создать собственный Operator и зарегистрировать Template для этого Operator в подклассе JPQLTemplates. В качестве альтернативы вы можете обойти выражения Operation, используя простой TemplateExpression: Expressions.booleanTemplate("plainto_tsquery({0}, {1})", QProduct.product.queryToken, query), который возвращает предикат.

...