2012-06-14 18 views
14

he ya han implementado la paginación usando el siguiente código:paginación con los Criterios de Hibernate y DISTINCT_ROOT_ENTITY

public Paginacao<Anuncio> consultarPaginado(int pagina, Integer cidadeId) { 

      Criteria criteria = this.sessionFactory.getCurrentSession().createCriteria(Anuncio.class);  
      criteria.add(Restrictions.eq("ativo", true)); 
      criteria.add(Restrictions.eq("statusLiberacao", AnunciosUtil.STATUS_ANUNCIO_LIBERADO)); 
      criteria.add(Restrictions.eq("statusVendaAnuncio", AnunciosUtil.STATUS_VENDA_ANUNCIO_DISPONIVEL)); 

      if (cidadeId != null) { 
       criteria.add(Restrictions.eq("cidade.id", cidadeId)); 
      } 

      criteria.addOrder(Order.desc("dataPostagem")); 
      criteria.setProjection(Projections.rowCount()); 

      Long count = (Long) criteria.uniqueResult(); 

      Paginacao<Anuncio> paginacao = new Paginacao<Anuncio>(); 
      int qtdPaginas = (count.intValue()/7) + 1; 

      paginacao.setQtdPaginas(qtdPaginas); 

      criteria.setProjection(null);// reseta a criteria sem a projeção 
      criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY); 

      if (pagina > qtdPaginas) { 
       pagina = qtdPaginas; 
      } 
      pagina = pagina - 1; 
      criteria.setFirstResult(pagina * ConstantesGenericas.MAXIMO_OBJETOS_RETORNADOS); 
      criteria.setMaxResults(ConstantesGenericas.MAXIMO_OBJETOS_RETORNADOS); 

      paginacao.setRegistros(criteria.list()); 

      return paginacao; 
     } 

Cuando construyo la consulta SQL de forma manual y lo presentará a la base de datos, me sale 8 resultados. Sin embargo, cuando pruebo el código anterior, antes de configurar ResultTransformer en DISTINCT_ROOT_ENTITY, obtengo 8 resultados (sin distinción) y después de configurarlo obtengo 4 resultados. Pero debería obtener 8 resultados (usando DISTINCT), porque cuando construyo el SQL manualmente sin distintivo obtengo 11 resultados y cuando uso DISTINCT obtengo correctamente, 8 resultados diferentes.

¿Qué pasa con el código anterior?

Respuesta

23

Después de un largo tiempo en busca de una solución para mi problema que lograron resolverlo. El problema de que si se crea un criterio o consulta que recupera asociaciones toMany usando combinaciones y entonces utilizar setMaxResults y establecer el ResultTransformer a DISTINCT_ROOT_ENTITY el resultado no será como se esperaba.

Como dijo JB Nizet, suponga que tiene 4 entidades A, cada uno con 3 entidades B, y supongamos que la consulta recupera todas las entidades A con su B.

En ese caso, la consulta SQL devolverá 12 filas. Si usa setMaxResults (7), recuperará (por ejemplo) tres filas para A1 y su Bs, tres filas para A2 y su Bs, y solo 1 fila para A3 y su primer B.

Y dado que tiene utilizado DISTINCT_ROOT_ENTITY, la consulta de criterios devolverá solo tres entidades: A1, A2 y A3 (que tendrá un conjunto incompleto de Bs).

Para solucionar esto, hay que establecer el MODO FETCH para toMany (generalmente colecciones) relaciones para seleccionar o Subseleccionar, y usted tiene básicamente 2 maneras de lograr esto:

La primera forma es utilizar @FetchMode (FetchMode.SUBSELECT) anotación en su atributo, y no me gusta este enfoque porque hace que cada consulta use SUBSELECT FETCH para recuperar la colección. Pero funcionará.

La otra forma es establecer modos de búsqueda para las relaciones cuando crea su consulta. Prefiero esta manera, porque puedo personalizar la consulta a mis necesidades y no tengo que usar SUBSELECTS para todas las consultas. Por lo tanto, he hecho esto:

