И какие альтернативные стратегии вы используете, чтобы избежать LazyLoadExceptions?
Я понимаю, что в рассматриваемом открытом сеансе есть проблемы с:
- Многоуровневые приложения, работающие на разных jvm
- Транзакции совершаются только в конце, и, скорее всего, результат вам нужен раньше.
Но, если вы знаете, что ваше приложение работает на одной виртуальной машине, почему бы не облегчить вашу боль, используя стратегию открытого сеанса в представлении?
java
hibernate
jpa
lazy-loading
open-session-in-view
HeDinges
источник
источник
Ответы:
Потому что отправка возможно неинициализированных прокси, особенно коллекций, на уровне представления и запуск загрузки спящего режима оттуда может вызвать проблемы как с точки зрения производительности, так и с точки зрения понимания.
Понимание :
Использование OSIV «загрязняет» уровень представления проблемами, связанными с уровнем доступа к данным.
Уровень представления не готов к обработке,
HibernateException
которая может произойти при ленивой загрузке, но, предположительно, уровень доступа к данным готов.Производительность :
OSIV имеет тенденцию затягивать правильную загрузку сущностей под ковер - вы обычно не замечаете, что ваши коллекции или сущности лениво инициализируются (возможно, N + 1). Больше удобства, меньше контроля.
Обновление: см . Антипаттерн OpenSessionInView для более подробного обсуждения этого вопроса. Автор перечисляет три важных момента:
источник
Для более подробного описания вы можете прочитать мою статью Open Session In View Anti-Pattern . В противном случае, вот краткое изложение того, почему вам не следует использовать Open Session In View.
Open Session In View использует неправильный подход к извлечению данных. Вместо того, чтобы позволить бизнес-уровню решать, как лучше всего получить все ассоциации, необходимые для уровня представления, он заставляет контекст сохранения оставаться открытым, чтобы уровень представления мог запускать инициализацию прокси.
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 .
Итак, убедитесь, что в
application.properties
файле конфигурации у вас есть следующая запись:Это отключит OSIV, чтобы вы могли справиться
LazyInitializationException
правильно .источник
транзакции могут быть зафиксированы на уровне обслуживания - транзакции не связаны с OSIV. Это то,
Session
что остается открытым, а не выполняющаяся транзакция.если уровни вашего приложения распределены по нескольким машинам, вы практически не можете использовать OSIV - вам нужно инициализировать все, что вам нужно, прежде чем отправлять объект по сети.
OSIV - приятный и прозрачный (т.е. никто из вашего кода не знает, что это происходит) способ использовать преимущества производительности от ленивой загрузки.
источник
Я бы не сказал, что Open Session In View считается плохой практикой; что производит у вас такое впечатление?
Open-Session-In-View - это простой подход к обработке сеансов с помощью Hibernate. Потому что это просто, а иногда и упрощенно. Если вам нужен детальный контроль над вашими транзакциями, например наличие нескольких транзакций в запросе, Open-Session-In-View не всегда является хорошим подходом.
Как отмечали другие, у OSIV есть некоторые компромиссы - вы гораздо более подвержены проблеме N + 1, потому что у вас меньше шансов понять, какие транзакции вы запускаете. В то же время это означает, что вам не нужно изменять уровень обслуживания, чтобы адаптироваться к незначительным изменениям в вашем представлении.
источник
Если вы используете контейнер Inversion of Control (IoC), такой как Spring, вы можете прочитать об области видимости bean-компонентов . По сути, я говорю Spring предоставить мне
Session
объект Hibernate , жизненный цикл которого охватывает весь запрос (т.е. он создается и уничтожается в начале и в конце HTTP-запроса). Мне не нужно беспокоитьсяLazyLoadException
ни о s, ни о закрытии сеанса, поскольку контейнер IoC управляет этим за меня.Как уже упоминалось, вам придется подумать о проблемах с производительностью N + 1 SELECT. Вы всегда можете настроить свой объект Hibernate после этого, чтобы выполнять загрузку активного соединения в местах, где производительность является проблемой.
Решение области видимости bean-компонентов не относится к Spring. Я знаю, что PicoContainer предлагает те же возможности, и уверен, что другие зрелые контейнеры IoC предлагают нечто подобное.
источник
По моему собственному опыту, OSIV не так уж и плох. Единственное, что я сделал, это использование двух разных транзакций: - первая, открытая на «уровне обслуживания», где у меня есть «бизнес-логика» - вторая открыта непосредственно перед рендерингом представления.
источник
Я только что написал сообщение о некоторых рекомендациях относительно того, когда использовать открытый сеанс в своем блоге. Проверьте это, если вам интересно.
http://heapdump.wordpress.com/2010/04/04/should-i-use-open-session-in-view/
источник
Я против. Ржавый на Hibernate ... но я думаю, что возможно иметь несколько транзакций в одном сеансе Hibernate. Таким образом, границы ваших транзакций не должны совпадать с событиями начала / остановки сеанса.
OSIV, imo, в первую очередь полезен, потому что мы можем избежать написания кода для запуска «контекста постоянства» (также известного как сеанс) каждый раз, когда запрос должен сделать доступ к БД.
На уровне обслуживания вам, вероятно, потребуется выполнять вызовы методов, которые имеют разные потребности транзакций, такие как «Требуется», «Новый требуется» и т. Д. Единственное, что нужно этим методам, - это чтобы кто-то (например, фильтр OSIV) запустил контекст постоянства, так что единственное, о чем они должны беспокоиться, это - «Эй, дайте мне сеанс гибернации для этого потока .. Мне нужно сделать кое-что» БД вещи ".
источник
Это не слишком поможет, но вы можете проверить мою тему здесь: * Hibernate Cache1 OutOfMemory с OpenSessionInView
У меня есть некоторые проблемы с OutOfMemory из-за OpenSessionInView и большого количества загруженных объектов, потому что они остаются на уровне кеша Hibernate1 и не собираются сборщиком мусора (я загружаю много объектов с 500 элементами на страницу, но все объекты остаются в кеше)
источник