Jaka jest różnica między JOIN i JOIN FETCH podczas korzystania z JPA i Hibernate

183

Proszę, pomóż mi zrozumieć, gdzie używać zwykłego JOIN, a gdzie JOIN FETCH.

Na przykład, jeśli mamy te dwa zapytania

FROM Employee emp
JOIN emp.department dep

i

FROM Employee emp
JOIN FETCH emp.department dep

Czy jest między nimi jakaś różnica? Jeśli tak, którego użyć kiedy?

abbas
źródło
2
można go znaleźć tutaj link czytaj 14.3. Stowarzyszenia i dołączenia
Angga
4
Przejrzałem tę dokumentację, ale nadal nie wiem, gdzie powinienem użyć JOIN, a gdzie JOIN FETCH.
abbas
2
Jeśli masz mapowanie @oneToOne ustawione na FetchType.LAZY i używasz drugiego zapytania (ponieważ potrzebujesz załadować obiekty działu jako część obiektów pracownika), to Hibernate zrobi, wyśle ​​zapytania w celu pobrania obiektów działu dla każdego pojedynczego obiektu pracownika pobiera z DB. W dalszej części kodu możesz uzyskać dostęp do obiektów działu za pośrednictwem jednowartościowego powiązania pracownika z działem, a Hibernate nie wyśle ​​żadnego zapytania w celu pobrania obiektu działu dla danego pracownika. Pamiętaj, że Hibernate nadal wysyła zapytania równe liczbie pobranych pracowników.
Bunti
Aby pomóc w polowaniu na dokumenty ~ Fetching Strategies
Eddie B
1
@ShameeraAnuranga Myślę, że w takim przypadku będziesz potrzebować LEFT OUTER JOIN.
abbas

Odpowiedzi:

181

W tych dwóch zapytaniach używasz JOIN do wysyłania zapytań do wszystkich pracowników, z którymi powiązany jest co najmniej jeden dział.

Ale różnica polega na tym, że w pierwszym zapytaniu zwracasz tylko Pracowników dla hibernacji. W drugim zapytaniu zwracasz pracowników i wszystkie powiązane działy.

Tak więc, jeśli użyjesz drugiego zapytania, nie będziesz musiał wykonywać nowego zapytania, aby ponownie trafić do bazy danych i zobaczyć działy każdego pracownika.

Możesz użyć drugiego zapytania, gdy jesteś pewien, że będziesz potrzebować działu każdego pracownika. Jeśli nie potrzebujesz działu, użyj pierwszego zapytania.

Polecam przeczytać ten link, jeśli chcesz zastosować warunek WHERE (co prawdopodobnie będziesz potrzebować): Jak poprawnie wyrazić JPQL "pobieranie złączenia" z klauzulą ​​"gdzie" jako Kryteria zapytania JPA 2?

Aktualizacja

Jeśli nie używasz, fetcha działy nadal są zwracane, jest to spowodowane ustawieniem mapowania między pracownikiem a działem (a @OneToMany) FetchType.EAGER. W takim przypadku każde fetchzapytanie HQL (z lub nie) z FROM Employeeprzyniesie wszystkie działy. Pamiętaj, że wszystkie mapowania * ToOne ( @ManyToOnei @OneToOne) są domyślnie EAGER.

Dherik
źródło
1
Jakie zachowanie będzie miało miejsce, jeśli wykonamy instrukcję bez pobierania i otrzymujemy wynik. Następnie w ramach sesji zajmiemy się działem?
gstackoverflow
1
@gstackoverflow, yes
Dherik
Używam natywnego zapytania z pobieraniem Lazy po obu stronach relacji, ale nadal ładuje hierarchię relacji potomnych.
Badamchi,
Warto wspomnieć, że fetchnależy tego użyć, jeśli (w naszym przykładzie) chcesz zamówić według jakiegoś atrybutu działu. W przeciwnym razie (ważne przynajmniej dla PG) możesz dostaćERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
długo
60

w tym linku wspomniałem wcześniej w komentarzu, przeczytaj tę część:

