Was ist der Unterschied zwischen JOIN und JOIN FETCH bei Verwendung von JPA und Hibernate?

182

Bitte helfen Sie mir zu verstehen, wo Sie einen regulären JOIN und wo einen JOIN FETCH verwenden können.

Zum Beispiel, wenn wir diese beiden Abfragen haben

FROM Employee emp
JOIN emp.department dep

und

FROM Employee emp
JOIN FETCH emp.department dep

Gibt es einen Unterschied zwischen ihnen? Wenn ja, welches wann?

abbas
quelle
2
Sie finden es hier Link lesen 14.3. Verbände und
Angga
4
Ich habe diese Dokumentation durchlaufen, weiß aber immer noch nicht, wo ich einen JOIN und wo einen JOIN FETCH verwenden soll.
Abbas
2
Wenn Sie die @ oneToOne-Zuordnung auf FetchType.LAZY festgelegt haben und eine zweite Abfrage verwenden (da Abteilungsobjekte als Teil von Mitarbeiterobjekten geladen werden müssen), werden im Ruhezustand Abfragen zum Abrufen von Abteilungsobjekten für jedes einzelne Mitarbeiterobjekt ausgegeben es holt von DB. Später im Code können Sie über die einwertige Zuordnung von Mitarbeiter zu Abteilung auf Abteilungsobjekte zugreifen, und der Ruhezustand gibt keine Abfrage zum Abrufen des Abteilungsobjekts für den angegebenen Mitarbeiter aus. Denken Sie daran, dass der Ruhezustand weiterhin Abfragen ausgibt, die der Anzahl der abgerufenen Mitarbeiter entsprechen.
Bunti
Um bei der Dokumentensuche zu helfen ~ Strategien holen
Eddie B
1
@ShameeraAnuranga Ich denke, in diesem Fall benötigen Sie einen LEFT OUTER JOIN.
abbas

Antworten:

179

In diesen beiden Abfragen verwenden Sie JOIN, um alle Mitarbeiter abzufragen, denen mindestens eine Abteilung zugeordnet ist.

Der Unterschied besteht jedoch darin, dass Sie in der ersten Abfrage nur die Mitarbeiter für den Ruhezustand zurückgeben. In der zweiten Abfrage geben Sie die Mitarbeiter und alle zugeordneten Abteilungen zurück.

Wenn Sie also die zweite Abfrage verwenden, müssen Sie keine neue Abfrage durchführen, um die Datenbank erneut aufzurufen und die Abteilungen der einzelnen Mitarbeiter anzuzeigen.

Sie können die zweite Abfrage verwenden, wenn Sie sicher sind, dass Sie die Abteilung jedes Mitarbeiters benötigen. Wenn Sie die Abteilung nicht benötigen, verwenden Sie die erste Abfrage.

Ich empfehle, diesen Link zu lesen, wenn Sie eine WHERE-Bedingung anwenden müssen (was Sie wahrscheinlich benötigen werden): Wie kann man JPQL "join fetch" mit der "where" -Klausel als JPA 2 CriteriaQuery richtig ausdrücken?

Aktualisieren

Wenn Sie nicht verwenden fetchund die Abteilungen weiterhin zurückgegeben werden, liegt dies daran, dass Ihre Zuordnung zwischen Mitarbeiter und Abteilung (a @OneToMany) mit festgelegt ist FetchType.EAGER. In diesem Fall bringt jede HQL- fetchAbfrage (mit oder nicht) mit FROM Employeealle Abteilungen. Denken Sie daran, dass alle Zuordnungen * ToOne ( @ManyToOneund @OneToOne) standardmäßig EAGER sind.

Dherik
quelle
1
Welches Verhalten wird sein, wenn wir eine Anweisung ohne Abruf ausführen und ein Ergebnis erhalten. Dann werden wir innerhalb der Sitzung die Abteilung behandeln?
gstackoverflow
1
@gstackoverflow, ja
Dherik
Ich verwende native Abfragen mit Lazy Fetch auf beiden Seiten der Beziehung, lade aber immer noch die Hierarchie der untergeordneten Beziehungen.
Badamchi
Erwähnenswert fetchist, dass dies verwendet werden muss, wenn Sie (anhand unseres Beispiels) nach einem Abteilungsattribut bestellen möchten. Andernfalls (gültig zumindest für PG) könnten SieERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
lange
60

In diesem Link, den ich zuvor im Kommentar erwähnt habe, lesen Sie diesen Teil:

Mit einem "Abruf" -Verbindungspunkt können Zuordnungen oder Auflistungen von Werten zusammen mit ihren übergeordneten Objekten mit einer einzigen Auswahl initialisiert werden. Dies ist besonders nützlich bei einer Sammlung. Es überschreibt effektiv die äußeren Verknüpfungen und verzögerten Deklarationen der Zuordnungsdatei für Zuordnungen und Sammlungen.

Dieser "JOIN FETCH" hat seine Wirkung, wenn Sie die Eigenschaft (fetch = FetchType.LAZY) für eine Sammlung innerhalb einer Entität haben (Beispiel unten).

Und es ist nur die Methode "wann die Abfrage erfolgen soll". Und Sie müssen auch wissen , diese :

Im Ruhezustand gibt es zwei orthogonale Begriffe: Wann und wie wird die Zuordnung abgerufen? Es ist wichtig, dass Sie sie nicht verwechseln. Wir verwenden Fetch, um die Leistung zu optimieren. Wir können Lazy verwenden, um einen Vertrag für die Daten zu definieren, die in einer getrennten Instanz einer bestimmten Klasse immer verfügbar sind.

Wann wird die Zuordnung abgerufen -> Ihr Typ "FETCH"

wie wird es abgerufen -> Join / select / Subselect / Batch

In Ihrem Fall hat FETCH nur dann eine Wirkung, wenn Sie eine Abteilung als Set in Employee haben, etwa so in der Entität:

@OneToMany(fetch = FetchType.LAZY)
private Set<Department> department;

wenn Sie verwenden

FROM Employee emp
JOIN FETCH emp.department dep

du wirst bekommen empund emp.dep. Wenn Sie Fetch nicht verwendet haben, können Sie immer noch abrufen, emp.depaber der Ruhezustand verarbeitet eine weitere Auswahl in der Datenbank, um diese Abteilung zu erhalten.

Es handelt sich also nur um eine Leistungsoptimierung. Sie möchten alle Ergebnisse (Sie benötigen sie oder nicht) in einer einzigen Abfrage erhalten (eifriges Abrufen) oder Sie möchten sie später abfragen, wenn Sie sie benötigen (verzögertes Abrufen).

Verwenden Sie eifriges Abrufen, wenn Sie kleine Daten mit einer Auswahl (einer großen Abfrage) abrufen möchten. Oder verwenden Sie Lazy Fetching, um abzufragen, was Sie letzteres benötigen (viele kleinere Abfragen).

Verwenden Sie fetch, wenn:

  • Keine große nicht benötigte Sammlung / Menge innerhalb der Entität, die Sie erhalten möchten

  • Kommunikation vom Anwendungsserver zum Datenbankserver zu weit und dauert lange

  • Möglicherweise benötigen Sie diese Sammlung, wenn Sie keinen Zugriff darauf haben ( außerhalb der Transaktionsmethode / -klasse).

Angga
quelle
Könnten Sie es für die Fragen erklären, die ich gerade in der aktualisierten Frage geschrieben habe?
Abbas
nützliche Überlegung: "Keine große, nicht benötigte Sammlung / Menge in dieser Entität, die Sie gleich erhalten werden"
Divs
Würden die Abteilungen immer noch eifrig abgerufen werden, wenn die Abteilungen innerhalb des Mitarbeiters eine Liststatt eine wären Set?
Stephane
Bedeutet die Verwendung des FETCHSchlüsselworts in einer JPQL-Anweisung eine eifrig abgerufene Eigenschaft?
Stephane
15

BEITRETEN

Bei Verwendung JOINfür Entitätszuordnungen generiert JPA einen JOIN zwischen der übergeordneten Entität und den untergeordneten Entitätstabellen in der generierten SQL-Anweisung.

Nehmen Sie also Ihr Beispiel, wenn Sie diese JPQL-Abfrage ausführen:

FROM Employee emp
JOIN emp.department dep

Hibernate generiert die folgende SQL-Anweisung:

SELECT emp.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id

Beachten Sie, dass die SQL- SELECTKlausel nur die employeeTabellenspalten enthält und nicht die department. Um die departmentTabellenspalten abzurufen , müssen wir JOIN FETCHanstelle von verwenden JOIN.

FETCH BEITRETEN

Also, im Vergleich zu JOIN, die JOIN FETCHermöglicht es Ihnen , die Verbindungstabellenspalten in der Projekt SELECTKlausel der generierten SQL - Anweisung.

In Ihrem Beispiel bei der Ausführung dieser JPQL-Abfrage:

FROM Employee emp
JOIN FETCH emp.department dep

Hibernate generiert die folgende SQL-Anweisung:

SELECT emp.*, dept.*
FROM employee emp
JOIN department dep ON emp.department_id = dep.id

