Что это значит? Я не нашел объяснения этого поведения.
Оцените любую помощь.
Ответ 2
Анти-шаблон OSIV
Вместо того, чтобы позволить бизнес-уровню решить, как лучше всего выбрать все ассоциации, которые необходимы для уровня View, OSIV (Open Session in View) заставляет постоянный контекст оставаться открытым, чтобы уровень View мог инициировать инициализацию Proxy, как показано по следующей схеме.
![enter image description here]()
OpenSessionInViewFilter
вызывает метод openSession
базового SessionFactory
и получает новый Session
.
Session
связан с TransactionSynchronizationManager
.
OpenSessionInViewFilter
вызывает doFilter
ссылки на объект javax.servlet.FilterChain
, и запрос дополнительно обрабатывается
- Вызывается
DispatcherServlet
, и он направляет HTTP-запрос на базовый PostController
.
PostController
вызывает PostService
, чтобы получить список Post
сущностей.
PostService
открывает новую транзакцию, а HibernateTransactionManager
повторно использует тот же Session
, который был открыт OpenSessionInViewFilter
.
PostDAO
выбирает список сущностей Post
без инициализации ленивых ассоциаций.
PostService
фиксирует основную транзакцию, но Session
не закрыт, потому что он был открыт снаружи.
DispatcherServlet
начинает рендеринг пользовательского интерфейса, который, в свою очередь, перемещается по ленивым ассоциациям и запускает их инициализацию.
OpenSessionInViewFilter
может закрыть Session
, и исходное соединение с базой данных также освобождается.
На первый взгляд, это не выглядит ужасно, но, как только вы посмотрите на это с точки зрения базы данных, ряд недостатков станет более очевидным.
Сервисный уровень открывает и закрывает транзакцию базы данных, но после этого не происходит явной транзакции. По этой причине каждый дополнительный оператор, выдаваемый на этапе визуализации пользовательского интерфейса, выполняется в режиме автоматической фиксации. Автоматическая фиксация оказывает давление на сервер базы данных, поскольку каждый оператор должен сбрасывать журнал транзакций на диск, что вызывает большой объем трафика ввода-вывода на стороне базы данных. Одним из способов оптимизации было бы пометить Connection
только для чтения, что позволило бы серверу баз данных избежать записи в журнал транзакций.
Больше нет разделения интересов, потому что операторы генерируются как сервисным уровнем, так и процессом рендеринга пользовательского интерфейса. Для написания интеграционных тестов, в которых утверждается количество генерируемых операторов, необходимо пройти через все уровни (веб, сервис, DAO), пока приложение развертывается в веб-контейнере. Даже при использовании базы данных в памяти (например, HSQLDB) и облегченного веб-сервера (например, Jetty) эти интеграционные тесты будут выполняться медленнее, чем если бы слои были разделены, а внутренние интеграционные тесты использовали базу данных, в то время как Фронтальные интеграционные тесты вообще издевались над уровнем сервиса.
Уровень пользовательского интерфейса ограничен навигационными ассоциациями, которые, в свою очередь, могут вызвать проблемы с N + 1 запросами. Хотя Hibernate предлагает @BatchSize
для извлечения связей в пакетах и FetchMode.SUBSELECT
, чтобы справиться с этим сценарием, аннотации влияют на план выборки по умолчанию, поэтому они применяются к каждому бизнесу случай использования. По этой причине запрос уровня доступа к данным является гораздо более подходящим, поскольку он может быть адаптирован к текущим требованиям извлечения данных варианта использования.
Наконец, что не менее важно, соединение с базой данных поддерживается на протяжении всей фазы рендеринга пользовательского интерфейса, что увеличивает время аренды соединения и ограничивает общую пропускную способность транзакций из-за перегрузки в пуле соединений с базой данных. Чем дольше удерживается соединение, тем больше других параллельных запросов будет ожидать получения соединения из пула.
Spring Boot и OSIV
К сожалению, OSIV (Open Session in View) по умолчанию включен в Spring Boot, и OSIV действительно плохая идея с точки зрения производительности и масштабируемости.
Поэтому убедитесь, что в файле конфигурации application.properties
есть следующая запись:
spring.jpa.open-in-view=false
Это отключит OSIV, так что вы сможете правильно обрабатывать LazyInitializationException
.
Начиная с версии 2.0, Spring Boot выдает предупреждение, когда OSIV включен по умолчанию, поэтому вы можете обнаружить эту проблему задолго до того, как она затронет производственную систему.
Подробнее о OSIV читайте в этой статье.