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?

0
Alex Mawashi 20 marzec 2020, 15:23

2 odpowiedzi

Najlepsza odpowiedź

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

  1. Wyjaśnij Hibernate, w jaki sposób jednostki są powiązane
  2. 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.

4
v.ladynev 20 marzec 2020, 16:27

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.

3
Andronicus 20 marzec 2020, 12:42