2009-10-21 18 views
5

La pregunta implementing-result-paging-in-hibernate-getting-total-number-of-rows desencadenar otra pregunta para mí, de cierta preocupación aplicación:Java codificación de las mejores prácticas para la reutilización de parte de una consulta para contar

Ahora se sabe que tiene que volver a utilizar parte de la consulta HQL hacer el conteo, ¿cómo reutilizar de manera eficiente?

Las diferencias entre las dos consultas HQL son:

  1. la selección es count(?), en lugar del POJO o propiedad (o lista de)
  2. las recuperaciones no debería ocurrir, por lo que algunas tablas no deben ser unido a
  3. la order by debe desaparecer

¿hay otras diferencias?

¿Tiene mejores prácticas de codificación para lograr esta reutilización de manera eficiente (preocupaciones: esfuerzo, claridad, rendimiento)?

Ejemplo para una simple consulta HQL:

select  a  from A a join fetch a.b b where a.id=66 order by a.name 
    select count(a.id) from A a     where a.id=66 

ACTUALIZADO

Recibí respuestas en:

  • usando Criterios (pero nosotros usamos HQL en su mayoría)
  • la manipulación de la cadena consulta (pero todo el mundo está de acuerdo en que parece complicado y no muy seguro)
  • envolver la consulta, basándose en la optimización de bases de datos (pero hay una sensación de que esto no es seguro)

Esperaba que alguien diera opciones a lo largo de otra ruta, más relacionada con la concatenación de cadenas.
¿Podríamos construir ambas consultas HQL usando las partes comunes?

Respuesta

1

Buena pregunta. Esto es lo que he hecho en el pasado (muchas cosas que usted ha mencionado ya):

  1. Compruebe si SELECCIONAR cláusula está presente.
    1. Si no es así, añadir select count(*)
    2. De lo contrario comprobar si tiene DISTINCT o agregados funciones en el mismo. Si está utilizando ANTLR para analizar su consulta, es posible solucionarlos, pero es bastante complicado. Probablemente es mejor que envuelva todo con select count(*) from().
  2. Retire fetch all properties
  3. Retire fetch de une analizar si está HQL como cadena. Si realmente está analizando la consulta con ANTLR puede eliminar left join por completo; es bastante complicado verificar todas las referencias posibles.
  4. Retire order by
  5. Dependiendo de lo que has hecho en 1.2 tendrá que quitar/ajustar group by/having.

Lo anterior se aplica a HQL, naturalmente. Para las consultas de Criteria, está bastante limitado con lo que puede hacer, ya que no se presta a la manipulación fácilmente. Si está utilizando una especie de capa de envoltorio sobre Criteria, terminará con un subconjunto (limitado) de resultados de análisis ANTLR y podría aplicar la mayoría de los anteriores en ese caso.

Dado que normalmente mantendría el desplazamiento de su página actual y el recuento total, generalmente ejecuto la consulta real con límite/desplazamiento determinado primero y solo ejecuto la consulta count(*) si el número de resultados es más o igual a el límite AND offset es cero (en todos los demás casos, ya ejecuté el count(*) o de todos modos obtuve todos los resultados). Este es un enfoque optimista con respecto a las modificaciones simultáneas, por supuesto.

actualización (en el montaje de la mano HQL)

me gusta especialmente ese enfoque. Cuando se asigna como consulta con nombre, HQL tiene la ventaja de la comprobación de errores en tiempo de compilación (bueno, en tiempo de ejecución técnicamente, porque SessionFactory debe construirse, aunque de todos modos se hace durante las pruebas de integración). Cuando se genera en tiempo de ejecución falla en tiempo de ejecución :-) No es exactamente fácil realizar optimizaciones de rendimiento.

El mismo razonamiento se aplica a Criteria, por supuesto, pero es un poco más difícil de arruinar debido a API bien definida en lugar de concatenación de cadenas. Construir dos consultas HQL en paralelo (paginada una y "cuenta global" una) también conduce a la duplicación de código (y potencialmente más errores) o le obliga a escribir algún tipo de capa de contenedor en la parte superior para hacerlo por usted. Ambas formas están lejos de ser ideal. Y si necesita hacer esto desde el código del cliente (como en la API), el problema empeora.

Tengo pondered quite a bit en este tema. La API de búsqueda de Hibernate-Generic-DAO parece un compromiso razonable; hay más detalles en mi respuesta a la pregunta vinculada anterior.

+0

