Beschreibung des Problems

Wie oben erwähnt, weist die Annotation LazyCollectionOption.EXTRA ein Problem auf: Sie führt für jedes Objekt eine separate Anfrage an die Datenbank durch. Wir müssen Hibernate irgendwie erklären, dass es sofort alle untergeordneten Objekte für unsere übergeordneten Objekte laden soll.

Die Entwickler von Hibernate haben eine Lösung für dieses Problem gefunden, den Join-Fetch- Operator in HQL.

Beispiel für eine HQL-Abfrage:

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

Bei dieser Abfrage ist alles einfach und komplex zugleich. Versuchen wir, es Stück für Stück aufzubauen.

Variante 1

Wir wollen alle Objekte herunterladenAufgabe, sortiert nach Frist. So würde diese Anfrage aussehen:

select task from Task t order by t.deadline

Während alles klar ist. Aber das FeldMitarbeiterder Task- Klasse enthält eine Sammlung von Employees, die mit der EXTRA- Annotation versehen sind . Und die Objekte dieser Sammlung werden nicht geladen.

Option 2

Erzwingen Sie, dass Hibernate untergeordnete Objekte für ein Objekt lädtAufgabe.

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

Mit Hilfe binden wir die Task- und Employee-Entitäten explizit in unserer Abfrage. Hibernate weiß dies bereits, da wir die @ManyToMany- Anmerkungen für diese Felder verwenden.

Aber wir brauchen eine Join- Anweisung , um es mit einer Fetch- Anweisung zu vervollständigen , um einen Join-Fetch zu erhalten . Auf diese Weise teilen wir Hibernate mit, dass die Objekte in den Task.employee-Sammlungen aus der Datenbank geladen werden müssen, wenn unsere Anfrage ausgeführt wird.

Option 3

Die vorherige Lösung weist einige Fehler auf. Erstens gibt SQL nach der Verwendung von Join keine Objekte an uns zurückAufgabe, denen in der Employee-Tabelle keine Objekte zugeordnet sind. Genau so funktioniert Inner Join .

Daher müssen wir unseren Join um einen linken Operator erweitern und ihn in einen linken Join umwandeln . Beispiel:

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

Option 4

Aber das ist nicht alles. Wenn in Ihrem Code die Beziehung zwischen Entitäten eine Viele-zu-Mai-Beziehung ist, werden in den Abfrageergebnissen Duplikate auftreten. Das gleiche ObjektAufgabekönnen auf verschiedenen Mitarbeitern gefunden werden (Mitarbeiterobjekte).

Sie müssen also das Schlüsselwort „distinct“ nach dem ausgewählten Wort hinzufügen, um das doppelte Task-Objekt zu entfernen.

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

So kamen wir in 4 Schritten zu der Anfrage, mit der wir angefangen haben. Nun, der Java-Code wird wie erwartet aussehen:

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-Einschränkungen

Niemand ist perfekt. JOIN FETCH-Anweisung auch. Es gibt einige Einschränkungen. Und die erste verwendet die Methoden setMaxResults() und setFirstResult() .

Für die JOIN FETCH-Anweisung generiert unser Hibernate eine sehr komplexe Abfrage, in der wir drei Tabellen zu einer kombinieren: Employee, Task und Employee_task. Tatsächlich handelt es sich hierbei nicht um eine Anfrage für Mitarbeiter oder Aufgaben, sondern für alle bekannten Mitarbeiter-Aufgaben-Paare.

Und SQL kann seine LIMIT- und OFFSET-Anweisungen auf genau diese Abfrage von Mitarbeiter-Aufgaben-Paaren anwenden. Gleichzeitig geht aus der HQL-Abfrage klar hervor, dass wir genau Aufgaben (Task) erhalten möchten, und wenn wir unsere Parameter FirstResult und MaxResult neu verteilen, sollten sie sich speziell auf Task-Objekte beziehen.

Wenn Sie Code wie diesen schreiben:

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

Dann kann Hibernate FirstResult und MaxResult nicht korrekt in die Parameter OFFSET und LIMIT der SQL-Abfrage konvertieren.

Stattdessen werden drei Dinge ausgeführt:

  • Die SQL-Abfrage wählt im Allgemeinen alle Daten aus der Tabelle aus und gibt sie an Hibernate zurück
  • Hibernate wählt die erforderlichen Datensätze in seinem Speicher aus und sendet sie an Sie zurück
  • Der Ruhezustand gibt eine Warnung aus

Die Warnung wird etwa so aussehen:

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