2009-08-25 7 views
7

tengo una consulta de hibernación que se arma dinámicamente usando los criterios api. genera consultas que son insoportablemente lentas, si se ejecutan tal cual.Cómo insertar una "sugerencia del optimizador" para Hibernar consulta de api de criterios

pero he notado que son aproximadamente 1000% más rápidos si anexo/* + FIRST_ROWS (10) */a la consulta. ¿Cómo puedo hacer esto con los criterios api?

intenté criteria.setComment (..), pero esto parece ser ignorado.

en los documentos hibernate, 3.4.1.7. Se mencionan sugerencias de consulta, pero indica claramente: "Tenga en cuenta que estas no son sugerencias de consulta SQL"

el resultado de la consulta se paginará, por lo que en el 99% de los casos mostraré los resultados 1-10.

Respuesta

5

Se puede modificar el modo de optimización a nivel de sesión:

ALTER SESSION SET optimizer_mode = FIRST_ROWS; 

O justo antes de la consulta y luego ponerlo de nuevo a su valor por defecto (ALL_ROWS) o en su caso, ya que el 99% de las consultas haría se beneficiará de ello podría modificarlo en el nivel de esquema (con un desencadenante ON LOGON para ejemplo) o incluso en el nivel de instancia (modifique el parámetro init).

1

El problema es que la sintaxis de la sugerencia no es un comentario, simplemente se parece a uno. Realmente tiene que ir entre SELECT y las columnas seleccionadas, mientras que setComment() antepone el comentario antes del SELECT.

Más allá de eso, no hay balas de plata. FIRST_ROWS no es una herramienta para mejorar el rendimiento. Puede llevar más tiempo obtener todas las filas. Por supuesto, en un programa orientado al usuario, recuperar las primeras diez filas puede ser todo lo que tenemos que hacer.

Pero, en cualquier forma en que rebote, si desea usar la sintaxis de sugerencia de Oracle, tendrá que ir por la ruta de SQL nativo.

¿Qué más puedes hacer? Todavía no tengo mucha experiencia ajustando Hibernate. La única vez que tuve esta tarea, la consulta fue tomar las filas de un montón de tablas para crear una instancia de un objeto con muchos subtipos. Cada subtipo era una tabla separada. La consulta generada por Hibernate tuvo muchas OUTER JOINs, lo que confundió al optimizador. Romper ese monstruo en varias consultas enfocadas (una por subtipo) que usaba ÚNICAMENTE UNIONES INNER producía una reducción de dos centavos en el tiempo de recuperación.

Esto puede no ser de utilidad inmediata para usted. Pero el principio es, mire la consulta de Hibernate y vea si se puede implementar de una manera diferente y más eficiente.

+0

el resultado será de hecho con la cara del usuario. –

+0

la consulta en sí es correcta tal como es. dado que se genera dinámicamente, no se puede optimizar manualmente, es diferente para cada consulta de búsqueda. –

6

Pude introducir una pista de Oracle agregando una Lista de proyección a los criterios.

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() vuelve List<Object[]> con el fin de ProjectionList

5

tengo otra solución genérica, que debería funcionar para cada consulta Criterios:
utilizar un comentario estándar y un interceptor de Hibernate cambiar el SQL definitiva a la base de datos.
(Lo usé con Hibernate 3.3, pero debería ser utilizable para cada versión, el registro del Interceptor puede ser diferente.)

En su código de consulta utilización:

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

Escribir un interceptor para cambiar a texto SQL (éste utiliza 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; 
} 

Por encima es para Oracle, pero debe ser fácilmente ajustable para cada DBMS.
Tal vez puedas/deberías crear una constante para el marcador "$ HINT $".
También se debe realizar el registro (para que pueda ver fácilmente la llamada correcta del Interceptor), lo dejé arriba para simplificar.

El interceptor debe estar registrado. En la primavera de esto se hace en applicationContext.xml:

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

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

O (copia de los Hibernate 3.3 docs): interceptor

Un ámbito de sesión se especifica cuando se abre una sesión usando uno de los SessionFactory sobrecargada Métodos .openSession() aceptando un Interceptor.

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

Un interceptor SessionFactory de ámbito se ha registrado en el objeto de configuración antes de la construcción de la SessionFactory. A menos que se abra una sesión especificando explícitamente el interceptor que se utilizará, el interceptor suministrado se aplicará a todas las sesiones abiertas desde esa SessionFactory. Los interceptores con alcance SessionFactory deben ser seguros . Asegúrese de no almacenar estados específicos de la sesión, ya que sesiones múltiples usarán este interceptor de manera concurrente.

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

Cuestiones relacionadas