2013-05-21 13 views
7

Non penso che comprenderò mai completamente i join.Come faccio un join "deep" per il recupero in JPQL?

Ho una query in cui sto tentando di riempire avidamente i riferimenti su due livelli.

Cioè, il mio A ha un optional Collection di B s, e ciascuno ha B 0 o 1 C. La dimensione della collezione B è nota per essere piccola (10-20 in più). Mi piacerebbe precaricare questo grafico.

A 's B rapporto è contrassegnato come FetchType.LAZY ed è facoltativo. La relazione di B su C è anche facoltativa e FetchType.LAZY.

Speravo che potevo fare:

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 

Quando eseguo questo, vedo che A s collezione B è davvero inverosimile (vedo un LEFT JOIN in SQL riferimento alla tabella a cui B è mappato) .

Tuttavia, non vedo alcuna prova che la tabella di C venga recuperata.

Come posso precaricare tutti C s e tutti B s e tutti C s che sono "raggiungibili" da un determinato A? Non riesco a vedere alcun modo per farlo.

+1

Quali provider JPA si usa? Ci sono suggerimenti disponibili in EclipseLink esattamente per questo tipo di funzionalità. Controlla i suggerimenti di 'eclipselink.join-fetch' e' eclipselink.batch' ... – Cascader

+0

Grazie. Se uso 'eclipselink.join-fetch', sembra che sia permesso impostare solo un attributo. È corretto? Per esempio, se faccio saltare il mio capitale 'join-fetch' su' a.bs.c', allora se volessi unirmi a fetch-say-'a.bs.d' sarei sfortunato. Destra? –

+0

Oh, questo è interessante. La documentazione di EclipseLink (http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Query_Hints#Join_Fetch) non dice che sono possibili chiavi duplicate per la query ma forse lo sono? Vedi questo link interessante: https://github.com/mysema/querydsl/issues/348 –

risposta

5

Le specifiche APP non permette aliasing un'operazione di recupero unirsi, ma alcuni provider JPA fanno.

EclipseLink fa da 2.4. EclipseLink consente anche il recupero del join annidato utilizzando la notazione punto (ad esempio "JOIN FETCH a.bs.c") e supporta un suggerimento di query "eclipselink.join-fetch" che consente l'unione nidificata (è possibile specificare più hint con lo stesso nome suggerimento).

In generale è necessario prestare attenzione quando si utilizza un alias in un join di recupero, in quanto è possibile influire sui dati restituiti.

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

+0

Grazie, @James; per vari motivi dobbiamo mantenere il nostro standard JPQL, quindi sembra che il suggerimento di query (formato stringa) per me. In particolare l'uso di alcuni di questi risultati in un bug: https://bugs.eclipse.org/bugs/show_bug.cgi?id=408719 (riportato anche da qualcun altro nel 2010 qui: https://forums.oracle. com/forums/thread.jspa? threadID = 847970. Sono troppo cauto per il prefetching in ogni caso, quindi forse questo è stranamente il migliore. :-) –

+0

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

4

JPA non consente nidificato join recupera, e non consentono un alias su un join recuperare, quindi questo è probabilmente JPA provider specifico.

In EclipseLink, è possibile specificare un suggerimento per la query per eseguire recuperi nidificati di join .

Non è possibile renderlo ricorsivo in JPQL, tuttavia, è possibile utilizzare solo i migliori livelli . In EclipseLink è possibile utilizzare @JoinFetch o @BatchFetch su il mapping per rendere ricorsive le query.

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

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

3

sto usando Hibernate (e questo può essere specifico ad esso) e ho avuto successo con questo:

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 

(Si noti la b nella lista di selezione).

Questo era l'unico modo che ho trovato questo dovrebbe funzionare per me, notare che questo torna Object[] per me e poi filtrare nel codice in questo modo:

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