Как вставить подсказку «Оптимизатор» в запрос Hibernate критериев api

У меня есть запрос на спящий режим, который динамически объединяется с использованием критериев api. он генерирует запросы, которые невыносимо медленны, если они выполняются как есть.

но я отметил, что они примерно на 1000% быстрее, если я добавлю / * + FIRST_ROWS (10) * / к запросу. как я могу сделать это с помощью критериев api?

Я пробовал критерии.setComment (..), но это, кажется, игнорируется.

в документах спящего режима, 3.4.1.7. Указаны подсказки, но в нем четко сказано: «Обратите внимание, что это не подсказки SQL-запросов»

результат запроса будет разбит на страницы, поэтому в 99% случаев я покажу результаты 1-10.

Вы можете изменить режим оптимизатора на уровне сеанса:

ALTER SESSION SET optimizer_mode = FIRST_ROWS; 

Либо перед вашим запросом, либо после его возврата к значению по умолчанию ( ALL_ROWS ) или в вашем случае, так как 99% запросов выиграют от него, вы можете изменить его на уровне схемы (например, с помощью запуска ON LOGON ) или даже на уровне экземпляра (изменить параметр init).

Я смог добавить подсказку Oracle, добавив к критериям критерий ProjectionList.

 ProjectionList proList = Projections.projectionList(); proList.add(Projections.sqlProjection("/*+INDEX_DESC(this_ MY_INDEX_NAME)*/ 1 as MYHINT", new String[]{}, new Type[]{})); //add properties from your class proList.add(Projections.property("field1")); proList.add(Projections.property("field2")); proList.add(Projections.property("field3")); c.setProjection(proList); 

c.list() возвращает List<Object[]> в порядке ProjectionList

У меня есть еще одно общее решение, которое должно работать для каждого запроса Criteria:
используйте стандартный комментарий и перехватчик Hibernate, изменяя конечный SQL в базу данных.
(Я использовал его с Hibernate 3.3, но должен использоваться для каждой версии, регистрация Interceptor может отличаться.)

В коде запроса используйте:

 criteria.setComment("$HINT$ push_pred(viewAlias)"); 

Напишите перехватчик, чтобы изменить текст SQL (этот использует commons.lang3.StringUtils):

 public class HibernateEntityInterceptor extends EmptyInterceptor { @Override public String onPrepareStatement(String sql) { if (sql.startsWith("/* $HINT$")) { String hintText = StringUtils.substringBetween(sql, "/* $HINT$", "*/"); sql = sql.replaceFirst("select ", "select /*+" + hintText + "*/ "); } return sql; } 

Выше для Oracle, но его легко настраивать для каждой СУБД.
Возможно, вы можете / должны создать константу для маркера подсказки «$ HINT $».
Ведение журнала также должно выполняться (так что вы можете легко увидеть правильное обращение перехватчика), я оставил его выше для простоты.

Перехватчик должен быть зарегистрирован. Весной это делается в applicationContext.xml :

 <bean id="entityListener" class="your.package.HibernateEntityInterceptor"/> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="entityInterceptor" ref="entityListener"/> [...] 

Или (копия из документов Hibernate 3.3):

Перехватчик, ограниченный сеансом, указывается при открытии сеанса с использованием одного из перегруженных методов SessionFactory.openSession (), принимающих Interceptor.

Session session = sf.openSession( new HibernateEntityInterceptor() );

Перехватчик SessionFactory зарегистрирован в объекте Configuration до создания SessionFactory. Если сеанс не открывается, явно указывая используемый перехватчик, поставляемый перехватчик будет применяться ко всем сеансам, открытым из этого SessionFactory. Перехватчики SessionFactory должны быть потокобезопасными. Убедитесь, что вы не сохраняете состояния, зависящие от сеанса, поскольку несколько сеансов будут использовать этот перехватчик потенциально одновременно.

new Configuration().setInterceptor( new HibernateEntityInterceptor() );

Проблема в том, что синтаксис подсказки не является комментарием, он просто немного похож на один. Это действительно должно идти между SELECT и выбранными столбцами, тогда как setComment() добавляет комментарий перед SELECT .

Помимо этого, нет серебряных пуль. FIRST_ROWS не является инструментом повышения производительности. Это может занять больше времени, чтобы вернуть все строки. Конечно, в программе, ориентированной на пользователя, получение первых десяти строк может быть всем, что нам нужно сделать.

Но, когда бы вы ни отказывались от него, если вы хотите использовать синтаксис подсказок Oracle, вам нужно будет спуститься по Native SQL-маршруту.

Что еще можно сделать? У меня пока нет опыта настройки Hibernate. Один раз, когда у меня была такая задача, запрос захватывал строки из целого ряда таблиц, чтобы создать экземпляр объекта с большим количеством подтипов. Каждый подтип представлял собой отдельную таблицу. В запросе, создаваемом Hibernate, было много OUTER JOINs, которые путали оптимизатор. Разрушение этого монстра на несколько сфокусированных запросов (по одному на подтип), которые использовали только INNER JOINs, привело к стократному сокращению времени поиска.

Это может быть нецелесообразно для вас. Но в принципе, посмотрите на запрос Hibernate и посмотрите, можно ли его реализовать другим, более эффективным способом.