Более подробное описание вы можете прочитать в моей статье Open Session In View Anti-Pattern . В противном случае, вот краткое изложение того, почему вы не должны использовать Open Session In View.
Open Session In View использует плохой подход к извлечению данных. Вместо того, чтобы позволить бизнес-уровню решать, как лучше выбрать все ассоциации, необходимые для слоя View, он заставляет контекст постоянства оставаться открытым, чтобы уровень View мог инициировать инициализацию Proxy.
-
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
, чтобы справиться с этим сценарием, аннотации влияют на план выборки по умолчанию, поэтому они применяются к каждому бизнесу вариант использования. По этой причине запрос уровня доступа к данным является гораздо более подходящим, поскольку он может быть адаптирован к текущим требованиям выборки данных варианта использования.
Наконец, что не менее важно, соединение с базой данных может поддерживаться на протяжении всей фазы рендеринга пользовательского интерфейса (в зависимости от режима освобождения соединения), что увеличивает время аренды соединения и ограничивает общую пропускную способность транзакции из-за перегрузки в пуле соединений с базой данных. Чем дольше удерживается соединение, тем больше других параллельных запросов будет ожидать получения соединения из пула.
Таким образом, либо вы удерживаете соединение слишком долго, либо вы получаете / освобождаете несколько соединений для одного HTTP-запроса, что оказывает давление на базовый пул соединений и ограничивает масштабируемость.
Spring Boot
К сожалению, Open Session in View включен по умолчанию в Spring Boot .
Итак, убедитесь, что в файле конфигурации application.properties
есть следующая запись:
spring.jpa.open-in-view=false
Это отключит OSIV, так что вы можете правильно обработать LazyInitializationException
.