Jakich alternatywnych strategii używasz, aby uniknąć LazyLoadExceptions?
Rozumiem, że otwarta sesja w widoku ma problemy z:
- Aplikacje warstwowe działające w różnych jvm
- Transakcje są zatwierdzane dopiero na końcu i najprawdopodobniej chciałbyś wcześniej uzyskać wyniki.
Ale jeśli wiesz, że Twoja aplikacja działa na jednej maszynie wirtualnej, dlaczego nie złagodzić bólu, stosując strategię otwartej sesji w widoku?
java
hibernate
jpa
lazy-loading
open-session-in-view
HeDinges
źródło
źródło
Odpowiedzi:
Ponieważ wysyłanie potencjalnie niezainicjowanych serwerów proxy, zwłaszcza kolekcji, w warstwie widoku i wyzwalanie z niej ładowania hibernacji może być kłopotliwe zarówno z punktu widzenia wydajności, jak i zrozumienia.
Zrozumienie :
Używanie OSIV „zanieczyszcza” warstwę widoku problemami związanymi z warstwą dostępu do danych.
Warstwa widoku nie jest przygotowana do obsługi,
HibernateException
co może się zdarzyć podczas leniwego ładowania, ale przypuszczalnie warstwa dostępu do danych jest.Wydajność :
OSIV ma tendencję do ciągnięcia prawidłowego ładowania bytów pod dywan - zwykle nie zauważasz, że twoje kolekcje lub jednostki są leniwie inicjalizowane (być może N + 1). Więcej wygody, mniej kontroli.
Aktualizacja: zobacz Antypattern OpenSessionInView, aby uzyskać szerszą dyskusję na ten temat. Autor wymienia trzy ważne punkty:
źródło
Dłuższy opis można znaleźć w moim artykule Anty-Pattern Open Session In View . W przeciwnym razie, oto podsumowanie, dlaczego nie należy używać otwartej sesji w widoku.
Otwarta sesja w widoku ma złe podejście do pobierania danych. Zamiast pozwalać warstwie biznesowej decydować, jak najlepiej pobrać wszystkie skojarzenia, które są wymagane przez warstwę widoku, wymusza ona pozostawienie otwartego kontekstu trwałości, aby warstwa widoku mogła wyzwolić inicjalizację serwera proxy.
OpenSessionInViewFilter
WywołujeopenSession
metodę bazowegoSessionFactory
i pozyskuje noweSession
.Session
Jest zobowiązany doTransactionSynchronizationManager
.OpenSessionInViewFilter
NazywadoFilter
sięjavax.servlet.FilterChain
odwołania do obiektu, a wniosek jest dalej przetwarzanyDispatcherServlet
Nazywa i IT kieruje żądania HTTP do instrumentu bazowegoPostController
.PostController
wywołujePostService
uzyskać listęPost
podmiotów.PostService
Otwiera nową transakcję iHibernateTransactionManager
ponownie wykorzystuje ten samSession
, który został otwarty przezOpenSessionInViewFilter
.PostDAO
Pobiera listęPost
podmiotów bez inicjowania żadnej leniwe stowarzyszenie.PostService
transakcję bazową, aleSession
nie jest zamknięta, ponieważ została otwarta na zewnątrz.DispatcherServlet
rozpoczyna renderowania interfejsu użytkownika, który z kolei nawiguje leniwe skojarzenia i wyzwala ich inicjalizacji.OpenSessionInViewFilter
Można zamknąćSession
, a pod spodem połączenie z bazą danych jest zwolniony także.Na pierwszy rzut oka może to nie wyglądać na straszną rzecz, ale gdy spojrzysz na to z perspektywy bazy danych, seria błędów staje się bardziej oczywista.
Warstwa usług otwiera i zamyka transakcję bazy danych, ale później nie ma żadnej jawnej transakcji. Z tego powodu każda dodatkowa instrukcja wydana z fazy renderowania interfejsu użytkownika jest wykonywana w trybie automatycznego zatwierdzania. Automatyczne zatwierdzanie wywiera presję na serwer bazy danych, ponieważ każda instrukcja musi opróżnić dziennik transakcji na dysk, powodując w ten sposób duży ruch we / wy po stronie bazy danych. Jedną z optymalizacji byłoby oznaczenie
Connection
jako tylko do odczytu, co pozwoliłoby serwerowi bazy danych uniknąć zapisywania w dzienniku transakcji.Nie ma już rozdzielania obaw, ponieważ instrukcje są generowane zarówno przez warstwę usług, jak i przez proces renderowania interfejsu użytkownika. Pisanie testów integracyjnych, które potwierdzają liczbę generowanych instrukcji, wymaga przejścia przez wszystkie warstwy (sieć, usługa, DAO), podczas gdy aplikacja jest wdrożona w kontenerze internetowym. Nawet w przypadku korzystania z bazy danych w pamięci (np. HSQLDB) i lekkiego serwera WWW (np. Jetty), te testy integracyjne będą wykonywane wolniej niż gdyby warstwy były rozdzielone, a wewnętrzne testy integracji korzystały z bazy danych, podczas gdy testy integracji front-endu w ogóle kpili z warstwy usług.
Warstwa interfejsu użytkownika jest ograniczona do nawigacji po asocjacjach, które z kolei mogą powodować problemy z zapytaniami N + 1. Chociaż Hibernate oferuje
@BatchSize
pobieranie skojarzeń w partiach iFetchMode.SUBSELECT
poradzi sobie z tym scenariuszem, adnotacje wpływają na domyślny plan pobierania, więc są stosowane w każdym biznesowym przypadku użycia. Z tego powodu kwerenda warstwy dostępu do danych jest o wiele bardziej odpowiednia, ponieważ można ją dostosować do aktualnych wymagań dotyczących pobierania danych dla przypadków użycia.Wreszcie, połączenie z bazą danych może być utrzymywane przez całą fazę renderowania interfejsu użytkownika (w zależności od trybu zwalniania połączenia), co wydłuża czas dzierżawy połączenia i ogranicza ogólną przepustowość transakcji z powodu przeciążenia puli połączeń bazy danych. Im dłużej połączenie jest utrzymywane, tym więcej innych współbieżnych żądań będzie czekać na połączenie z puli.
Tak więc, albo połączenie jest wstrzymane zbyt długo, albo uzyskujesz / zwalniasz wiele połączeń dla jednego żądania HTTP, wywierając w ten sposób presję na bazową pulę połączeń i ograniczając skalowalność.
Spring Boot
Niestety, otwarta sesja w widoku jest domyślnie włączona w Spring Boot .
Dlatego upewnij się, że w
application.properties
pliku konfiguracyjnym znajduje się następujący wpis:Spowoduje to wyłączenie OSIV, abyś mógł sobie poradzić
LazyInitializationException
we właściwy sposób .źródło
transakcje mogą być zatwierdzane w warstwie usług - transakcje nie są związane z OSIV. To
Session
pozostaje otwarte, a nie transakcja - trwająca.jeśli twoje warstwy aplikacji są rozmieszczone na wielu komputerach, praktycznie nie możesz używać OSIV - musisz zainicjować wszystko, czego potrzebujesz, przed wysłaniem obiektu przez sieć.
OSIV to przyjemny i przejrzysty (tj. - żaden z twoich kodów nie jest świadomy tego, że tak się dzieje) sposób na wykorzystanie korzyści wydajnościowych wynikających z leniwego ładowania
źródło
Nie powiedziałbym, że otwarta sesja w widoku jest uważana za złą praktykę; co sprawia na tobie takie wrażenie?
Open-Session-In-View to proste podejście do obsługi sesji za pomocą Hibernate. Ponieważ jest prosty, czasami jest uproszczony. Jeśli potrzebujesz szczegółowej kontroli nad swoimi transakcjami, na przykład wielu transakcji w żądaniu, otwarta sesja w widoku nie zawsze jest dobrym rozwiązaniem.
Jak zauważyli inni, OSIV ma pewne kompromisy - jesteś znacznie bardziej podatny na problem N + 1, ponieważ mniej prawdopodobne jest, że zdasz sobie sprawę, jakie transakcje rozpoczynasz. Jednocześnie oznacza to, że nie musisz zmieniać warstwy usług, aby dostosować się do drobnych zmian w widoku.
źródło
Jeśli używasz kontenera Inversion of Control (IoC), takiego jak Spring, możesz poczytać o zakresie fasoli . Zasadniczo mówię Springowi, aby dał mi
Session
obiekt Hibernate, którego cykl życia obejmuje całe żądanie (tj. Jest tworzony i niszczony na początku i na końcu żądania HTTP). Nie muszę się martwić oLazyLoadException
s ani o zamknięcie sesji, ponieważ kontener IoC zarządza tym za mnie.Jak wspomniano, będziesz musiał pomyśleć o problemach z wydajnością N + 1 SELECT. Zawsze możesz później skonfigurować swoją jednostkę Hibernacji, aby wykonywała szybkie ładowanie sprzężenia w miejscach, w których wydajność jest problemem.
Rozwiązanie dotyczące zakresu fasoli nie jest specyficzne dla wiosny. Wiem, że PicoContainer oferuje te same możliwości i jestem pewien, że inne dojrzałe kontenery IoC oferują coś podobnego.
źródło
Z własnego doświadczenia wynika, że OSIV nie jest taki zły. Jedyne ustalenie jakie poczyniłem to użycie dwóch różnych transakcji: - pierwsza, otwarta w „warstwie usługowej”, gdzie mam „logikę biznesową” - druga otwarta tuż przed renderowaniem widoku
źródło
Właśnie napisałem post na temat wskazówek, kiedy używać otwartej sesji na moim blogu. Sprawdź, jeśli jesteś zainteresowany.
http://heapdump.wordpress.com/2010/04/04/should-i-use-open-session-in-view/
źródło
Jestem b. Zardzewiały na Hibernate ... ale myślę, że jest możliwe, aby mieć wiele transakcji w jednej sesji Hibernate. Zatem granice transakcji nie muszą być takie same, jak zdarzenia rozpoczęcia / zakończenia sesji.
OSIV, imo, jest przede wszystkim przydatne, ponieważ możemy uniknąć pisania kodu uruchamiającego „kontekst trwałości” (inaczej sesję) za każdym razem, gdy żądanie wymaga dostępu do bazy danych.
W warstwie usług prawdopodobnie będziesz musiał wywoływać metody, które mają inne potrzeby transakcyjne, takie jak „Wymagane, Nowe wymagane itp.”. Jedyną rzeczą, jakiej potrzebują te metody, jest to, że ktoś (np. Filtr OSIV) uruchomił kontekst trwałości, więc jedyną rzeczą, o którą muszą się martwić, jest - „hej, daj mi sesję hibernacji dla tego wątku… Muszę coś zrobić Rzeczy DB ”.
źródło
To nie pomoże zbyt wiele, ale możesz sprawdzić mój temat tutaj: * Hibernate Cache1 OutOfMemory with OpenSessionInView
Mam pewne problemy z OutOfMemory z powodu OpenSessionInView i wielu załadowanych jednostek, ponieważ pozostają w pamięci podręcznej Hibernate na poziomie 1 i nie są zbierane jako śmieci (ładuję wiele jednostek z 500 elementami na stronę, ale wszystkie jednostki pozostają w pamięci podręcznej)
źródło