Qual è la differenza tra JOIN e JOIN FETCH quando si utilizza JPA e Hibernate


183

Aiutami a capire dove utilizzare un JOIN regolare e dove un JOIN FETCH.

Ad esempio, se abbiamo queste due query

FROM Employee emp
JOIN emp.department dep

e

FROM Employee emp
JOIN FETCH emp.department dep

C'è qualche differenza tra loro? Se sì, quale usare quando?


2
puoi trovarlo qui link leggi 14.3. Associazioni e adesioni
Angga,

4
Ho passato quella documentazione ma ancora non so dove dovrei usare un JOIN e dove un JOIN FETCH.
abbas,

2
Se il mapping di @oneToOne è impostato su FetchType.LAZY e si utilizza la seconda query (poiché è necessario caricare oggetti Department come parte degli oggetti Employee), ciò che Hibernate farà, genererà query per recuperare gli oggetti Department per ogni singolo oggetto Employee recupera dal DB. Più avanti nel codice è possibile accedere agli oggetti Dipartimento tramite l'associazione a valore singolo da Dipendente a Dipartimento e Hibernate non invierà alcuna query per recuperare l'oggetto Dipartimento per il Dipendente specificato. Ricorda che Hibernate emette ancora query pari al numero di dipendenti che ha recuperato.
Bunti,

Per aiutare nella caccia al doc ~ Strategie di recupero
Eddie B,

1
@ShameeraAnuranga Penso che in quel caso avrai bisogno di un ENTRA ESTERNO SINISTRA.
abbas,

Risposte:


181

In queste due query, si utilizza JOIN per interrogare tutti i dipendenti a cui è associato almeno un reparto.

Ma la differenza è: nella prima query stai restituendo solo i Dipendenti per l'ibernazione. Nella seconda query, si restituiscono i dipendenti e tutti i dipartimenti associati.

Pertanto, se si utilizza la seconda query, non sarà necessario eseguire una nuova query per accedere nuovamente al database per visualizzare i reparti di ciascun dipendente.

È possibile utilizzare la seconda query quando si è certi di aver bisogno del dipartimento di ciascun dipendente. Se non è necessario il dipartimento, utilizzare la prima query.

Consiglio di leggere questo link se è necessario applicare alcune condizioni WHERE (ciò di cui probabilmente avrete bisogno): Come esprimere correttamente JPQL "join fetch" con la clausola "where" come CriteriaQuery JPA 2?

Aggiornare

Se non si utilizza fetche i Reparti continuano a essere restituiti, è perché la mappatura tra Dipendente e Dipartimento (a @OneToMany) è regolata FetchType.EAGER. In questo caso, qualsiasi fetchquery HQL (con o meno) con FROM Employeeporterà tutti i Dipartimenti. Ricorda che tutti i mapping * ToOne ( @ManyToOnee @OneToOne) sono EAGER per impostazione predefinita.


1
Quale comportamento sarà se eseguiamo l'istruzione senza recuperare e ottenere il risultato. Quindi nella sessione ci occuperemo del dipartimento?
gstackoverflow,

1
@gstackoverflow, yes
Dherik,

Uso la query nativa con Lazy Fetch in entrambi i lati della relazione, ma continuo a caricare la gerarchia delle relazioni figlio.
DVB

Vale la pena ricordare che fetchdeve essere utilizzato se (usando il nostro esempio) si desidera ordinare in base ad un attributo Reparto. Altrimenti, (valido almeno per PG) potresti ottenereERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
lungo il

60

in questo link che ho menzionato prima nel commento, leggi questa parte:

Un join "fetch" consente l'inizializzazione di associazioni o raccolte di valori insieme ai loro oggetti padre utilizzando una sola selezione. Ciò è particolarmente utile nel caso di una collezione. Sovrascrive efficacemente il join esterno e le dichiarazioni pigre del file di mapping per associazioni e raccolte.

questo "JOIN FETCH" avrà effetto se si dispone della proprietà (fetch = FetchType.LAZY) per una raccolta all'interno di un'entità (esempio sotto).

Ed è solo effetto del metodo di "quando la query dovrebbe accadere". E devi anche sapere questo :

l'ibernazione ha due nozioni ortogonali: quando viene recuperata l'associazione e come viene recuperata. È importante non confonderli. Usiamo il recupero per ottimizzare le prestazioni. Possiamo usare lazy per definire un contratto per quali dati sono sempre disponibili in qualsiasi istanza distaccata di una particolare classe.

quando viene recuperata l'associazione -> il tuo tipo "FETCH"

come viene recuperato -> Unisci / seleziona / Sottoseleziona / Batch

Nel tuo caso, FETCH avrà effetto solo se hai un dipartimento come set all'interno del Dipendente, qualcosa del genere nell'entità:

@OneToMany(fetch = FetchType.LAZY)
private Set<Department> department;

quando usi

FROM Employee emp
JOIN FETCH emp.department dep

otterrai empe emp.dep. quando non hai usato il recupero puoi ancora ottenere emp.depma l'ibernazione elaborerà un'altra selezione nel database per ottenere quel set di dipartimenti.

quindi è solo una questione di ottimizzazione delle prestazioni, su di te che vuoi ottenere tutti i risultati (ne hai bisogno o no) in una singola query (recupero desideroso), o vuoi interrogarlo successivamente quando ne hai bisogno (recupero pigro).

Usa il recupero desideroso quando devi ottenere piccoli dati con una selezione (una query grande). Oppure usa il recupero pigro per interrogare ciò di cui hai bisogno in seguito (molte query più piccole).

