Как вставить «подсказку оптимизатора» в API-запрос критерия гибернации - PullRequest
7 голосов
/ 25 августа 2009

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

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

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

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

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

Ответы [ 4 ]

6 голосов
/ 16 июля 2011

Мне удалось добавить подсказку 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

5 голосов
/ 13 мая 2016

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

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

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 () принимает перехватчик.

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

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

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

5 голосов
/ 25 августа 2009

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

ALTER SESSION SET optimizer_mode = FIRST_ROWS;

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

1 голос
/ 25 августа 2009

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...