opis problemu

Jak powiedzieliśmy powyżej, adnotacja LazyCollectionOption.EXTRA ma pewien problem - wykonuje osobne zapytanie do bazy danych dla każdego obiektu. Musimy jakoś wyjaśnić Hibernate, że chcemy, aby natychmiast ładował wszystkie obiekty potomne dla naszych obiektów nadrzędnych.

Twórcy Hibernate wymyślili rozwiązanie tego problemu, operator dołączania pobierania w HQL.

Przykład zapytania HQL:

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

W tym zapytaniu wszystko jest jednocześnie proste i złożone. Spróbujmy zbudować go kawałek po kawałku.

opcja 1

Chcemy pobrać wszystkie obiektyzadanie, posortowane według terminu. Oto jak wyglądałoby to żądanie:

select task from Task t order by t.deadline

Podczas gdy wszystko jest jasne. Ale polepracownikklasy Task będzie zawierał kolekcję Pracownicy opatrzoną adnotacją EXTRA . A obiekty tej kolekcji nie zostaną załadowane.

Opcja 2

Wymuś ładowanie przez Hibernate obiektów podrzędnych dla obiektuzadanie.

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

Z pomocą jawnie powiązujemy encje Task i Employee w naszym zapytaniu. Hibernate już to wie, ponieważ używamy adnotacji @ManyToMany w tych polach.

Ale potrzebujemy instrukcji join , aby uzupełnić ją instrukcją fetch , aby uzyskać polecenie join fetch . W ten sposób informujemy Hibernate, że obiekty w kolekcjach Task.employee muszą zostać załadowane z bazy danych, gdy nasze żądanie zostanie wykonane.

Opcja 3

Poprzednie rozwiązanie zawiera kilka błędów. Po pierwsze, po użyciu join, SQL nie zwróci nam obiektówzadanie, z którymi w tabeli Pracownik nie są skojarzone żadne obiekty. Dokładnie tak działa sprzężenie wewnętrzne .

Musimy więc rozszerzyć nasze łączenie za pomocą lewego operatora i przekształcić je w lewe łączenie . Przykład:

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

Opcja 4

Ale to nie wszystko. Jeśli w twoim kodzie relacja między jednostkami to wiele do maja, w wynikach zapytania pojawią się duplikaty. Ten sam obiektzadaniemożna znaleźć na różnych pracownikach (Obiekty pracowników).

Musisz więc dodać odrębne słowo kluczowe po wybranym słowie, aby pozbyć się zduplikowanego obiektu Task.

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

Tak w 4 krokach doszliśmy do wniosku od którego zaczęliśmy. Cóż, kod Java będzie wyglądał całkiem nieźle:

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

DOŁĄCZ DO POBIERANIA Ograniczenia

Nikt nie jest idealny. Instrukcja JOIN FETCH też. Ma sporo ograniczeń. Pierwszym z nich jest użycie metod setMaxResults() i setFirstResult() .

Dla instrukcji JOIN FETCH nasz Hibernate wygeneruje bardzo złożone zapytanie, w którym łączymy trzy tabele w jedną: pracownik, zadanie i pracownik_zadanie. W rzeczywistości nie jest to prośba o pracowników lub zadania, ale o wszystkie znane pary pracownik-zadanie.

A SQL może zastosować swoje instrukcje LIMIT i OFFSET do dokładnie tego zapytania par pracownik-zadanie. Jednocześnie z zapytania HQL jasno wynika, że ​​chcemy otrzymać dokładnie zadania (Task), a jeśli redystrybuujemy nasze parametry FirstResult i MaxResult, to powinny one odnosić się konkretnie do obiektów Task.

Jeśli napiszesz taki kod:

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

Wtedy Hibernate nie będzie w stanie poprawnie przekonwertować FirstResult i MaxResult na parametry OFFSET i LIMIT zapytania SQL.

Zamiast tego zrobi trzy rzeczy:

  • Zapytanie SQL wybierze ogólnie wszystkie dane z tabeli i zwróci je do Hibernate
  • Hibernate wybierze niezbędne rekordy w swojej pamięci i zwróci je Tobie
  • Hibernacja wyświetli ostrzeżenie

Ostrzeżenie będzie wyglądać mniej więcej tak:

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