usa recupera quando:

  • nessuna grande raccolta / set non necessari all'interno di quell'entità che stai per ottenere

  • la comunicazione dal server delle applicazioni al server di database è troppo lunga e richiede molto tempo

  • potresti aver bisogno di quest'ultima raccolta quando non hai accesso ad essa ( al di fuori del metodo / classe transazionale )


Potresti spiegarlo per le domande che ho appena scritto nella domanda aggiornata.
abbas,

considerazione utile: "nessuna grande raccolta / set non necessario all'interno di quell'entità che stai per ottenere"
Divs

I dipartimenti sarebbero ancora avidamente recuperati se i dipartimenti all'interno del dipendente fossero un Listinvece di un Set?
Stephane,

L'uso della FETCHparola chiave in un'istruzione JPQL implica una proprietà recuperata con entusiasmo?
Stephane,

15

ADERIRE

Quando si utilizza JOINcontro associazioni di entità, JPA genererà un JOIN tra l'entità padre e le tabelle entità figlio nell'istruzione SQL generata.

Quindi, prendendo il tuo esempio, quando esegui questa query JPQL:

FROM Employee emp
JOIN emp.department dep

Hibernate genererà la seguente istruzione SQL:

SELECT emp.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id

Si noti che la SELECTclausola SQL contiene solo le employeecolonne della tabella e non departmentquelle. Per recuperare le departmentcolonne della tabella, è necessario utilizzare JOIN FETCHinvece di JOIN.

UNISCITI FETCH

Pertanto, rispetto a JOIN, JOIN FETCHconsente di proiettare le colonne della tabella di join nella SELECTclausola dell'istruzione SQL generata.

Quindi, nel tuo esempio, quando esegui questa query JPQL:

FROM Employee emp
JOIN FETCH emp.department dep

Hibernate genererà la seguente istruzione SQL:

SELECT emp.*, dept.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id

Si noti che, questa volta, departmentvengono selezionate anche le colonne della tabella, non solo quelle associate all'entità elencata nella FROMclausola JPQL.

Inoltre, JOIN FETCHè un ottimo modo per affrontare LazyInitializationExceptionquando si utilizza Hibernate in quanto è possibile inizializzare le associazioni di entità utilizzando la FetchType.LAZYstrategia di recupero insieme all'entità principale che si sta recuperando.


È possibile utilizzare più JOIN FETCH nella stessa query?
A. Onur Özcan il

2
Puoi ISCRIVERSI FETCH a più associazioni one-to-one e one-to-one e al massimo una raccolta. Il recupero di più raccolte, come le associazioni una-a-molte o molte-a-molte, finirà in un prodotto cartesiano . Tuttavia, se si desidera recuperare più raccolte, è possibile utilizzare query secondarie per la seconda, terza, ..., ennesima raccolta. Dai un'occhiata a questo articolo per maggiori dettagli.
Vlad Mihalcea il

5

Se la @oneToOnemappatura è impostata su FetchType.LAZYe si utilizza la seconda query (poiché è necessario caricare gli oggetti Department come parte degli oggetti Employee), ciò che Hibernate farà, invierà query per recuperare gli oggetti Department per ogni singolo oggetto Employee che recupera dal DB.

Successivamente, nel codice è possibile accedere agli oggetti Dipartimento tramite l'associazione a valore singolo da Dipendente a Dipartimento e Hibernate non invierà alcuna query per recuperare l'oggetto Dipartimento per il Dipendente specificato.

Ricorda, Hibernate emette comunque query pari al numero di dipendenti che ha recuperato. Hibernate emetterà lo stesso numero di query in entrambe le query precedenti, se si desidera accedere agli oggetti Reparto di tutti gli oggetti Dipendente


2

Dherik: Non sono sicuro di quello che dici, quando non usi il recupero il risultato sarà di tipo: il List<Object[ ]>che significa un elenco di tabelle di oggetti e non un elenco di Employee.

Object[0] refers an Employee entity 
Object[1] refers a Departement entity 

Quando si utilizza il recupero, è disponibile una sola selezione e il risultato è l'elenco dei Dipendenti List<Employee>contenente l'elenco dei dipartimenti. Sostituisce la dichiarazione pigra dell'entità.


Non so se capisco la tua preoccupazione. Se non si utilizza fetch, la query restituirà solo i dipendenti. Se i Dipartimenti, anche in questo caso, continuano a essere restituiti, è perché la mappatura tra Employee e Department (a @OneToMany) è impostata con FetchType.EAGER. In questo caso, qualsiasi fetchquery HQL (con o meno) con FROM Employeeporterà tutti i Dipartimenti.
Dherik,

Senza utilizzare fetch (solo termine join), il risultato sarebbe una matrice di raccolte, due righe, la prima è una raccolta di dipendenti e la seconda è una raccolta di dipartimenti. Usando il recupero desideroso o il recupero pigro, i reparti verranno recuperati.
Bilal BBB,

Senza recupero su HQL, questo accadrà solo se la mappatura tra Employee e Department è EAGER ( @OneToMany(fetch = FetchType.EAGER). In caso contrario, i Dipartimenti non verranno restituiti.
Dherik,

@Dherik provalo tu stesso, otterrai una ClassCastException.
Bilal BBB

Capisco il problema. Non è un problema di recupero, ma come è selectstato realizzato in HQL. Prova SELECT emp FROM Employee emp JOIN FETCH emp.department dep. JPA / Ibernazione hanno questo comportamento di ritorno a Listdi Object[]quando si omette la SELECTparte.
Dherik,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.