Hibernate Open Session в представлении: транзакция за запрос?

Я использую Hibernate с Spring на Tomcat. Я читал и перечитывал часто указывая на страницу Wiki JBoss по этой теме, и это было полезно. Но это оставляет мне некоторые вопросы.

  • Идея начать транзакцию для каждого запроса беспокоит меня. Думаю, я мог бы ограничить фильтр определенными контроллерами - возможно, поставить все мои контроллеры, которым нужна транзакция под псевдо-tx-маршрутом или что-то в этом роде. Но разве это не плохая идея использовать транзакции, если вы не знаете, если вам это понадобится? И если я просто читаю в каком-то запросе - читает, что, скорее всего, может произойти из кеша - разве мне не лучше без транзакции?

  • Я читал сообщения о том, как они обрабатывали транзакции на уровне службы, и я хотел бы сделать это с помощью Spring. Но тогда как выглядит код фильтра? Я все еще хочу, чтобы сеанс был доступен для моего просмотра для некоторой ленивой загрузки.

  • Если все, что мне нужно сделать, это вызвать sessionFactory.getCurrentSession() в моем фильтре, как он "освободится" обратно на сеанс factory для повторного использования? (Я ожидал увидеть session.close() или что-то еще, даже при использовании транзакций.) Кто говорит сеансу factory, что этот сеанс можно повторно использовать?

  • Возможно, это вызов beginTransaction(), который связывает данное соединение с базой данных с данным сеансом на время запроса? В противном случае сеанс вытягивает соединения db из пула по мере необходимости, правильно?

Спасибо за ваше терпение со всеми моими вопросами.

(И если ваш ответ будет ссылкой на документацию Spring, вы просто заставите меня плакать. Вы этого не хотите, не так ли? Я заплачу реальные деньги, если люди перестанут отвечать Spring -связанные вопросы таким образом.)

Ответ 1

Ваши проблемы действительны, решение, представленное на странице wiki, слишком упрощено. Транзакция не должна управляться на веб-уровне - ее следует обрабатывать на уровне обслуживания.

Правильная реализация откроет сеанс и привяжет его к потоку в фильтре. Никакая транзакция не запущена. Сессия помещается в режим скрытого режима - режим только для чтения. Сервисный вызов установил сессию в режим автосохранения и начал/совершил транзакцию. Как только метод обслуживания заканчивается, сеанс флеш-режима возвращается обратно к никогда.

Существует также возможность не открывать сеанс в фильтре. Каждый вызов уровня обслуживания открывает отдельный сеанс и транзакцию - после завершения вызова службы сеанс не закрывается, а регистрируется для отложенного закрытия. Сессия будет закрыта после завершения обработки веб-запроса.

Spring предоставляет OpensessionInViewFilter, который работает, как описано выше. Поэтому проигнорируйте статью wiki jboss и просто настройте OpensessionInViewFilter - все будет хорошо.

SessionFactory.getCurrentSession() - внутренне создает и назначает сеанс локальному потоку. Каждый запрос/поток будет иметь свой собственный сеанс. По завершении обработки веб-запроса сеанс будет закрыт. Из вашего кода вам просто нужно использовать SessionFactory.getCurrentSession() и не нужно его закрывать. Образец кода на странице wiki jboss неверен - в блоке finally должен быть SessionFactory.getCurrentSession(). Close(). Или они могут использовать транзакцию JTA и настроить спящий режим для открытия/закрытия сеанса в сочетании с транзакцией JTA.

Ответ 2

Не проблема, если фильтр создает сеанс для каждого запроса, потому что сеансы идут из пула сеансов, и они будут повторно использоваться. С точки зрения ОС ничего не происходит.

Спящий сеанс - это, по сути, подключение tcp (или socket/pipe) к серверу базы данных. Стоимость создания db-контакта очень зависит от типа sql (postgresql в этом плохой, хотя это очень хорошо в каждом случае). Но это ничего не значит, потому что hibernate повторно использует соединения с базой данных.

Простое решение фильтра спящего режима запускает новую транзакцию в сеансе для каждого запроса. Это транзакция с точки зрения SQL: это запрос "BEGIN" и "COMMIT". Это всегда дорого, и это должно быть сокращено.

IMHO возможным решением было, если транзакции были начаты только при первом запросе текущего запроса. Возможно, spring имеет что-то полезное для этого.