Łączenie „pobierania” umożliwia zainicjowanie asocjacji lub kolekcji wartości wraz z ich obiektami nadrzędnymi za pomocą pojedynczego wyboru. Jest to szczególnie przydatne w przypadku kolekcji. Skutecznie zastępuje sprzężenia zewnętrzne i leniwe deklaracje pliku mapowania dla skojarzeń i kolekcji.

to "JOIN FETCH" będzie miało skutek, jeśli masz (fetch = FetchType.LAZY) właściwość dla kolekcji wewnątrz encji (przykład poniżej).

I ma to wpływ tylko na metodę „kiedy zapytanie powinno się wydarzyć”. Musisz też wiedzieć, że :

hibernacja ma dwa ortogonalne pojęcia: kiedy jest pobierana asocjacja i jak jest pobierana. Ważne jest, aby ich nie mylić. Używamy pobierania do dostrajania wydajności. Możemy użyć leniwego, aby zdefiniować kontrakt, dla którego dane są zawsze dostępne w dowolnej odłączonej instancji określonej klasy.

kiedy jest pobierane skojarzenie -> Twój typ „FETCH”

jak jest pobierany -> Dołącz / wybierz / Subselect / Batch

W twoim przypadku FETCH odniesie skutek tylko wtedy, gdy masz dział jako zestaw wewnątrz pracownika, coś takiego w encji:

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

kiedy używasz

FROM Employee emp
JOIN FETCH emp.department dep

dostaniesz empi emp.dep. jeśli nie użyłeś pobierania, nadal możesz uzyskać, emp.depale hibernacja przetwarza kolejny wybór do bazy danych, aby uzyskać ten zestaw działu.

więc jest to tylko kwestia dostrojenia wydajności, o tym, że chcesz uzyskać wszystkie wyniki (potrzebujesz tego lub nie) w jednym zapytaniu (pobierające chętnie) lub chcesz zapytać je później, kiedy ich potrzebujesz (pobieranie z opóźnieniem).

Korzystaj z szybkiego pobierania, gdy chcesz uzyskać małe dane za pomocą jednego wyboru (jedno duże zapytanie). Lub użyj leniwego pobierania, aby zapytać o to, czego potrzebujesz (wiele mniejszych zapytań).

użyj pobierania, gdy:

  • nie ma dużej niepotrzebnej kolekcji / zestawu wewnątrz tej jednostki, którą masz zamiar dostać

  • komunikacja z serwera aplikacji do serwera bazy danych jest za daleko i zajmuje dużo czasu

  • Może trzeba, że zbiór ten ostatni, gdy nie masz dostępu do niego ( poza o transakcyjnej metody / klasy)

Angga
źródło
Czy mógłbyś to wyjaśnić dla zapytań, które właśnie napisałem w zaktualizowanym pytaniu.
abbas
przydatna uwaga: „nie ma dużej niepotrzebnej kolekcji / zestawu wewnątrz tej jednostki, którą za
chwilę
Czy działy nadal byłyby chętnie ściągane, gdyby działy wewnątrz pracownika były a Listzamiast a Set?
Stephane
Czy użycie FETCHsłowa kluczowego w instrukcji JPQL oznacza chętnie pobieraną właściwość?
Stephane
15

PRZYSTĄP

W przypadku korzystania JOINz powiązań jednostek JPA wygeneruje JOIN między jednostką nadrzędną a tabelami jednostek podrzędnych w wygenerowanej instrukcji SQL.

Na przykład, wykonując to zapytanie JPQL:

FROM Employee emp
JOIN emp.department dep

Hibernate wygeneruje następującą instrukcję SQL:

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

Zauważ, że SELECTklauzula SQL zawiera tylko employeekolumny tabeli, a nie departmentte. Aby pobrać departmentkolumny tabeli, musimy użyć JOIN FETCHzamiast JOIN.

DOŁĄCZ DO FETCH

Tak więc, w porównaniu z JOIN, JOIN FETCHpozwala na rzutowanie kolumn tabeli łączącej w SELECTklauzuli wygenerowanej instrukcji SQL.

Tak więc, w twoim przykładzie, podczas wykonywania tego zapytania JPQL:

FROM Employee emp
JOIN FETCH emp.department dep

