2009-08-25 16 views
7

Mam zapytanie hibernacji, które jest dynamicznie łączone przy użyciu kryterium api. generuje zapytania, które są nieznośnie powolne, jeśli są wykonywane w niezmienionej postaci.Jak wstawić "Wskazówkę optymalizatora" do zapytania Kryteria Hibernate API api

ale zauważyłem, że są one o około 1000% szybsze, jeśli dodaję/* + FIRST_ROWS (10) */do zapytania. jak mogę to zrobić z api kryteriów?

Próbowałem criteria.setComment (..), ale wydaje się to być ignorowane.

w dokumentach do hibernacji, 3.4.1.7. Wskazówki dotyczące kwerendy są wymienione, ale wyraźnie stwierdza: "Należy zauważyć, że nie są to podpowiedzi do zapytań SQL"

wynik zapytania będzie podzielony na strony, więc w 99% przypadków będą wyświetlane wyniki 1-10.

Odpowiedz

5

Można zmienić tryb optymalizatora na poziomie sesji:

ALTER SESSION SET optimizer_mode = FIRST_ROWS; 

Albo tuż przed zapytania, a następnie umieszczenie go z powrotem do jego wartości domyślnej (ALL_ROWS) lub w przypadku, ponieważ 99% zapytań będzie skorzystaj z niego, możesz go zmodyfikować na poziomie schematu (z np. wyzwalaczem ON LOGON) lub nawet na poziomie instancji (zmodyfikuj parametr init).

1

Problem polega na tym, że składnia podpowiedzi nie jest komentarzem, po prostu wygląda trochę jak jedna. To naprawdę musi przejść między SELECT i wybranych kolumn, natomiast setComment() dodaje komentarz przed SELECT.

Poza tym nie ma srebrnych kul. FIRST_ROWS nie jest narzędziem zwiększającym wydajność. Może to potrwać dłużej, aby uzyskać wiersze z powrotem. Oczywiście w programie skierowanym do użytkownika, pierwsze dziesięć wierszy może być wszystkim, co musimy zrobić.

Ale w jaki sposób można go odbijać, jeśli chcesz użyć składni wskazującej Oracle, musisz przejść przez natywną trasę SQL.

Co jeszcze można zrobić? Nie mam (jeszcze) dużego doświadczenia w dostrajaniu Hibernacji. Jednorazowo pod takim zadaniem zapytanie pobierało wiersze z całej gamy tabel, aby utworzyć instancję z wieloma podtypami. Każdy podtyp był osobną tabelą. Zapytanie generowane przez Hibernate miało wiele ZEWNĘTRZNYCH POŁĄCZEŃ, które pomylili się z optymalizacją. Przełamanie tego potwora na kilka skoncentrowanych zapytań (jeden na podtyp), które wykorzystywały tylko INNER JOINs, dało dwustokrotną redukcję czasu wyszukiwania.

Ta funkcja może nie być natychmiast używana. Ale zasadą jest, spójrz na kwerendę Hibernate i zobacz, czy można ją zaimplementować w inny, bardziej wydajny sposób.

+0

wynik rzeczywiście będzie użytkownik twarzy. –

+0

samo zapytanie jest poprawne, tak jak jest. ponieważ jest generowany dynamicznie, nie można go ręcznie zoptymalizować, jest inny w przypadku każdego zapytania wyszukiwania. –

6

Udało mi się wprowadzić wskazówkę dotyczącą Oracle przez dodanie ProjectionList do kryteriów.

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() powraca List<Object[]> w kolejności ProjectionList

5

mam inny rodzajowe rozwiązanie, które powinno działać na każdym zapytaniu kryteria:
użyć standardowego komentarza oraz hibernacji Interceptor zmienia ostateczny SQL do bazy danych.
(Użyłem go z Hibernate 3.3, ale powinien być użyteczny dla każdej wersji, rejestracja Interceptora może być inna.)

w zapytaniu użyć kodu:

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

napisać Interceptor, aby zmienić tekst SQL (ten wykorzystuje 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; 
} 

powyżej jest dla Oracle, ale powinien być łatwo regulowany dla każdego DBMS.
Może możesz/powinieneś utworzyć stałą dla znacznika podpowiedzi "$ HINT $".
Należy również wykonać rejestrowanie (aby można było łatwo zobaczyć poprawne wywoływanie Interceptora), zostawiłem to powyżej dla uproszczenia.

Interceptor musi być zarejestrowany. Wiosną odbywa się to w applicationContext.xml:

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

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

Albo (kopia z Hibernate 3.3 docs):

Sesja o zasięgu Interceptor jest określona, ​​gdy sesja jest otwarta stosując jeden z przeciążonej SessionFactory Metody .openSession() akceptowanie Interceptora.

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

SessionFactory o zasięgu Interceptor jest zarejestrowany w obiekcie konfiguracji sprzed budowy SessionFactory. O ile sesja nie zostanie otwarta, określając bezpośrednio używany przechwytujący, dostarczony przechwytywacz zostanie zastosowany do wszystkich sesji otwartych z tego obiektu SessionFactory o wartości . Obiekty przechwytujące SessionFactory muszą być bezpieczne dla wątku . Upewnij się, że nie przechowujesz stanów specyficznych dla sesji, ponieważ wiele sesji będzie używać jednocześnie tego obiektu przechwytującego.

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

Powiązane problemy