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łuje openSession
metodę bazowego SessionFactory
i pozyskuje nowe Session
.
Session
Jest zobowiązany do TransactionSynchronizationManager
.
OpenSessionInViewFilter
Nazywa doFilter
się javax.servlet.FilterChain
odwołania do obiektu, a wniosek jest dalej przetwarzany
DispatcherServlet
Nazywa i IT kieruje żądania HTTP do instrumentu bazowego PostController
.
- W
PostController
wywołuje PostService
uzyskać listę Post
podmiotów.
PostService
Otwiera nową transakcję i HibernateTransactionManager
ponownie wykorzystuje ten sam Session
, który został otwarty przez OpenSessionInViewFilter
.
PostDAO
Pobiera listę Post
podmiotów bez inicjowania żadnej leniwe stowarzyszenie.
- Zatwierdza
PostService
transakcję bazową, ale Session
nie jest zamknięta, ponieważ została otwarta na zewnątrz.
- Do
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 i FetchMode.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:
spring.jpa.open-in-view=false
Spowoduje to wyłączenie OSIV, abyś mógł sobie poradzić LazyInitializationException
we właściwy sposób .