LINUX.ORG.RU

hibernate, правильное открытие сессии

 , , ,


0

1

Приветсвую, $USER. Начал тут знакомство с Hibernate, не могу никак разобраться, как правильно работать с сессиями.
Есть некоторое кол-во DAO классов, для управления сущностями ДБ, реализующие один интерфейс. Пример(source). Instance такого DAO создается один раз (при первом обращении), и живет при всем жизненном цикле приложения (на сервлетах). И все бы было бы хорошо, если бы это работало корректно, во всех случаях.
В ситуации, как в исходнике, когда на каждый DAO выделяется своя ссессия, возникает исключительная ситуация при update нескольких связанных объектов:

Illegal attempt to associate a collection with two open sessions


Если же для каждого обращения открывать свою сессию, тогда возникают проблемы с LAZY, коих у меня на самом деле больше, чем EAGER, т.к. в структуре много связей.
Если же создать общую сессию для всех DAO, и обращаться к ней через getCurrentSession(), но тогда каждый commit() закрывает сессию.

Еще пробовал ( чисто just for funs, дабы точно исключить эту глупую возможность) еще и общую транзакцию, с вызовом flush() после каждого обращения к бд, но тут проблема со вложенными транзакциями случается. Почему, не очень понятно.

nested transactions not supported

Больше идей нет. Что же я делаю не так? Где компромиссный вариант?

★★★★

Начни с самого простого - один запрос, одна сессия. 1. SessionFactory должен быть синглтоном. 2. В сервлетном фильтре делаешь сессию и транзакцию. 3. Проставь пропертю «hibernate.current_session_context_class», «thread»

dizza ★★★★★ ()

используй JTA

anonymous ()
Ответ на: комментарий от dizza

SessionFactory должен быть синглтоном

done

В сервлетном фильтре делаешь сессию и транзакцию.

Хм. Т.е. одна сессия и транзакция для каждого сервлета? А как тогда работать с этой сессией из сервлета? Передавать его в каком нибудь request?

Проставь пропертю «hibernate.current_session_context_class», «thread»

done

comp00 ★★★★ ()
Ответ на: комментарий от comp00

Да, я тупанул, JTA - не то. Я подразумевал декларативные транзакции, вроде спринговой @Transactional, Java же может быть менее многословной если использовать магию.

ИМХО: делать lazy-fetch за пределами DAO - это плохо (не менее чем повсеместное ручное открытие/закрытие транзакций). Хотя есть практики, когда сессия открывается при начале обработки запроса, и закрывается при его завершении.

Как по мне, тащить транзакцию через весь запрос - чревато внезапными лочками базы из-за подвисшего запроса.

Если же это нужно, и нет желания влазить в спринги и прочую EE, можно сделать SessionHolder с getCurrentSession() и ThreadLocal переменной. Но тогда желателен фильтр SessionHolderCleaner, который будет по завершении работы сервлета прибивать Session если она была открыта.

anonymous ()
Ответ на: комментарий от anonymous

Я реализовал фильтр, как посоветовал dizza и открываю-закрываю сессии при обращении к сервлетам. Вроде, пока работает

делать lazy-fetch за пределами DAO - это плохо

А как иначе? В DAO дублировать методы, для загрузки lazy полей, и после, в зависимости от нужды в lazy обращаться к нужному DAO методу?

comp00 ★★★★ ()
Ответ на: комментарий от comp00

Если такая возможность есть, то да, имхо. Возможно даже проще будет здесь реализовать кеширование, если оно понадобится (лично я не очень доверяю магии second-level кеша, ибо не прозрачно и vendor-lock).

Если же её нет, то придется либо всегда помнить о лочках, либо включать автокомит, что как я понимаю не возможно, учитывая что присутствуют нестед транзкции.

Представить хотя бы select, после которого что-то доопределяется от удаленного сервера, который досят не хорошие люди, и запрос он обрабатывает 50 секунд. И на всё это время данные которые выгреб select висят readonly, в то время как бедный юзер пытается что-то записать в базу.

Ситуация в общем-то довольно задаче-специфична. И проверять придется на практике.

И на последок немного буквоедства:

Мнение людей о 'Open session in a view':

http://stackoverflow.com/questions/1103363/why-is-hibernate-open-session-in-v...

И мнение JBoss о этом же, с примерами:

https://community.jboss.org/wiki/OpenSessionInView

anonymous ()
Ответ на: комментарий от comp00

А как тогда работать с этой сессией из сервлета? Передавать его в каком нибудь request?

Нед. Когда делаешь «hibernate.current_session_context_class», «thread», то currentSession будет браться из thread-local перменной. То есть сервлет делает open session, begin, commit, etc, а в сервлете открытая сессия будет доступна через currentSession.

dizza ★★★★★ ()
Ответ на: комментарий от comp00

Lazy load можно делать по разному. Начни с простого, с session-per-request.

dizza ★★★★★ ()
Ответ на: комментарий от dizza

Я разобрался, спасибо за помощь.
Кстати, с целью уменьшения колличества сервлетов и более удобногообмена между js и сервлетами, решил посмотреть в сторону Rest API. Стоит ли, и какие подводные камни меня могут ожидать? Относительно hibernate? Получится ли там реализовать open session in a view?

comp00 ★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.