Beskrivning av problemet

Som vi sa ovan har LazyCollectionOption.EXTRA -anteckningen ett problem - den utför en separat begäran till databasen för varje objekt. Vi måste på något sätt förklara för Hibernate att vi vill att den omedelbart ska ladda alla underordnade objekt för våra överordnade objekt.

Utvecklarna av Hibernate har kommit på en lösning på detta problem, join-fetch -operatören i HQL.

Exempel på HQL-fråga:

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

I den här frågan är allt enkelt och komplext på samma gång. Låt oss försöka konstruera den bit för bit.

Alternativ 1

Vi vill ladda ner alla objektuppgift, sorterade efter deadline. Så här skulle begäran se ut:

select task from Task t order by t.deadline

Medan allt är klart. Men fältetanställdi klassen Task kommer att innehålla en samling anställda som är kommenterade med EXTRA -anteckningen . Och föremålen i den här samlingen kommer inte att laddas.

Alternativ 2

Tvinga viloläge för att ladda underordnade objekt för ett objektuppgift.

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

Med hjälp binder vi uttryckligen Uppgifts- och Anställd-enheterna i vår förfrågan. Hibernate vet redan detta eftersom vi använder @ManyToMany -anteckningarna på dessa fält.

Men vi behöver en join -sats för att komplettera den med en fetch -sats för att få en join-hämtning . Så här säger vi till Hibernate att objekten i Task.employee-samlingarna måste laddas från databasen när vår begäran exekveras.

Alternativ 3

Den tidigare lösningen har några buggar. För det första, efter att ha använt join kommer SQL inte att returnera objekt till ossuppgift, som inte har några objekt kopplade till sig i tabellen Employee. Det är precis så inre sammanfogning fungerar .

Så vi måste utöka vår koppling med en vänster operatör och förvandla den till en vänster koppling . Exempel:

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

Alternativ 4

Men det är inte allt. Om förhållandet mellan entiteter i din kod är många till maj, kommer det att finnas dubbletter i frågeresultaten. Samma föremåluppgiftfinns på olika anställda (Anställd objekt).

Så du måste lägga till det distinkta nyckelordet efter det valda ordet för att bli av med det duplicerade Task-objektet.

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

Så här kom vi i 4 steg fram till förfrågan som vi började med. Ja, Java-koden kommer att se ganska förväntad ut:

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

GÅ MED HÄMTNING Begränsningar

Ingen är perfekt. JOIN FETCH uttalande också. Den har en del begränsningar. Och den första använder metoderna setMaxResults() och setFirstResult() .

För JOIN FETCH-satsen kommer vår Hibernate att generera en mycket komplex fråga där vi kombinerar tre tabeller till en: anställd, uppgift och anställd_uppgift. I själva verket är detta inte en begäran om anställda eller uppgifter, utan för alla kända anställd-uppgiftspar.

Och SQL kan tillämpa sina LIMIT- och OFFSET-satser på exakt den frågan av anställd-uppgiftspar. Samtidigt följer det tydligt av HQL-frågan att vi vill få exakt uppgifter (Task), och om vi omfördelar våra FirstResult- och MaxResult-parametrar så ska de referera specifikt till Task-objekt.

Om du skriver kod så här:

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

Då kan Hibernate inte korrekt konvertera FirstResult och MaxResult till OFFSET- och LIMIT-parametrarna i SQL-frågan.

Istället kommer det att göra tre saker:

  • SQL-frågan väljer i allmänhet all data från tabellen och returnerar den till viloläge
  • Hibernate väljer de nödvändiga posterna i sitt minne och returnerar dem till dig
  • Hibernate kommer att utfärda en varning

Varningen blir ungefär så här:

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