Dowiedziałem się, że Hibernate umożliwia odpytywanie elementów podrzędnych tylko wtedy, gdy jest to konieczne, dzięki zastosowaniu Lazy Loading.
Więc jeśli mamy:
@Entity
public class Payment {
...
@ManyToOne(fetch = FetchType.LAZY)
private Debtor debtor;
}
Spodziewam się, że kiedy pobieram płatność z bazy danych Hibernate, ustawia symbol zastępczy w atrybucie dłużnika i pobiera dłużnika tylko wtedy, gdy jest to ściśle wymagane .
Jeśli więc użyję metody pobierającej w celu uzyskania dłużnika z mojego obiektu płatności:
Debtor debtor = payment.getDebtor();
Oczekuję blokad wątku, dopóki Hibernate nie wykona zapytania SELECT i nie zwróci obiektu Debtor.
Dlaczego więc, do cholery, zawsze dostaję wyjątek HibernateLazyLoading , który zobowiązuje mnie do napisania niestandardowego zapytania pobierania w PaymentRepository, spowalniając moje początkowe zapytanie JAK bym użył EAGER FetchType ?
Dlaczego więc to FetchType.LAZY
istnieje, jeśli nie działa zgodnie z oczekiwaniami?
2 odpowiedzi
Chciałbym podać odpowiedź @Andronicus, ponieważ odpowiedź nie jest dokładna.
LazyInitializationException
Nie jest ściśle powiązany z @Transactional
, transakcjami lub otwartymi / zamkniętymi połączeniami. Zachowanie jest dość proste (poniżej znajduje się pseudo kod)
Bez LazyInitializationException
Context context = Hibernate.openPersistentContext();
Payment payment = context.getById(1L, Payment.class);
Debtor debtor = payment.getDebtor();
Hibernate.closePersistentContext();
Z LazyInitializationException
Context context = Hibernate.openPersistentContext();
Payment payment = context.getById(1L, Payment.class);
Hibernate.closePersistentContext();
Debtor debtor = payment.getDebtor();
Pytania
Dlaczego więc do cholery zawsze otrzymuję wyjątek HibernateLazyLoading, który zobowiązuje mnie do napisania niestandardowego zapytania pobierania w PaymentRepository, spowalniając moje początkowe zapytanie, ponieważ użyłbym EAGER FetchType?
Ponieważ Hibernate.closePersistentContext()
wydarzyło się gdzieś wcześniej.
Dlaczego więc ten FetchType.LAZY istnieje, jeśli nie działa zgodnie z oczekiwaniami?
Ponieważ nie zawsze potrzebujemy całej sieci grafu encji. Możemy użyć JPQL (HQL), kryteriów i prognoz do załadowania części jednostki. Musimy wyjaśnić Hibernate, w jaki sposób jednostki są powiązane, więc musimy dodać skojarzenia, takie jak @ManyToOne
.
Jest tu mały problem: mapowanie służy dwóm celom
- Wyjaśnij Hibernate, w jaki sposób jednostki są powiązane
- Zapisz / załaduj jednostki
Więc najprostszym sposobem odłączenia ładowania od mapowania obiektów jest FetchType.LAZY
.
Prosta zasada
Zawsze używaj FetchType.LAZY
wszędzie i pobieraj niezbędne części wykresu encji w miejscu, w którym jest to potrzebne.
Dlaczego więc, do cholery, zawsze dostaję wyjątek HibernateLazyLoading, który zobowiązuje mnie do napisania niestandardowego zapytania pobierania w PaymentRepository, spowalniając moje początkowe zapytanie, ponieważ użyłbym EAGER FetchType?
To dlatego, że to zdanie nie jest do końca prawdziwe:
Spodziewam się, że kiedy pobieram płatność z bazy danych Hibernate, ustawia symbol zastępczy w atrybucie dłużnika i pobiera dłużnika tylko wtedy, gdy jest to ściśle wymagane.
Debtor
rzeczywiście zostanie pobrany, jeśli zostanie uzyskany, ale tylko wtedy, gdy można go pobrać. Jeśli uruchomisz go w transakcji (na przykład przez adnotację metody z @Transactional
), błąd nie wystąpi, ponieważ połączenie z bazą danych nie jest zwracane do puli / zamknięte.
W Twoim przypadku dane są pobierane, Debtor
jest pakowany w proxy i połączenie zostaje utracone. Jeśli spróbujesz uzyskać do niego dostęp, wyrzuca się LazyInitializationException
.
P.S .: Nie zaleca się korzystania z transakcji w celu uniknięcia LazyInitializationException
z powodu problemów z wydajnością, które mogą wystąpić. Jeśli pobierzesz rodzica, a następnie wykonasz iterację po wielu (powiedzmy N) leniwie pobieranych dzieciach, zostanie wystrzelonych N + 1 zapytań do bazy danych.
Podobne pytania
Nowe pytania
java
Java to język programowania wysokiego poziomu. Użyj tego tagu, jeśli masz problemy z używaniem lub zrozumieniem samego języka. Ten tag jest rzadko używany samodzielnie i jest najczęściej używany w połączeniu z [spring], [spring-boot], [jakarta-ee], [android], [javafx], [hadoop], [gradle] i [maven].