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!