Kriterien-API: Abruf einer Liste gibt wiederholte Haupteinheit zurück

8

Ich habe die folgenden Entitäten; Ticket enthält einen Satz von 0, N WorkOrder:

%Vor%

Ich lade Tickets und hole die Attribute ab. Alle 0,1 Attribute sind kein Problem. Für WorkOrders habe ich diese Antwort verwendet, um das zu erhalten folgenden Code.

%Vor%

Das Ergebnis ist, dass ich in einer Abfrage, die mir 1 Ticket mit 5 workOrders zurückgeben soll, dasselbe Ticket 5 mal abrufen werde.

Wenn ich nur die WorkOrders zu einem Eager Fetch mache und den Fetch-Code lösche, funktioniert es wie es soll.

Kann mir jemand helfen? Vielen Dank im Voraus.

UPDATE:

Eine Erklärung, warum ich nicht nur mit der Antwort von JB Nizet zufrieden bin (auch wenn es am Ende funktioniert).

Wenn ich gerade die Beziehung begierig mache, prüft JPA genau die gleichen Daten, wenn ich es faul mache und die Fetch-Klausel zu Criteria / JPQL hinzufüge. Die Beziehungen zwischen den verschiedenen Elementen sind ebenfalls klar, da ich die ListAttribute für die Kriterienabfrage definiere.

Es gibt eine vernünftige Erklärung dafür, dass JPA in beiden Fällen nicht die gleichen Daten zurückgibt?

UPDATE FOR BOUNTY: Während JB Nizets Antwort das Problem gelöst hat, finde ich es immer noch bedeutungslos, dass zwei Operationen mit derselben Bedeutung ("Get Ticket und falle alle WorkOrder in ticket.workOrders ") ausgeführt werden sie durch ein eifriges Laden benötigt keine weiteren Änderungen, während das Angeben eines Abrufs einen DISTINCT -Befehl erfordert

    
SJuan76 14.06.2012, 11:01
quelle

2 Antworten

20
  1. Es gibt einen Unterschied zwischen dem Eager-Laden und dem Fetch-Join. Eager Loading bedeutet nicht, dass die Daten in derselben Abfrage geladen werden. Es bedeutet nur, dass es sofort geladen wird, obwohl durch zusätzliche Abfragen.

  2. Die Kriterien werden immer in eine SQL-Abfrage übersetzt. Wenn Sie Joins angeben, wird es in SQL verknüpft. Durch die Art von SQL multipliziert dies auch die Daten der Root-Entität, was zu dem Effekt führt, den Sie erhalten haben. (Beachten Sie, dass Sie dieselbe Instanz mehrmals erhalten, so dass die Root-Entität nicht im Speicher multipliziert wird.)

Dafür gibt es mehrere Lösungen:

  • benutze distinct(true)
  • Verwenden Sie die eindeutige root-Entity-Umsetzung ( .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) ).
  • Wenn Sie nicht nach untergeordneten Eigenschaften filtern müssen, vermeiden Sie den Join
  • Wenn Sie nach untergeordneten Eigenschaften filtern müssen, filtern Sie nach einer Unterabfrage ( DetachedCriteria ).
  • Optimieren Sie das N + 1-Problem, indem Sie Batch-Size
Stefan Steinegger 29.06.2012, 06:38
quelle
3

Haben Sie versucht, distinct(true) für die CriteriaQuery aufzurufen?

Die JPA 2-Spezifikation, Seite 161, sagt:

  

Das Schlüsselwort DISTINCT wird verwendet, um anzugeben, dass doppelte Werte erforderlich sind   aus dem Abfrageergebnis eliminiert.

     

Wenn DISTINCT nicht angegeben ist, werden doppelte Werte nicht eliminiert.

Der Javadoc sagt auch:

  

Geben Sie an, ob doppelte Abfrageergebnisse eliminiert werden sollen. A true   Wert bewirkt, dass Duplikate eliminiert werden. Ein falscher Wert verursacht   Duplikate, die beibehalten werden sollen. Wenn distinct nicht angegeben wurde,   doppelte Ergebnisse müssen beibehalten werden.

Der Grund, warum Sie distinct nicht benötigen, wenn die Assoziation eifrig geladen wird, ist wahrscheinlich nur, dass die Assoziation nicht mit einem Fetch-Join geladen wird, sondern mit einer zusätzlichen Abfrage.

    
JB Nizet 14.06.2012 11:10
quelle

Tags und Links