Descrierea problemei

După cum am spus mai sus, adnotarea LazyCollectionOption.EXTRA are o problemă - efectuează o solicitare separată către baza de date pentru fiecare obiect. Trebuie să explicăm cumva lui Hibernate că vrem să încarce imediat toate obiectele copil pentru obiectele noastre părinte.

Dezvoltatorii Hibernate au venit cu o soluție la această problemă, operatorul join fetch în HQL.

Exemplu de interogare HQL:

select distinct task from Task t left join fetch t.employee order by t.deadline

În această interogare, totul este simplu și complex în același timp. Să încercăm să o construim bucată cu bucată.

Opțiunea 1

Vrem să descarcăm toate obiectelesarcină, sortate după termen. Iată cum ar arăta această solicitare:

select task from Task t order by t.deadline

În timp ce totul este clar. Dar câmpulangajatdin clasa Task va conține o colecție de angajați adnotați cu adnotarea EXTRA . Iar obiectele acestei colecții nu vor fi încărcate.

Opțiunea 2

Forțați Hibernarea să încarce obiecte copil pentru un obiectsarcină.

select task from Task t join fetch t.employee order by t.deadline

Cu ajutor, legăm în mod explicit entitățile Sarcină și Angajat din interogarea noastră. Hibernate știe deja acest lucru deoarece folosim adnotările @ManyToMany pe aceste câmpuri.

Dar avem nevoie de o instrucțiune join pentru a o completa cu o instrucțiune fetch pentru a obține un join fetch . Așa îi spunem lui Hibernate că obiectele din colecțiile Task.employee trebuie să fie încărcate din baza de date atunci când cererea noastră este executată.

Opțiunea 3

Soluția anterioară are câteva erori. În primul rând, după folosirea join, SQL nu ne va returna obiectesarcină, care nu au obiecte asociate cu ele în tabelul Angajați. Exact așa funcționează îmbinarea interioară .

Așa că trebuie să ne mărim uniunea cu un operator stânga și să o transformăm într-o alăturare stângă . Exemplu:

select task from Task t left join fetch t.employee order by t.deadline

Opțiunea 4

Dar asta nu este tot. Dacă în codul dvs. relația dintre entități este multi-la-mai, atunci vor exista duplicate în rezultatele interogării. Același obiectsarcinăpot fi găsite pe diferiți angajați (Obiecte de angajat).

Deci, trebuie să adăugați cuvântul cheie distinct după cuvântul selectat pentru a scăpa de obiectul Sarcină duplicat.

select distinct task from Task t left join fetch t.employee order by t.deadline

Așa am ajuns în 4 pași la solicitarea cu care am început. Ei bine, codul Java va arăta destul de așteptat:

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();

Limitări JOIN FETCH

Nimeni nu e perfect. De asemenea, declarația JOIN FETCH. Are destul de multe limitări. Și primul folosește metodele setMaxResults() și setFirstResult() .

Pentru instrucțiunea JOIN FETCH, Hibernate va genera o interogare foarte complexă în care combinăm trei tabele într-unul singur: angajat, task și employee_task. De fapt, aceasta nu este o solicitare pentru angajați sau sarcini, ci pentru toate perechile cunoscute angajat-sarcină.

Și SQL își poate aplica instrucțiunile LIMIT și OFFSET exact acelei interogări de perechi angajat-sarcină. În același timp, rezultă clar din interogarea HQL că dorim să obținem exact sarcini (Sarcina), iar dacă ne redistribuim parametrii FirstResult și MaxResult, atunci aceștia ar trebui să se refere în mod specific la obiectele Task.

Dacă scrieți cod așa:

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();

Atunci Hibernate nu va putea converti corect FirstResult și MaxResult în parametrii OFFSET și LIMIT ai interogării SQL.

În schimb, va face trei lucruri:

  • Interogarea SQL va selecta în general toate datele din tabel și le va returna în Hibernate
  • Hibernate va selecta înregistrările necesare în memoria sa și vi le va returna
  • Hibernare va emite un avertisment

Avertismentul va fi cam așa:

WARN [org.hibernate.hql.internal.ast.QueryTranslatorImpl] HHH000104: 
firstResult/maxResults specified with collection fetch; applying in memory!