Антипаттерн OSIV
Вместо того, чтобы позволить бизнес-уровню решать, как лучше всего получить все ассоциации, необходимые для уровня представления, OSIV (открытый сеанс в представлении) заставляет контекст сохранения оставаться открытым, чтобы уровень представления мог запускать инициализацию прокси, как показано. по следующей диаграмме.
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
для того, чтобы справиться с этим сценарием, аннотации влияют на план выборки по умолчанию, поэтому они применяются к каждому бизнес-варианту использования. По этой причине запрос уровня доступа к данным является гораздо более подходящим, поскольку он может быть адаптирован к требованиям текущего варианта использования к выборке данных.
И последнее, но не менее важное: соединение с базой данных поддерживается на всем этапе рендеринга пользовательского интерфейса, что увеличивает время аренды соединения и ограничивает общую пропускную способность транзакций из-за перегрузки в пуле соединений с базой данных. Чем дольше удерживается соединение, тем больше других одновременных запросов будут ждать, чтобы получить соединение из пула.
Весенняя загрузка и 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, прочтите эту статью .