+1 Gracias por estas precisiones sobre la manipulación de la consulta. Gracias también por la excelente precisión que ** una consulta de recuento debe ejecutarse solo después de una primera consulta **. – KLE

+0

He actualizado mi pregunta, ¿harías otra respuesta relacionada con la parte nueva? Me gustaron muchas de tus otras publicaciones, y eres un experto en Java :-) ... – KLE

+0

Gracias :-) He actualizado mi respuesta anterior. – ChssPly76

2

Bueno, no estoy seguro de que esta es una de las mejores prácticas, pero es mi-práctica :)

Si tengo como consulta algo como:

select A.f1,A.f2,A.f3 from A, B where A.f2=B.f2 order by A.f1, B.f3 

Y sólo quiero saber cuántos resultados obtendrán, ejecuto:

select count(*) from (select A.f1, ... order by A.f1, B.f3) 

y luego obtener el resultado como un entero, y sin resultados de los mapas en un POJO.

Analice su consulta para eliminar algunas partes, como 'ordenar por' es muy complicado. Un buen RDBMS optimizará su consulta para usted.

Buena pregunta.

+0

Gracias por su apoyo :-) Mi consulta típica es HQL, ya sea devolviendo pojos o una lista de propiedades. – KLE

+0

Al igual que usted, no tengo ganas de analizar la consulta para eliminar algunas partes; pero realmente no confío en RDBMS para optimizar la consulta en todos los casos. Creo que algunos casos se estropean y es difícil predecir cuál. ** ¿Hay alguna lista de hechos sobre estas optimizaciones? ** – KLE

+0

No sé dónde encontrar estas listas de hechos o si esta información es pública. No puedo estar completamente seguro de que un RDBMS se optimice de esta manera. Pero como estudiante, vi algunas optimizaciones "antiguas y básicas" que parecen más difíciles que esta. En este caso, p. RDBMS pensaría: "Me están interrogando por varias filas, por lo que el orden es evitable". – sinuhepop

2

¿Ha intentado aclarar sus intenciones a Hibernate estableciendo una proyección en sus criterios (SQL?)? sobre todo He estado usando criterios, así que no estoy seguro de cómo aplica esto es su caso, pero he estado usando

getSession().createCriteria(persistentClass). 
setProjection(Projections.rowCount()).uniqueResult() 

y dejar que Hibernate averiguar las cosas de almacenamiento en caché/reutilización/inteligente por sí mismo .. No estoy seguro de cuantas cosas inteligentes hace en realidad ... ¿Alguien quiere comentar sobre esto?

+0

Hibernate no almacena en caché las consultas por sí mismo; tienes que hacerlo explícitamente El problema con el enfoque anterior (y el uso de Criteria en general) es que la capa que ensambla los criterios tiene que crear otra copia para el conteo. En otras palabras, no puedo simplemente crear un Criterio en la capa empresarial, pasarlo al servicio (o DAO) y recuperar 1 página de resultados + recuento total. No es un gran negocio para aplicaciones pequeñas, pero conduce a MUCHO código innecesario en las más grandes. – ChssPly76

0

En una situación a mano alzada HQL me gustaría utilizar algo como esto, pero esto no es reutilizable ya que es bastante específico para las entidades dadas

Integer count = (Integer) session.createQuery("select count(*) from ....").uniqueResult(); 

Haga esto una vez y ajustar en consecuencia el número inicial hasta que la página a través.

Para los criterios pesar de que utilizan una muestra como esta

final Criteria criteria = session.createCriteria(clazz); 
      List<Criterion> restrictions = factory.assemble(command.getFilter()); 
      for (Criterion restriction : restrictions) 
       criteria.add(restriction); 
      criteria.add(Restrictions.conjunction()); 
      if(this.projections != null) 
       criteria.setProjection(factory.loadProjections(this.projections)); 
      criteria.addOrder(command.getDir().equals("ASC")?Order.asc(command.getSort()):Order.desc(command.getSort())); 
      ScrollableResults scrollable = criteria.scroll(ScrollMode.SCROLL_INSENSITIVE); 
      if(scrollable.last()){//returns true if there is a resultset 
       genericDTO.setTotalCount(scrollable.getRowNumber() + 1); 
       criteria.setFirstResult(command.getStart()) 
         .setMaxResults(command.getLimit()); 
       genericDTO.setLineItems(Collections.unmodifiableList(criteria.list())); 
      } 
      scrollable.close(); 
      return genericDTO; 

Pero esto hace el recuento de cada momento llamando ScrollableResults:last().

Cuestiones relacionadas