2013-05-21 9 views
7

Ich glaube nicht, dass ich Fetch Joins jemals vollständig verstehen werde.Wie mache ich einen "tiefen" Abruf Join in JPQL?

Ich habe eine Abfrage, wo ich eifrig versuche, Referenzen auf zwei Ebenen "aufzublasen".

Das heißt, mein A verfügt über eine optionale Collection von B s, und jeder B hat entweder 0 oder 1 C. Die Größe der B Kollektion ist bekanntlich klein (10-20 Tops). Ich möchte dieses Diagramm vorab abrufen.

A 's B Beziehung ist als FetchType.LAZY markiert und ist optional. B 's Beziehung zu C ist auch optional und FetchType.LAZY.

Ich hatte gehofft, was ich tun konnte:

SELECT a 
    FROM A a 
    LEFT JOIN FETCH a.bs // look, no alias; JPQL forbids it 
    LEFT JOIN a.bs b // "repeated" join necessary since you can't alias fetch joins 
    LEFT JOIN FETCH b.c // this doesn't seem to do anything 
WHERE a.id = :id 

Als ich das laufen, ich sehe, dass A s B Sammlung tatsächlich abgerufen wird (Ich sehe einen LEFT JOIN in der SQL-Verweisen auf die Tabelle, auf die B abgebildet) .

Allerdings sehe ich keinen solchen Beweis, dass die Tabelle C abgerufen wird.

Wie kann ich Prefetch alle C s und alle B s und alle C s, die „erreichbar“ von einem gegebenen A sind? Ich sehe keinen Weg, dies zu tun.

+1

Welche JPA-Provider verwenden Sie? Es gibt Hinweise in EclipseLink genau für diese Art von Funktionalität. Check out 'eclipselink.join-fetch' und' eclipselink.batch' Hinweise ... – Cascader

+0

Vielen Dank. Wenn ich 'eclipselink.join-fetch' verwende, scheint es mir erlaubt zu sein, nur ein Attribut zu setzen. Ist das korrekt? Zum Beispiel, wenn ich mein 'join-fetch'-Kapital auf' a.bs.c' blase, dann wäre ich kein Glück, wenn ich auch fetch-say-'a.bs.d' beitreten wollte. Recht? –

+0

Oh, das ist interessant. Die EclipseLink-Dokumentation (http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Query_Hints#Join_Fetch) sagt nicht, dass doppelte Abfragehinweisschlüssel möglich sind, aber vielleicht sind sie? Sehen Sie diesen interessanten Link: https://github.com/mysema/querydsl/issues/348 –

Antwort

5

Die PPV-Spezifikation erlaubt es nicht, eine Join-Aliasing holen, aber einige PPV-Anbieter tun.

EclipseLink funktioniert ab 2.4. EclipseLink ermöglicht auch den Abruf von verschachtelten Joins mithilfe der Punktnotation (zB "JOIN FETCH a.bs.c") und unterstützt einen Abfragehinweis "eclipselink.join-fetch", der verschachtelte Joins erlaubt (Sie können mehrere Hinweise desselben Hint-Namens angeben)).

Im Allgemeinen müssen Sie vorsichtig sein, wenn Sie einen Alias ​​für einen Fetch-Join verwenden, da Sie die zurückgegebenen Daten beeinflussen können.

See, http://java-persistence-performance.blogspot.com/2012/04/objects-vs-data-and-filtering-join.html

+0

Danke, @ James; Aus verschiedenen Gründen müssen wir unseren JPQL-Standard beibehalten, daher sieht es für mich wie das Abfrageformat (Zeichenfolgenformat) aus. Insbesondere die Verwendung einiger dieser Ergebnisse führt zu einem Fehler: https://bugs.eclipse.org/bugs/show_bug.cgi?id=408719 (auch von jemand anderem im Jahr 2010 berichtet hier: https://forums.oracle. com/forums/thread.jspa? threadID = 847970. Ich bin misstrauisch, sowieso zu tief vorzuladen, also vielleicht ist das seltsamerweise das Beste. :-) –

+0

eclipselink.join-fetch funktioniert auch in eclipseLink 2.3: .setHint ("eclipselink.join-fetch", "a.bs.c") – tak3shi

4

JPA nicht verschachtelte erlaubt beitreten Abrufs, noch erlauben ein Alias ​​auf einen Join holen, so ist dies wahrscheinlich JPA-Provider spezifisch.

In EclipseLink können Sie einen Abfragehinweis angeben, um verschachtelte Joins Fetches durchzuführen.

Sie können es in JPQL nicht rekursiv machen, aber Sie könnten nur bestenfalls n Ebenen gehen. In EclipseLink können Sie @JoinFetch oder @BatchFetch unter das Mapping verwenden, um die Abfrage rekursiv zu machen.

See, http://java-persistence-performance.blogspot.com/2010/08/batch-fetching-optimizing-object-graph.html

Quelle: http://www.coderanch.com/t/570828/ORM/databases/Recursive-fetch-join-recursively-fetching

3

Ich Hibernate (und dies kann es sein), und ich habe Erfolg mit diesem hatte:

SELECT DISTINCT a, b 
FROM A a 
LEFT JOIN a.bs b 
LEFT JOIN FETCH a.bs 
LEFT JOIN FETCH b.c 
WHERE a.id = :id 

(Beachten Sie die b in der Auswahlliste).

Dies ist der einzige Weg gefunden, ich war das für mich funktionieren würde, beachten Sie, dass diese Object[] für mich gibt und ich es dann wie so in Code filtern:

(List<A>) q.getResultList().stream().map(pair -> (A) (((Object[])pair)[0])).distinct().collect(Collectors.toList());