Почему бы не использовать Spring OpenEntityManagerInViewFilter
В то время как много сообщений было написано на тему Spring OpenSession/EntityManagerInViewFilter, я не мог найти ничего, что упоминает его недостатки. Из того, что я понимаю, и принимая типичную многоуровневую архитектуру веб-приложений, используя сервисный уровень @Transactional, фильтр работает следующим образом:
- Фильтр перехватывает запрос сервлета
- Фильтр открывает EntityManager и связывает его с текущим потоком
- Веб-контроллер называется
- Веб-контроллер вызывает службу
- Перехватчик транзакций начинает новую транзакцию, извлекает связанный с потоком EntityManager и связывает его с транзакцией
- Вызывается служба, выполняет некоторые действия с EntityManager, а затем возвращает
- Перехватчик транзакций сбрасывает EntityManager, затем совершает транзакцию
- Веб-контроллер готовит представление, а затем возвращает
- Вид построен
- Фильтр закрывает EntityManager и отвязывает его от текущего потока
На шагах 8 и 9 все объекты, загруженные потоком EntityManager, все еще управляются. Следовательно, если на этих этапах затрагиваются ленивые ассоциации, они будут загружены из базы данных с использованием еще открытого EntityManager. Насколько я понимаю, каждый такой доступ требует, чтобы база данных открывала транзакцию. Spring управление транзакциями не будет знать об этом, поэтому я называю это "неявной транзакцией".
Я вижу две проблемы с этим:
- Загрузка нескольких ленивых ассоциаций приведет к нескольким транзакциям с базами данных, возможному удару по производительности
- Корневой объект и его ленивые ассоциации загружаются в разные транзакции базы данных, поэтому данные могут быть устаревшими (например, root загружен нитью 1, корневыми ассоциациями, обновленными потоком 2, корневыми ассоциациями, загруженными потоком 1)
С одной стороны, эти 2 проблемы кажутся достаточными, чтобы отклонить использование этого фильтра (поражение производительности, несогласованность данных). С другой стороны, это решение очень удобно, избегает написания нескольких строк кода, проблема 1 может быть не такой заметной, а проблема 2 может быть чистой паранойей.
Как вы думаете?
Спасибо!
Ответы
Ответ 1
Как вы сказали, фильтр OpenSessionInView очень удобен в веб-приложениях. Что касается упомянутых вами ограничений:
1) Загрузка нескольких ленивых ассоциаций приведет к нескольким транзакциям с базами данных, что может повлиять на производительность.
Да, переход к БД часто может привести к проблемам с производительностью. В идеале вы хотите получить все данные, которые вам нужны в одной поездке. Подумайте об использовании Hibernate join-fetch для этого. Но получение слишком большого количества данных из БД также будет медленным. Эмпирическое правило, которое я использую, заключается в том, чтобы использовать извлечение соединения, если данные необходимы каждый раз, когда я рисую представление; если данные не нужны в большинстве случаев, я позволяю Hibernate ленивым извлекать его, когда мне это нужно, - тогда открывается секция openlocal open.
2) Корневой объект и его ленивые ассоциации загружаются в разные транзакции базы данных, поэтому данные могут быть устаревшими (например, root загружен потоком 1, корневыми ассоциациями, обновленными потоком 2, корневыми ассоциациями, загруженными потоком 1).
Представьте, что вы пишете это приложение в JDBC - если требования к согласованности приложений требуют, чтобы корневой каталог и оба они были загружены в один и тот же txn, используйте команду join. Если нет, то это часто случается, ленивая выборка не приведет к проблемам с согласованностью.
IMHO, тем более важным недостатком OpenSessionInView является то, что вы хотите, чтобы ваш уровень обслуживания повторно использовался в не-веб-контексте. Из вашего описания у вас, похоже, нет этой проблемы.
Ответ 2
Основной аргумент, который я слышал против OpenSessionInView и ленивая загрузка, - это избыток транзакций и негативное влияние на производительность. Это чрезвычайно удобно использовать в приложении с низкими требованиями к использованию, но в высокопроизводительном приложении я бы рекомендовал использовать старомодные полностью заполненные DTO (объекты передачи данных).
Ответ 3
Одной из основных проблем, с которыми я сталкиваюсь с OpenSessionInViewFilter, является использование приложений AJAX и javascript. Если вы используете javascript для рендеринга сеток или некоторых компонентов пользовательского интерфейса; есть ленивая нагрузка (если вы включите фильтр); и возникает исключение. В вашем представлении пользовательского интерфейса отображается бросок. Данные могут никогда не отображаться, страница начинает бросать странные исключения javascript, для которых вам нужно написать дополнительный код js для обработки. И вы подвергаете исключениям db пользователям (не очень хорошая идея).
В обычном приложении эти исключения могут быть захвачены и может быть выбрано допустимое исключение пользователя.
Ответ 4
Если ваше приложение представляет собой многоуровневую архитектуру (уровень представления, развернутый на разных JVM и уровне обслуживания, будет развернут на разных виртуальных машинах), то сохранение сеанса в открытом состоянии не имеет смысла. Я бы не использовал OpenSessionViewFilter, если ваш уровень сервиса не зависит от вашего уровня приложения.