public Paginacao<Anuncio> consultarPaginado(int pagina, Integer cidadeId) { 

     Criteria criteria = this.sessionFactory.getCurrentSession().createCriteria(Anuncio.class);  
     criteria.add(Restrictions.eq("ativo", true)); 
     criteria.add(Restrictions.eq("statusLiberacao", AnunciosUtil.STATUS_ANUNCIO_LIBERADO)); 
     criteria.add(Restrictions.eq("statusVendaAnuncio", AnunciosUtil.STATUS_VENDA_ANUNCIO_DISPONIVEL)); 
     criteria.setFetchMode("imagens", FetchMode.SELECT); 
     criteria.setFetchMode("pagamentos", FetchMode.SELECT);  

     if (cidadeId != null) { 
      criteria.add(Restrictions.eq("cidade.id", cidadeId)); 
     } 

     criteria.addOrder(Order.desc("dataPostagem")); 
     criteria.setProjection(Projections.rowCount()); 

     Long count = (Long) criteria.uniqueResult(); 

     Paginacao<Anuncio> paginacao = new Paginacao<Anuncio>(); 
     int qtdPaginas = (count.intValue()/7) + 1; 

     paginacao.setQtdPaginas(qtdPaginas); 

     criteria.setProjection(null);// reseta a criteria sem a projeção 
     criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY); 

     if (pagina > qtdPaginas) { 
      pagina = qtdPaginas; 
     } 
     pagina = pagina - 1; 
     criteria.setFirstResult(pagina * ConstantesGenericas.MAXIMO_OBJETOS_RETORNADOS); 
     criteria.setMaxResults(ConstantesGenericas.MAXIMO_OBJETOS_RETORNADOS); 

     paginacao.setRegistros(criteria.list()); 

     return paginacao; 
    } 

Espero que ayude a los demás. ; D

+8

Realmente deseo que sus nombres de variables NO estuvieran en portugués :) –

+0

Todavía no puedo hacer que funcione .. Intenté agregar FetchMode.SELECT a todas las colecciones * ToMany pero aún sin resultado ... – nKognito

+0

¡Gracias! no solo funcionó para mí, sino que he estado buscando una solución por algunos días. ¡Encontraste la solución y la explicación! ¡Realmente me gustó esta publicación! – Alexandros

13

No estoy seguro si entiendo su pregunta correctamente, pero si su consulta recupera entidades con muchas asociaciones join-fetched, la paginación no funcionará como se esperaba.

En efecto, supongamos que tiene 4 entidades A, cada uno con 3 entidades B, y supongamos que la consulta recupera todas las entidades A con su B.

En ese caso, la consulta SQL devolverá 12 filas. Si usa setMaxResults (7), recuperará (por ejemplo) tres filas para A1 y su Bs, tres filas para A2 y su Bs, y solo 1 fila para A3 y su primer B.

Y dado que tiene utilizado DISTINCT_ROOT_ENTITY, la consulta de criterios devolverá solo tres entidades: A1, A2 y A3 (que tendrá un conjunto incompleto de Bs).

+1

Sí, lo tengo, ya lo he resuelto. Ant es exactamente como dijiste. Voy a publicar la respuesta. – jguilhermemv

9

Esto fue un problema para mí, y me llevó un tiempo encontrar una solución que funcionara para todos los escenarios que tengo.

Lo que desea para cada página de paginación es 2 cosas, el recuento total de todos los resultados y su única página de resultados, pero para hacer eso debe realizar 3 pasos. 1) obtener el recuento total, 2) obtener los identificadores únicos para su página, y 3) obtener la información completa para los identificadores encontrados en el paso 2. Y puede hacer todo eso con un único objeto de criterio:

1) obtener el recuento total, el uso de identificadores distintos (uniqueField = el nombre de su id en la clase de entidad)

Criteria criteria = session.createCriteria(YourEntity.class); 
    Projection idCountProjection = Projections.countDistinct(uniqueField); 
    criteria.setProjection(idCountProjection); 
    //setup criteria, joins etc here 
    int totalResultCount = ((Long)criteria.uniqueResult()).intValue(); 

2) restablecer la proyección y ajuste de inicio y longitud (desea que los identificadores de distintos)

criteria.setProjection(Projections.distinct(Projections.property(uniqueField))); 
    criteria.setFirstResult(start); 
    criteria.setMaxResults(length); 
    List uniqueSubList = criteria.list(); 

3) restablecer la proyección y obtener resultados distintos que coincidan con los identificadores

criteria.setProjection(null); 
    criteria.setFirstResult(0); criteria.setMaxResults(Integer.MAX_VALUE); 
    criteria.add(Restrictions.in(uniqueField, uniqueSubList)); 
    criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY); 
    List searchResults = criteria.list(); 
    //and now, however you want to return your results 
    Map<String, Object> searchResultsMap = new HashMap<String, Object>(); 
    searchResultsMap.put("searchResults", searchResults); 
    searchResultsMap.put("totalResultCount", totalResultCount); 
+0

¡Esto es genial! Sin embargo, una nota: si utiliza el orden en algunas bases de datos, es necesario que el orden por columna sea parte del conjunto de resultados para la segunda consulta. Consulte http://www.h2database.com/javadoc/org/h2/constant/ErrorCode.html#c90068 para ver un ejemplo. – jontejj

Cuestiones relacionadas