2009-08-25 10 views
7

Ho una query di sospensione che viene creata dinamicamente utilizzando l'API dei criteri. genera query che sono insopportabilmente lente, se eseguite così com'è.Come inserire un "suggerimento di ottimizzazione" per i criteri di ibernazione query api

ma ho notato che sono circa il 1000% più veloce se antepone/* + FIRST_ROWS (10) */alla query. come posso fare questo con i criteri API?

ho provato criteria.setComment (..), ma questo sembra essere ignorato.

nella documentazione di ibernazione, 3.4.1.7. Query suggerimenti sono menzionati, ma afferma chiaramente: "Si noti che questi non sono suggerimenti di query SQL"

il risultato della query verrà impaginato, quindi nel 99% dei casi visualizzerò i risultati 1-10.

risposta

5

Si potrebbe modificare la modalità di ottimizzazione a livello di sessione:

ALTER SESSION SET optimizer_mode = FIRST_ROWS; 

In entrambi poco prima la query e poi rimetterlo al suo valore di default (ALL_ROWS) o nel vostro caso, poiché il 99% delle query avrebbe beneficiando di questo è possibile modificarlo a livello di schema (con un trigger ON LOGON per esempio) o anche a livello di istanza (modificare il parametro init).

1

Il problema è che la sintassi del suggerimento non è un commento, ma assomiglia un po 'a uno. Deve davvero passare tra lo SELECT e le colonne selezionate, mentre setComment() anteporre il commento prima dello SELECT.

Oltre a ciò, non ci sono proiettili d'argento. FIRST_ROWS non è uno strumento per migliorare le prestazioni. Potrebbe richiedere più tempo per ottenere tutte le righe indietro. Naturalmente, in un programma rivolto all'utente che recupera le prime dieci righe potrebbe essere tutto ciò che dobbiamo fare.

Ma, in qualsiasi modo lo si rimbalzi, se si desidera utilizzare la sintassi del suggerimento di Oracle è necessario seguire la rotta Native SQL.

Cos'altro si può fare? Non ho (ancora) molta esperienza nell'accordare Hibernate. L'unica volta che ho avuto questo compito, la query ha catturato le righe di un intero gruppo di tabelle per creare un'istanza di un oggetto con molti sottotipi. Ogni sottotipo era una tabella separata. La query generata da Hibernate ha avuto molti OUTER JOIN, che hanno confuso l'ottimizzazione dell'ottimizzatore. Rompendo quel mostro in diverse query focalizzate (una per sottotipo) che usava solo JOIN INNER ha prodotto una riduzione di duecento volte del tempo di recupero.

Questo potrebbe non essere di alcun uso immediato per voi. Ma il principio è, guarda la query di Hibernate e vedi se può essere implementata in un modo diverso, più efficiente.

+0

il risultato sarà davvero essere user-faccia. –

+0

la query stessa è corretta così com'è. poiché è generato dinamicamente, non può essere ottimizzato manualmente, è diverso per ogni query di ricerca. –

6

Sono stato in grado di inserire un suggerimento Oracle aggiungendo un ProjectionList ai criteri.

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() rendimenti List<Object[]> in ordine di ProjectionList

5

ho un'altra soluzione generica, che dovrebbe funzionare per ogni query Criteri:
utilizzare un commento standard ed un Hibernate Interceptor cambiare lo SQL finale al database.
(L'ho usato con Hibernate 3.3, ma dovrebbe essere utilizzabile per ogni versione, la registrazione dell'Interceptor potrebbe essere diversa.)

Nell'interrogazione utilizzare il codice:

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

Scrivi un intercettore per passare al testo SQL (questo usa 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; 
} 

Sopra è per Oracle, ma dovrebbe essere facilmente regolabile per ogni DBMS.
Forse è possibile/dovrebbe creare una costante per l'indicatore di suggerimento "$ HINT $".
Anche la registrazione deve essere eseguita (in modo da poter vedere facilmente la chiamata corretta dell'intercettore), l'ho lasciata fuori per semplicità.

L'intercettore deve essere registrato. In primavera questo è fatto in applicationContext.xml:

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

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

Or (copia dai Hibernate 3.3 docs): intercettore

Una sessione con ambito viene specificato quando si apre una sessione utilizzando uno dei SessionFactory sovraccarico metodi .openSession() accettando un intercettore.

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

A intercettore SessionFactory con ambito è registrata con l'oggetto configurazione prima di costruire la SessionFactory. A meno che non venga aperta una sessione che specifica l'intercettore da utilizzare, l'intercettore fornito da verrà applicato a tutte le sessioni aperte da tale SessionFactory . Gli intercettori con scope SessionFactory devono essere protetti da thread . Assicurarsi di non archiviare stati specifici della sessione, dal momento che più sessioni useranno questo intercettatore potenzialmente simultaneamente da .

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

Problemi correlati