2012-06-14 10 views
14

Ich habe habe bereits Paginierung mit dem folgenden Code implementiert:Paginierung mit Hibernate Kriterien und 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; 
     } 

Wenn ich die SQL-Abfrage manuell und legt ihn in die Datenbank aufzubauen, erhalte ich 8 Ergebnisse. Allerdings, wenn ich den obigen Code versuche, bevor ich den ResultTransformer auf DISTINCT_ROOT_ENTITY setze, bekomme ich 8 Ergebnisse (ohne distinct) und nach der Einstellung bekomme ich 4 Ergebnisse. Aber ich sollte 8 Ergebnisse erhalten (mit DISTINCT), denn wenn ich das SQL manuell ohne distinct erstelle, bekomme ich 11 Ergebnisse und wenn ich DISTINCT richtig benutze, unterscheidet 8 Ergebnisse.

Was ist falsch mit dem obigen Code?

Antwort

23

Nachdem ich lange nach einer Lösung für mein Problem gesucht habe, habe ich es gelöst. Das Problem, dass wenn Sie ein Kriterium oder eine Abfrage erstellen, die mit JOINS toMany-Zuordnungen abgerufen wird, und dann setMaxResults verwenden und den ResultTransformer auf DISTINCT_ROOT_ENTITY setzen, wird das Ergebnis nicht Ihren Erwartungen entsprechen.

Wie JB Nizet sagte, nehmen wir an, Sie haben 4 A-Entitäten mit jeweils 3 B-Entitäten und nehmen an, dass Ihre Abfrage alle A-Entitäten mit ihren Bs abruft.

In diesem Fall gibt die SQL-Abfrage 12 Zeilen zurück. Wenn Sie setMaxResults (7) verwenden, ruft es (zum Beispiel) drei Zeilen für A1 und seine Bs, drei Zeilen für A2 und seine Bs und nur eine Zeile für A3 und seine erste B auf.

Und seit Sie haben Wenn Sie DISTINCT_ROOT_ENTITY verwenden, gibt die Kriterienabfrage nur drei Entitäten zurück: A1, A2 und A3 (die einen unvollständigen Satz von Bs enthalten).

Um dies zu lösen, haben Sie den Fetch-Modus für Tomany (in der Regel Sammlungen) Beziehungen auf SELECT oder subselect, und Sie haben grundsätzlich zwei Möglichkeiten, dies zu erreichen:

Der erste Weg ist @FetchMode zu verwenden (FetchMode.SUBSELECT) Annotation für Ihr Attribut, und ich mag diesen Ansatz nicht, da jede Abfrage SUBSELECT FETCH verwendet, um die Auflistung abzurufen. Aber es wird funktionieren.

Die andere Möglichkeit besteht darin, Abrufmodi für Beziehungen beim Erstellen der Abfrage festzulegen. Ich bevorzuge diesen Weg, weil ich die Abfrage an meine Bedürfnisse anpassen kann und ich SUBSELECTS nicht für alle Abfragen verwenden muss. Also, ich habe es so gemacht:

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

Ich hoffe, dass es jemand anderen hilft. ; D

+8

Ich wünsche ernsthaft, dass Ihre Variablen Namen NICHT in Portugiesisch waren :) –

+0

Immer noch kann es nicht funktionieren .. Versucht FetchMode.SELECT zu allen * ToMany Sammlungen hinzuzufügen, aber immer noch ohne Ergebnis .. – nKognito

+0

Vielen Dank! Es hat nicht nur für mich funktioniert, sondern ich suche seit einigen Tagen nach einer Lösung. Du hast die Lösung und die Erklärung gefunden! Ich habe diesen Beitrag wirklich sehr gemocht !!! – Alexandros

13

Ich bin mir nicht sicher, ob ich Ihre Frage richtig verstehe, aber wenn Ihre Abfrage Entitäten mit Join-Joined toMany-Zuordnungen abruft, funktioniert die Paginierung nicht wie erwartet.

Angenommen, Sie haben 4 A-Entitäten mit jeweils 3 B-Entitäten und nehmen an, dass Ihre Abfrage alle A-Entitäten mit ihren Bs abruft.

In diesem Fall gibt die SQL-Abfrage 12 Zeilen zurück. Wenn Sie setMaxResults (7) verwenden, ruft es (zum Beispiel) drei Zeilen für A1 und seine Bs, drei Zeilen für A2 und seine Bs und nur eine Zeile für A3 und seine erste B auf.

Und seit Sie haben Wenn Sie DISTINCT_ROOT_ENTITY verwenden, gibt die Kriterienabfrage nur drei Entitäten zurück: A1, A2 und A3 (die einen unvollständigen Satz von Bs enthalten).

+1

Ja, ich habe es habe ich schon gelöst. Ant es ist genau wie du gesagt hast. Ich werde die Antwort posten. – jguilhermemv

9

Das war ein Problem für mich, und es dauerte eine Weile, bis ich eine Lösung gefunden hatte, die für alle Szenarien, die ich habe, funktioniert.

Was Sie für jede Seitenumbruch Seite wollen, ist 2 Dinge, die Gesamtzahl aller Ergebnisse und Ihre einzige Seite der Ergebnisse, aber dazu müssen Sie 3 Schritte machen. 1) Holen Sie sich die Gesamtzahl, 2) Holen Sie sich die eindeutigen IDs für Ihre Seite, und 3) erhalten Sie die vollständigen Daten für die IDs in Schritt 2 und Sie können alles mit einem einzigen Kriterium Objekt tun:

1) Gesamtzahl, indem verschiedene IDs (uniqueField den Namen Ihres ID in der Entity-Klasse =)

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) zurückgesetzt Vorsprung und setzen Start und Länge (Sie die eindeutigen IDs wollen)

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

3) Setze die Projektion zurück und erhalte eindeutige Ergebnisse, die mit den IDs

übereinstimmen
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

Das ist großartig! Eine Anmerkung: Wenn Sie die Reihenfolge einiger Datenbanken verwenden, müssen Sie die Reihenfolge nach Spalte als Teil der Ergebnismenge für die zweite Abfrage angeben. Ein Beispiel finden Sie unter http://www.h2database.com/javadoc/org/h2/constant/ErrorCode.html#c90068. – jontejj