Beachten Sie, dass diesmal auch die departmentTabellenspalten ausgewählt werden, nicht nur diejenigen, die der in der FROMJPQL-Klausel aufgeführten Entität zugeordnet sind .

Dies JOIN FETCHist auch eine gute Möglichkeit, LazyInitializationExceptiondas FetchType.LAZYProblem bei der Verwendung des Ruhezustands zu beheben, da Sie Entitätszuordnungen mithilfe der Abrufstrategie zusammen mit der Hauptentität, die Sie abrufen, initialisieren können .

Vlad Mihalcea
quelle
Ist es möglich, mehrere JOIN FETCH in derselben Abfrage zu verwenden?
A.Onur Özcan
2
Sie können sich mehreren Viele-zu-Eins- und Eins-zu-Eins-Assoziationen und höchstens einer Sammlung anschließen. Das Abrufen mehrerer Sammlungen, z. B. Eins-zu-Viele- oder Viele-zu-Viele-Assoziationen, führt zu einem kartesischen Produkt . Wenn Sie jedoch mehrere Sammlungen abrufen möchten, können Sie sekundäre Abfragen für die zweite, dritte, ..., n-te Sammlung verwenden. Weitere Informationen finden Sie in diesem Artikel .
Vlad Mihalcea
5

Wenn Sie die @oneToOneZuordnung auf festgelegt haben FetchType.LAZYund eine zweite Abfrage verwenden (da Abteilungsobjekte als Teil von Mitarbeiterobjekten geladen werden müssen), werden im Ruhezustand Abfragen ausgeführt, um Abteilungsobjekte für jedes einzelne Mitarbeiterobjekt abzurufen, das aus der Datenbank abgerufen wird.

Später im Code können Sie über die einwertige Zuordnung von Mitarbeiter zu Abteilung auf Abteilungsobjekte zugreifen, und der Ruhezustand gibt keine Abfrage zum Abrufen des Abteilungsobjekts für den angegebenen Mitarbeiter aus.

Denken Sie daran, dass Hibernate weiterhin Abfragen ausgibt, die der Anzahl der abgerufenen Mitarbeiter entsprechen. Der Ruhezustand gibt in beiden oben genannten Abfragen die gleiche Anzahl von Abfragen aus, wenn Sie auf Abteilungsobjekte aller Mitarbeiterobjekte zugreifen möchten

Bunti
quelle
2

Dherik: Ich bin mir nicht sicher, was Sie sagen. Wenn Sie Fetch nicht verwenden, ist das Ergebnis vom Typ: Dies List<Object[ ]>bedeutet eine Liste von Objekttabellen und keine Liste von Mitarbeitern.

Object[0] refers an Employee entity 
Object[1] refers a Departement entity 

Wenn Sie Fetch verwenden, gibt es nur eine Auswahl und das Ergebnis ist die Liste der Mitarbeiter, List<Employee>die die Liste der Abteilungen enthält. Es überschreibt die verzögerte Deklaration der Entität.

Bilal BBB
quelle
Ich weiß nicht, ob ich Ihre Besorgnis verstehe. Wenn Sie nicht verwenden fetch, gibt Ihre Abfrage nur die Mitarbeiter zurück. Wenn die Abteilungen auch in diesem Fall weiterhin zurückgegeben werden, liegt dies daran, dass Ihre Zuordnung zwischen Mitarbeiter und Abteilung (ein @OneToMany) mit FetchType.EAGER festgelegt wurde. In diesem Fall bringt jede HQL- fetchAbfrage (mit oder nicht) mit FROM Employeealle Abteilungen.
Dherik
Ohne die Verwendung von fetch (nur Join-Begriff) wäre das Ergebnis ein Array von Sammlungen, zwei Zeilen, die erste ist eine Sammlung von Mitarbeitern und die zweite ist eine Sammlung von Abteilungen. Mit eifrigem oder faulem Abruf werden Abteilungen abgerufen.
Bilal BBB
Ohne Abruf auf HQL geschieht dies nur, wenn Ihre Zuordnung zwischen Mitarbeiter und Abteilung EAGER ( @OneToMany(fetch = FetchType.EAGER) ist. Ist dies nicht der Fall, werden die Abteilungen nicht zurückgesandt.
Dherik
@Dherik probieren Sie es selbst aus, Sie erhalten eine ClassCastException.
Bilal BBB
Ich finde das Problem heraus. Ist kein Abrufproblem, sondern wie das selectin der HQL gemacht wurde. Versuchen Sie es SELECT emp FROM Employee emp JOIN FETCH emp.department dep. JPA / Hibernate haben dieses Verhalten der Rückkehr eine Listder , Object[]wenn Sie den ommit SELECTTeil.
Dherik