Co dokładnie kontroluje strategia pobierania JPA? Nie potrafię dostrzec różnicy między chętnym a leniwym. W obu przypadkach JPA / Hibernate nie łączy automatycznie relacji „wiele do jednego”.
Przykład: osoba ma jeden adres. Adres może należeć do wielu osób. Klasy jednostek z adnotacjami JPA wyglądają następująco:
@Entity
public class Person {
@Id
public Integer id;
public String name;
@ManyToOne(fetch=FetchType.LAZY or EAGER)
public Address address;
}
@Entity
public class Address {
@Id
public Integer id;
public String name;
}
Jeśli używam zapytania JPA:
select p from Person p where ...
JPA / Hibernate generuje jedno zapytanie SQL do wyboru z tabeli Person, a następnie odrębne zapytanie adresowe dla każdej osoby:
select ... from Person where ...
select ... from Address where id=1
select ... from Address where id=2
select ... from Address where id=3
Jest to bardzo złe w przypadku dużych zestawów wyników. Jeśli jest 1000 osób, generuje 1001 zapytań (1 od osoby i 1000 niezależnie od adresu). Wiem o tym, ponieważ patrzę na dziennik zapytań MySQL. Zrozumiałem, że ustawienie typu pobierania adresu na chętny spowoduje, że JPA / Hibernate automatycznie zapyta o sprzężenie. Jednak niezależnie od typu pobierania nadal generuje odrębne zapytania dotyczące relacji.
Dopiero gdy wyraźnie powiem mu, aby dołączył, faktycznie dołączy:
select p, a from Person p left join p.address a where ...
Czy coś mi umyka? Teraz muszę ręcznie zakodować każde zapytanie, aby zostało dołączone do relacji „wiele do jednego”. Używam implementacji JPA Hibernate z MySQL.
Edycja: Wygląda na to, że (patrz FAQ Hibernacji tutaj i tutaj ) FetchType
nie wpływa na zapytania JPA. Więc w moim przypadku wyraźnie nakazałem mu dołączyć.
Odpowiedzi:
JPA nie dostarcza żadnych specyfikacji dotyczących mapowania adnotacji w celu wybrania strategii pobierania. Ogólnie rzecz biorąc, powiązane podmioty można pobrać na jeden z poniższych sposobów
A więc są dwie skrajności
SELECT
iJOIN
mieszcząSUBSELECT
się pomiędzy. Można wybrać odpowiednią strategię w oparciu o model swojej domeny.Domyślnie
SELECT
jest używany przez JPA / EclipseLink i Hibernate. Można to zmienić, używając:w Hibernate. Pozwala również na
SELECT
jawne ustawienie trybu za pomocą@Fetch(FetchMode.SELECT)
którego można dostroić używając rozmiaru partii np@BatchSize(size=10)
.Odpowiednie adnotacje w EclipseLink to:
źródło
„mxc” ma rację.
fetchType
po prostu określa, kiedy relacja powinna zostać rozwiązana.Aby zoptymalizować przyspieszone ładowanie za pomocą połączenia zewnętrznego, musisz dodać
na swoim polu. To jest adnotacja dotycząca hibernacji.
źródło
Atrybut fetchType określa, czy pole z adnotacjami jest pobierane natychmiast po pobraniu jednostki podstawowej. Niekoniecznie decyduje o tym, jak skonstruowana jest instrukcja pobierania, rzeczywista implementacja sql zależy od dostawcy, z którego korzystasz, linku górnego / hibernacji itp.
Jeśli ustawisz
fetchType=EAGER
Oznacza to, że pole z adnotacjami jest wypełniane swoimi wartościami w tym samym czasie, co inne pola w encji. Więc jeśli otworzysz uprawnionego menedżera, pobierz obiekty swojej osoby, a następnie zamkniesz uprawnionego, następnie wykonanie polecenia person.address nie spowoduje wyrzucenia leniwego obciążenia.Jeśli ustawisz,
fetchType=LAZY
pole jest wypełniane tylko wtedy, gdy jest dostępne. Jeśli wcześniej zamknąłeś uprawnionego menedżera, wyjątek leniwego ładowania zostanie wyrzucony, jeśli wykonasz polecenie person.address. Aby załadować pole, musisz umieścić jednostkę z powrotem w kontekście uprawniających za pomocą em.merge (), a następnie uzyskać dostęp do pola, a następnie zamknąć menadżera uprawnień.Możesz chcieć leniwe ładowanie podczas konstruowania klasy klienta z kolekcją dla zamówień klientów. Jeśli pobierałeś każde zamówienie dla klienta, gdy chciałeś uzyskać listę klientów, może to być kosztowna operacja na bazie danych, gdy szukasz tylko nazwy klienta i danych kontaktowych. Najlepiej zostawić dostęp do bazy danych na później.
Druga część pytania - jak przejść w stan hibernacji, aby wygenerować zoptymalizowany SQL?
Hibernate powinno pozwolić ci na udzielenie wskazówek, jak skonstruować najbardziej wydajne zapytanie, ale podejrzewam, że coś jest nie tak z twoją tabelą. Czy związek jest ustalony w tabelach? Hibernate mógł zdecydować, że proste zapytanie będzie szybsze niż łączenie, zwłaszcza jeśli brakuje indeksów itp.
źródło
Spróbuj z:
U mnie działa podobnie z JPA2 / EclipseLink, ale wygląda na to, że ta funkcja jest również obecna w JPA1 :
źródło
Jeśli używasz EclipseLink zamiast Hibernacji, możesz zoptymalizować swoje zapytania za pomocą „wskazówek dotyczących zapytań”. Zobacz ten artykuł z Eclipse Wiki: EclipseLink / Examples / JPA / QueryOptimization .
Jest rozdział na temat „Połączonego czytania”.
źródło
aby dołączyć, możesz zrobić wiele rzeczy (używając eclipselink)
w jpql możesz zrobić lewe dołączenie pobierania
w nazwanym zapytaniu można określić wskazówkę dotyczącą zapytania
w TypedQuery możesz powiedzieć coś w rodzaju
query.setHint("eclipselink.join-fetch", "e.projects.milestones");
jest też wskazówka dotycząca pobierania zbiorczego
query.setHint("eclipselink.batch", "e.address");
widzieć
http://java-persistence-performance.blogspot.com/2010/08/batch-fetching-optimizing-object-graph.html
źródło
Miałem dokładnie ten problem z wyjątkiem tego, że klasa Person miała osadzoną klasę klucza. Moim rozwiązaniem było dołączenie ich do zapytania ORAZ usunięcie
@Fetch(FetchMode.JOIN)
Moja osadzona klasa identyfikatora:
źródło
Przyszły mi na myśl dwie rzeczy.
Po pierwsze, czy na pewno masz na myśli ManyToOne jako adres? Oznacza to, że wiele osób będzie miało ten sam adres. Jeśli jest edytowany dla jednego z nich, zostanie zmieniony dla wszystkich. Czy taki jest twój zamiar? 99% adresów jest „prywatnych” (w tym sensie, że należą tylko do jednej osoby).
Po drugie, czy masz inne chętne relacje z bytem Person? Jeśli dobrze pamiętam, Hibernate może obsłużyć tylko jedną gorliwą relację na jednostce, ale jest to prawdopodobnie nieaktualna informacja.
Mówię to, ponieważ twoje zrozumienie, jak to powinno działać, jest zasadniczo poprawne z miejsca, w którym siedzę.
źródło