Descrizione del problema
Come abbiamo detto sopra, l' annotazione LazyCollectionOption.EXTRA ha un problema: esegue una richiesta separata al database per ogni oggetto. Dobbiamo in qualche modo spiegare a Hibernate che vogliamo che carichi immediatamente tutti gli oggetti figli per i nostri oggetti genitori.
Gli sviluppatori di Hibernate hanno escogitato una soluzione a questo problema, l' operatore join fetch in HQL.
Esempio di query HQL:
select distinct task from Task t left join fetch t.employee order by t.deadline
In questa query tutto è semplice e complesso allo stesso tempo. Proviamo a costruirlo pezzo per pezzo.
opzione 1
Vogliamo scaricare tutti gli oggetticompito, ordinati per scadenza. Ecco come sarebbe la richiesta:
select task from Task t order by t.deadline
Mentre tutto è chiaro. Ma il campodipendentedella classe Task conterrà una raccolta di Employees annotati con l' annotazione EXTRA . E gli oggetti di questa collezione non verranno caricati.
opzione 2
Forza l'ibernazione a caricare oggetti figlio per un oggettocompito.
select task from Task t join fetch t.employee order by t.deadline
Con l'aiuto, leghiamo esplicitamente le entità Task e Employee nella nostra query. Hibernate lo sa già perché usiamo le annotazioni @ManyToMany su questi campi.
Ma abbiamo bisogno di un'istruzione join per completarla con un'istruzione fetch per ottenere un join fetch . Questo è il modo in cui diciamo a Hibernate che gli oggetti nelle raccolte Task.employee devono essere caricati dal database quando viene eseguita la nostra richiesta.
Opzione 3
La soluzione precedente presenta alcuni bug. Innanzitutto, dopo aver utilizzato join, SQL non ci restituirà oggetticompito, a cui non sono associati oggetti nella tabella Employee. Questo è esattamente il modo in cui funziona l'inner join .
Quindi dobbiamo aumentare il nostro join con un operatore sinistro e trasformarlo in un join sinistro . Esempio:
select task from Task t left join fetch t.employee order by t.deadline
Opzione 4
Ma non è tutto. Se nel tuo codice la relazione tra le entità è molti a maggio, ci saranno duplicati nei risultati della query. Lo stesso oggettocompitopossono essere trovati su diversi dipendenti (oggetti dei dipendenti).
Quindi è necessario aggiungere la parola chiave distinta dopo la parola selezionata per eliminare l'oggetto Task duplicato.
select distinct task from Task t left join fetch t.employee order by t.deadline
È così che in 4 passi siamo arrivati alla richiesta con cui siamo partiti. Bene, il codice Java sembrerà abbastanza previsto:
String hql = " select distinct task from Task t left join fetch t.employee order by t.deadline";
Query<Task> query = session.createQuery( hql, Task.class);
return query.list();
JOIN FETCH Limitazioni
Nessuno è perfetto. JOIN FETCH dichiarazione troppo. Ha parecchie limitazioni. E il primo utilizza i metodi setMaxResults() e setFirstResult() .
Per l'istruzione JOIN FETCH, il nostro Hibernate genererà una query molto complessa in cui combiniamo tre tabelle in una sola: employee, task e employee_task. In realtà, questa non è una richiesta per dipendenti o compiti, ma per tutte le coppie di dipendenti-mansioni conosciute.
E SQL può applicare le sue istruzioni LIMIT e OFFSET esattamente a quella query di coppie dipendente-attività. Allo stesso tempo, dalla query HQL risulta chiaramente che vogliamo ottenere esattamente le attività (Task) e se ridistribuiamo i nostri parametri FirstResult e MaxResult, dovrebbero fare riferimento specificamente agli oggetti Task.
Se scrivi codice come questo:
String hql = " select distinct task from Task t left join fetch t.employee order by t.deadline";
Query<Task> query = session.createQuery( hql, Task.class);
query.setFirstResult(0);
query.setMaxResults(1);
return query.list();
Quindi Hibernate non sarà in grado di convertire correttamente FirstResult e MaxResult nei parametri OFFSET e LIMIT della query SQL.
Invece, farà tre cose:
- La query SQL selezionerà generalmente tutti i dati dalla tabella e li restituirà a Hibernate
- Hibernate selezionerà i record necessari nella sua memoria e te li restituirà
- Hibernate emetterà un avviso
L'avviso sarà qualcosa del genere:
WARN [org.hibernate.hql.internal.ast.QueryTranslatorImpl] HHH000104:
firstResult/maxResults specified with collection fetch; applying in memory!
GO TO FULL VERSION