beschrijving van het probleem

Zoals we hierboven zeiden, heeft de LazyCollectionOption.EXTRA- annotatie een probleem: het voert voor elk object een afzonderlijk verzoek uit naar de database. We moeten op de een of andere manier aan Hibernate uitleggen dat we willen dat het onmiddellijk alle onderliggende objecten voor onze bovenliggende objecten laadt.

De ontwikkelaars van Hibernate hebben een oplossing bedacht voor dit probleem, de join fetch operator in HQL.

Voorbeeld van HQL-query:

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

In deze query is alles tegelijkertijd eenvoudig en complex. Laten we proberen het stuk voor stuk op te bouwen.

Optie 1

We willen alle objecten downloadentaak, gesorteerd op deadline. Dit is hoe dat verzoek eruit zou zien:

select task from Task t order by t.deadline

Terwijl alles duidelijk is. Maar het veldmedewerkervan de klasse Taak zal een verzameling werknemers bevatten die zijn geannoteerd met de EXTRA- annotatie . En de objecten van deze collectie worden niet geladen.

Optie 2

Forceer Hibernate om onderliggende objecten voor een object te ladentaak.

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

Met hulp binden we expliciet de entiteiten Taak en Werknemer in onze query. Hibernate weet dit al omdat we de @ManyToMany- annotaties op deze velden gebruiken.

Maar we hebben een join- instructie nodig om het te voltooien met een fetch -instructie om een ​​join-fetch te krijgen . Dit is hoe we Hibernate vertellen dat de objecten in de Task.employee-collecties uit de database moeten worden geladen wanneer ons verzoek wordt uitgevoerd.

Optie 3

De vorige oplossing heeft een paar bugs. Ten eerste zal SQL na het gebruik van join geen objecten naar ons retournerentaak, waaraan geen objecten zijn gekoppeld in de tabel Werknemer. Dit is precies hoe inner join werkt .

We moeten onze join dus uitbreiden met een linkeroperator en er een linkerjoin van maken . Voorbeeld:

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

Optie 4

Maar dat is niet alles. Als in uw code de relatie tussen entiteiten veel-op-mei is, zullen er duplicaten zijn in de queryresultaten. Hetzelfde voorwerptaakzijn te vinden op verschillende werknemers (Employee-objecten).

U moet dus het afzonderlijke trefwoord toevoegen na het geselecteerde woord om het dubbele Task-object te verwijderen.

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

Zo kwamen we in 4 stappen tot de aanvraag waarmee we begonnen. Welnu, de Java-code ziet er nogal verwacht uit:

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-beperkingen

Niemand is perfect. JOIN FETCH-instructie ook. Het heeft nogal wat beperkingen. En de eerste gebruikt de methoden setMaxResults() en setFirstResult() .

Voor de JOIN FETCH-instructie genereert onze Hibernate een zeer complexe query waarin we drie tabellen combineren tot één: werknemer, taak en werknemer_taak. In feite is dit geen aanvraag voor medewerkers of taken, maar voor alle bekende medewerker-taakparen.

En SQL kan zijn LIMIT- en OFFSET-instructies toepassen op precies die query van werknemer-taakparen. Tegelijkertijd volgt duidelijk uit de HQL-query dat we precies taken (Task) willen krijgen, en als we onze FirstResult- en MaxResult-parameters opnieuw verdelen, dan moeten ze specifiek verwijzen naar Task-objecten.

Als je code als volgt schrijft:

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

Dan kan Hibernate FirstResult en MaxResult niet correct converteren naar de OFFSET- en LIMIT-parameters van de SQL-query.

In plaats daarvan zal het drie dingen doen:

  • SQL-query selecteert over het algemeen alle gegevens uit de tabel en zet deze terug in Hibernate
  • Hibernate selecteert de benodigde records in zijn geheugen en stuurt ze naar u terug
  • Hibernate zal een waarschuwing geven

De waarschuwing zal ongeveer zo zijn:

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