Пользовательский SQL для заказа в JPA Criteria API - PullRequest
0 голосов
/ 08 октября 2018

Я переключаюсь с устаревшего (к сожалению) API Hibernate Criteria на JPA Criteria API.У нас есть пользовательская реализация интерфейса Order (из Hibernate), чтобы переопределить сгенерированный для него SQL.Случай довольно сложный, так как нам нужно использовать гигантский SELECT с подзапросами.Мы реализовали toSqlString метод интерфейса для возврата этого огромного SQL, и нам нужен способ перенести его в JPA Criteria API.

Вопрос: есть ли способ в JPA Criteria API переопределить сгенерированный SQL?Или есть странный способ использовать Hibernate Order с JPA Criteria API?

Спасибо!

ОБНОВЛЕНИЕ Хотя предложение @Tobias Liefke довольно интересно, мой SQL тоже меняетсямного, чтобы создать класс функции для SQL.Я попытался реализовать один класс функций и передать туда SQL в качестве аргумента, но это не сработало (обработанный SQL был заключен в одинарные кавычки, поэтому он был отправлен в базу данных как параметр, а не как часть сгенерированного запроса)

1 Ответ

0 голосов
/ 09 октября 2018

Нельзя использовать фрагменты SQL в запросах JPQL или критериях ...

... кроме случаев, когда ...

1.Вызов функции

JPA и Hibernate позволяют использовать функции в их выражениях, например:

... ORDER BY trim(entity.label) ASC

Resp.

query.orderBy(criteriaBuilder.asc(
    criteriaBuilder.function("trim", String.class, root.get(ExampleEntity_.label))));

Проблема в том, что этона самом деле не вызов функции SQL trim, а вызов функции JPA, которая должна быть зарегистрирована (Hibernate делает это уже для наиболее распространенных функций SQL).

К счастью, вы можете определить свои собственные функции JPA в DialectResolver:

public class MyDialectResolver implements DialectResolver {

  public Dialect resolveDialect(final DialectResolutionInfo info) {
    Dialect dialect = StandardDialectResolver.INSTANCE.resolve(info);
    dialect.registerFunction("myOrderFunction", ...);
    return dialect;
  }

}

registerFunction принимает два параметра, первый - это имя функции в JPA, другой -сопоставление с SQL.

Не забудьте объявить свой преобразователь диалекта в вашем persistence.xml:

<persistence-unit name="database">
  <properties>
    <property name="hibernate.dialect_resolvers" 
              value="my.package.MyDialectResolver" />
  </properties>
</persistence-unit>

Теперь вы можете создать свою собственную функцию на своем сервере SQL, которая содержит ваш огромный SQL и зарегистрируйте это как функцию:

dialect.registerFunction("myOrderFunction", 
  new StandardSQLFunction("myOrderFunctionInSQL", StringType.INSTANCE));

Или вы можете написать свое собственное отображение, которое включает в себя ваш огромный SQL :

public class MyOrderFunction implements SQLFunction {

  public String render((Type firstArgumentType, List arguments,
      SessionFactoryImplementor factory) throws QueryException) {
    return my_huge_SQL;
  }

  // ...
}

И зарегистрируйте это:

dialect.registerFunction("myOrderFunction",  new MyOrderFunction());

Еще одно преимущество этого решения: вы можете определять различные SQL в зависимости от фактического диалекта базы данных.

2.Используя формулу

Вы можете использовать дополнительный атрибут для вашей сущности:

@Formula("my huge SQL")
private String orderAttribute;

Теперь вы можете сортировать по этому атрибуту:

... ORDER BY entity.orderAttribute ASC

Респ.

query.orderBy(criteriaBuilder.asc(root.get(ExampleEntity_.orderAttribute))));

Я рекомендую это решение, только если вам все равно нужен результат огромного SQL в вашей модели.В противном случае это только загрязнит вашу модель сущности и добавит SQL в каждый запрос вашей сущности (за исключением того, что вы пометите его @Basic(fetch = FetchType.lazy) и будете использовать инструментарий байт-кода ).

Аналогичным решением было бы определение сущности @Subselect с огромным SQL - с теми же недостатками.

...