2009-08-25 3 views
7

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

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

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

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

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

ответ

5

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

ALTER SESSION SET optimizer_mode = FIRST_ROWS; 

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

1

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

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

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

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

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

+0

результат действительно будет пользователь лицом. –

+0

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

6

Я смог установить подсказку 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

У меня есть еще одно общее решение, которое должно работать для каждого Критерии запроса:
использовать стандартный комментарий и гибернации перехватчик изменения конечной 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 Docs):

Сеанс-Scoped перехватчик указывается, когда сеанс открыт используя один из перегруженных SessionFactory .openSession() прием перехватчика.

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

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

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