2012-06-14 16 views
14

ho hanno già implementato la paginazione utilizzando il seguente codice:Impaginazione con criteri di Hibernate e 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; 
     } 

Quando costruisco la query SQL manualmente e lo presenta alla banca dati, ottengo 8 risultati. Tuttavia, quando provo il codice sopra, prima di impostare il ResultTransformer su DISTINCT_ROOT_ENTITY, ottengo 8 risultati (senza distinti) e dopo averlo impostato ottengo 4 risultati. Ma dovrei ottenere 8 risultati (usando DISTINCT), perché quando costruisco l'SQL manualmente senza distinzione ottengo 11 risultati e quando uso DISTINCT ottengo correttamente, 8 distingue i risultati.

Cosa c'è che non va nel codice sopra?

risposta

23

Dopo tanto tempo alla ricerca di una soluzione per il mio problema sono riuscito a risolverlo. Il problema è che se si crea un criterio o una query che recupera in Molte associazioni utilizzando JOINS e si utilizza setMaxResults e si imposta ResultTransformer su DISTINCT_ROOT_ENTITY il risultato non sarà come previsto.

Come diceva JB Nizet, supponiamo di avere 4 entità A, ciascuna con 3 entità B, e supponiamo che la tua query recuperi tutte le entità A con i loro Bs.

In tal caso, la query SQL restituirà 12 righe. Se si utilizza setMaxResults (7), verrà recuperato (ad esempio) tre righe per A1 e relativi Bs, tre righe per A2 e relativi Bs e solo 1 riga per A3 e il suo primo B.

E poiché si ha utilizzato DISTINCT_ROOT_ENTITY, la query dei criteri restituirà solo tre entità: A1, A2 e A3 (che avrà un insieme incompleto di Bs).

Per risolvere questo problema, è necessario impostare la modalità FETCH per pressi di molte (di solito le collezioni) rapporti per selezionare o selezione secondaria, e si dispone fondamentalmente 2 modi per raggiungere questo:

Il primo modo è quello di utilizzare @FetchMode (FetchMode.SUBSELECT) annotazione sul tuo attributo e non mi piace questo approccio perché fa sì che ogni query utilizzi SUBSELECT FETCH per recuperare la raccolta. Ma funzionerà.

L'altro modo è impostare le modalità di recupero per le relazioni quando si crea la query. Preferisco in questo modo, perché posso personalizzare la query in base alle mie esigenze e non è necessario utilizzare SUBSELECTS per tutte le query. Così, ho fatto in questo modo:

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; 
    } 

Spero che sia di aiuto a chiunque altro. ; D

+8

Mi auguro seriamente che i nomi delle variabili NON siano in portoghese :) –

+0

Ancora non riesco a farlo funzionare .. Ho cercato di aggiungere FetchMode.SELECT a tutte le raccolte * ToMany ma ancora senza risultato .. – nKognito

+0

Grazie! non solo ha funzionato per me, ma ho cercato una soluzione da qualche giorno. Hai trovato la soluzione e la spiegazione! Mi è veramente piaciuto questo post !!! – Alexandros

13

Non sono sicuro di aver compreso correttamente la domanda, ma se la query recupera le entità con molte associazioni unite, l'impaginazione non funzionerà come previsto.

Infatti, supponiamo di avere 4 entità A, ciascuna con 3 entità B, e supponiamo che la tua query recuperi tutte le entità A con i loro Bs.

In tal caso, la query SQL restituirà 12 righe. Se si utilizza setMaxResults (7), verrà recuperato (ad esempio) tre righe per A1 e relativi Bs, tre righe per A2 e relativi Bs e solo 1 riga per A3 e il suo primo B.

E poiché si ha utilizzato DISTINCT_ROOT_ENTITY, la query dei criteri restituirà solo tre entità: A1, A2 e A3 (che avrà un insieme incompleto di Bs).

+1

Sì, ho capito, l'ho già risolto. Ant è esattamente come hai detto tu. Posterò la risposta – jguilhermemv

9

Questo è stato un problema per me, e ci è voluto del tempo per trovare una soluzione che funzioni per tutti gli scenari che ho.

Quello che vuoi per ogni pagina di impaginazione è di 2 cose, il conteggio totale di tutti i risultati e la tua singola pagina di risultati, ma per farlo devi fare 3 passi. 1) ottieni il conteggio totale, 2) ottieni gli ID univoci per la tua pagina e 3) ottieni i dati completi per gli ID trovati nel passaggio 2. E puoi fare tutto ciò con un singolo criterio:

1) ottenere conteggio totale, utilizzando gli ID distinti (uniqueField = il nome del tuo id nella classe entità)

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) ripristinare proiezione e impostare inizio e la durata (si desidera che i ids distinte)

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

3) resetta la proiezione e ottieni risultati distinti che corrispondono agli ID

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

Questo è fantastico! Una nota però: se si utilizza l'ordine da alcuni database, l'ordine per colonna deve essere parte del set di risultati per la seconda query. Vedere http://www.h2database.com/javadoc/org/h2/constant/ErrorCode.html#c90068 per un esempio. – jontejj

Problemi correlati