Как вставить «подсказку оптимизатора» в API-запрос критериев гибернации

у меня есть запрос гибернации, который динамически составляется с использованием критериев API. он генерирует запросы, которые невыносимо медленны, если они выполняются как есть.

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

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

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

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

Ответы на вопрос(4)

добавив в критерии список 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 в порядке ProjectionList

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

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

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

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

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

 Andreas Petersson25 авг. 2009 г., 14:18
результат действительно будет пользовательским.
 Andreas Petersson25 авг. 2009 г., 14:21
сам запрос верен так, как есть. поскольку он генерируется динамически, его нельзя оптимизировать вручную, он отличается для каждого поискового запроса.
Решение Вопроса

ALTER SESSION SET optimizer_mode = FIRST_ROWS;

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

которое должно работать для каждого запроса Criteria:

используйте стандартный комментарий и перехватчик Hibernate, изменяющий окончательный SQL для базы данных.

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

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

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

Напишите Interceptor, чтобы перейти к тексту 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, но должно быть легко настраивается для каждой СУБД.

Может быть, вы можете / должны создать константу для маркера подсказки "$ ПОДСКАЗКА $».

Ведение журнала также должно быть выполнено (чтобы вы могли легко увидеть правильный вызов Interceptor), я упустил это выше для простоты.

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

<bean id="entityListener" class="your.package.HibernateEntityInterceptor">

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

Или (скопируйте из документа Hibernate 3.3):

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

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

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

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

Ваш ответ на вопрос