Hibernate wygeneruje następującą instrukcję SQL:

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

Zwróć uwagę, że tym razem departmentwybrane są również kolumny tabeli, a nie tylko te związane z podmiotem wymienionym w FROMklauzuli JPQL.

Ponadto, JOIN FETCHjest to świetny sposób, aby zająć się LazyInitializationExceptionprzy użyciu Hibernate jak można zainicjować skojarzenia obiektu przy użyciu FetchType.LAZYstrategii pobierania wraz z głównym podmiotem pobierasz.

Vlad Mihalcea
źródło
Czy można użyć wielu funkcji JOIN FETCH w tym samym zapytaniu?
A.Onur Özcan
2
Możesz DOŁĄCZ DO FETCH wielu skojarzeń typu „wiele do jednego” i „jeden do jednego” oraz co najwyżej jednej kolekcji. Pobieranie wielu kolekcji, takich jak asocjacje jeden do wielu lub wiele do wielu, zakończy się w produkcie kartezjańskim . Jeśli jednak chcesz pobrać wiele kolekcji, możesz użyć zapytań pomocniczych dla drugiej, trzeciej, ..., n-tej kolekcji. Sprawdź ten artykuł o więcej szczegółów.
Vlad Mihalcea
5

Jeśli masz @oneToOneustawione mapowanie FetchType.LAZYi używasz drugiego zapytania (ponieważ chcesz, aby obiekty działu były ładowane jako część obiektów pracownika), to, co zrobi Hibernate, to wysyła zapytania w celu pobrania obiektów działu dla każdego pojedynczego obiektu pracownika, który pobiera z DB.

Później w kodzie można uzyskać dostęp do obiektów działu za pośrednictwem jednowartościowego powiązania pracownika z działem, a Hibernate nie wysyła żadnego zapytania w celu pobrania obiektu działu dla danego pracownika.

Pamiętaj, że Hibernate nadal wysyła zapytania równe liczbie pobranych pracowników. Hibernate wyśle ​​taką samą liczbę zapytań w obu powyższych zapytaniach, jeśli chcesz uzyskać dostęp do obiektów działu wszystkich obiektów pracownika

Bunti
źródło
2

Dherik: Nie jestem pewien, co mówisz, kiedy nie używasz funkcji pobierania, wynik będzie typu: List<Object[ ]>co oznacza listę tabel obiektów, a nie listę pracowników.

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

Kiedy używasz fetch, jest tylko jeden wybór, a wynikiem jest lista Pracownik List<Employee>zawierająca listę działów. Zastępuje leniwą deklarację jednostki.

Bilal BBB
źródło
Nie wiem, czy rozumiem twoje obawy. Jeśli nie używasz fetch, twoje zapytanie zwróci tylko pracowników. Jeśli działy, nawet w tym przypadku, są nadal zwracane, to dlatego, że mapowanie między pracownikiem a działem (a @OneToMany) jest ustawione za pomocą FetchType.EAGER. W takim przypadku każde fetchzapytanie HQL (z lub nie) z FROM Employeeprzyniesie wszystkie działy.
Dherik
Bez użycia funkcji pobierania (tylko termin dołączenia) wynikiem byłaby tablica kolekcji, dwa wiersze, pierwszy to zbiór pracowników, a drugi to zbiór działów. Używając szybkiego pobierania lub leniwego pobierania, zostaną pobrane działy.
Bilal BBB
Bez pobierania w HQL stanie się to tylko wtedy, gdy mapowanie między pracownikiem a działem to EAGER ( @OneToMany(fetch = FetchType.EAGER). Jeśli tak nie jest, Działy nie zostaną zwrócone.
Dherik
@Dherik spróbuj sam, otrzymasz wyjątek ClassCastException.
Bilal BBB
Rozumiem problem. To nie jest problem z pobieraniem, ale to, jak selectzostał wykonany w HQL. Spróbuj SELECT emp FROM Employee emp JOIN FETCH emp.department dep. JPA / Hibernate mają takie zachowanie, że zwraca wartość Listof, Object[]gdy pominiesz SELECTczęść.
Dherik