![]() |
Модераторы: LSD, AntonSaburov |
![]() ![]() ![]() |
|
PovAnd |
|
||||||||||||||||||
Шустрый ![]() Профиль Группа: Участник Сообщений: 102 Регистрация: 20.8.2007 Репутация: нет Всего: нет |
Хочу для себя окончательно определить следующую вещь, которая уже давно не дает пакоя, а ответа еще на нее я так и не нашел: как себя правильно вести, работая с Hibernate и Spring, чтобы не получать Lazy Initialization Exception.
Заранее прошу прощение за утрирование, но подругому я не пойму. Мне известно, что Lazy Initialization Exception возникает тогда, когда мы пытаемся получить проинициализировать объект данными из БД, обращаясь к обертке - прокси. Чтобы инициализация произошла успешно, нужна открытая session с БД. Но все держать время открытой session не рационально и она как правило после частичной инициализации объекта закрывается. Теперь я приведу полное описание, того как я поступаю чтобы избегать Lazy Initialization Exception и далее расчитываю на то, что более граммотные товарищи меня поправят и укажут, где именно я не совсем прав. Беру наиболее простой (на мой взгляд) пример: Имеем пару объектов: 1. Человек 2. Адрес Человек может проживать по нескольким Адресам. По одному Адресу может проживать только один Человек. ________________________________ Beans:
hibernate-mapping:
//Spring настройка доступа к DB, Hibernate и Transaction Manger:
DAO:
Spring конфиг для DAO:
Services:
Spring конфиг для Services:
____________________________________ Выше приведенный листинг, это типичный пример того, как я пишу код, используя Hibernate и Spring. Вкратце: 1. описали в рамках класса конкретные сущности, 2. смэпили БД на эти сущности 3. написали классы, непосредственно отвечающие за изменение и выборку сущностей в БД (DAO) 4. написали классы для манипуляций с сущностями в иных случаях, не относящихся к работе с БД (Сервисы). Эти сервисы обернули в транзакшен манаджер, с тем расчетом, чтобы Spring сам смотрел, как и где надо открывать-закрывать сеансы связи с БД, разрешать-отменять комиты при работе с БД. У меня есть сомнения, что-то, что написано это лучший вариант (ну или я чего-то не совсем правильно понимаю, используя данный подход). Опишу ситуацию, из которой такие сомнения рождаются:
В test3() - всё ОК, а случаях test1() и test2() получили
Вобщем какие выводы делаю из всего этого я: В случае test1() мы получаем эксепшен, потому что объект пытаемся попросить у прокси объекта Person лист адресов, прокся пытается запросить данные у БД, но сеанс связи закрыт и поэтому облом. Случай test2(): вывод там получается такой как и для test1(), но здесь я имею некоторые сомнения в правильности своего подхода: ведь сервис PersonService обернут транзакшен менеджером, неужели нет в Spring такой возможности, чтобы в рамках этой обертки произошла проверка: закрыта ли сессия для прокси данного сущности и если да , то ее нужно снова открыть? В случае test3() все работает, но как-то криво это все получается. Ибо чтобы забрать лист Adress у объекта Person, я должен в конечном итоге добраться аж до самого дао, чтобы объект отрефрешить и потом "ручками" проинициализировать лист адресов. И это все при том, что у объекта Person имеется метод List<Adress> getAdresses(), который как-будто издевательски имеет модификатор доступа public. ----------------- В итоге мне было бы интересно, услышать, что именно в моих рассуждениях (исходя из выводов) требует пересмотра точки зрения на проблему. Или как именно нужно изменить сам подход (т.е. покажите более правильный код), чтобы выводы стали другими. В крайнем случае, исходя из моего примера: считаете ли что я правильно рассуждаю относительно подхода использования hibernate и spring?(ДА/НЕТ) Это сообщение отредактировал(а) PovAnd - 24.3.2008, 13:42 |
||||||||||||||||||
|
|||||||||||||||||||
Kangaroo |
|
|||
![]() AA - Aussie Animal ![]() ![]() ![]() ![]() Профиль Группа: Участник Клуба Сообщений: 2042 Регистрация: 7.10.2006 Где: US Репутация: 21 Всего: 104 |
Присоединяюсь к вопросу.
У нас в проекте сделано через *. Мы тянули все сразу, а там где нужно было меньше информации делали еще один маппинг. Хочется узнать как это правильно делать. -------------------- Lost.... |
|||
|
||||
fixxer |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 672 Регистрация: 14.9.2006 Где: Саратов, Россия Репутация: 6 Всего: 27 |
Попробую высказать свое ХО. На мой взгляд тут есть два решения.
1. Open Session In View, когда сессия закрывается только после того, как view отрендерится. (В фильре, интерсепторе, еще где-то) 2. Command, когда в контекст сессии помещаются не атомарные CRUD операции из DAO, а более осмысленный законченный набор операций, так называемый Unit Of Work. А у Вас, насколько я понял, сервисный слой функционально дублирует дао слой. -------------------- ![]() |
|||
|
||||
PovAnd |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 102 Регистрация: 20.8.2007 Репутация: нет Всего: нет |
1. Open Session In View, когда сессия закрывается только после того, как view отрендерится. (В фильре, интерсепторе, еще где-то)
Суть ведь вопроса в том не в том, что я не знаю как обойти проблему закрытия/открытия session, а в том как это легше всего сделать в рамках использования Spring. Именно кайф в том чтобы вообще забыть о том что есть? какая-то сессия, пускай об этом думает правильно настроенный Spring. Если конечно это возможно. 2. Command, когда в контекст сессии помещаются не атомарные CRUD операции из DAO, а более осмысленный законченный набор операций, так называемый Unit Of Work. А у Вас, насколько я понял, сервисный слой функционально дублирует дао слой. Согласно п. 2, если это возможно, покажите в рамках описанного примера, как возможно получать лист Adress для Person. |
|||
|
||||
fixxer |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 672 Регистрация: 14.9.2006 Где: Саратов, Россия Репутация: 6 Всего: 27 |
Ну вот смотрите. У вас какая задача? По заданному Person#id получить Person, а по нему список Address. Два шага. Но у вас они разнесены по двум разным сервисам. Объедините их в одном сервисном методе, например, getPersonAddressesByPersonId. Дело в том, что по моему мнению, делить сервисы стоит не по принципу сущности, с которой работает сервис, а по принципу бизнес-задачи. Ведь смысл сервисов в том, что они добавляют дополнительный уровень косвенности. -------------------- ![]() |
|||
|
||||
PovAnd |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 102 Регистрация: 20.8.2007 Репутация: нет Всего: нет |
В реальности задача несколько сложнее. В данном примере согласен, наличие сервисов имеет минимальный смылс, поскольку в рамках примера нет никаких особых задач, есть только запроса Person через DAO. Но сервисы я обозначил исходя из того, что у меня есть реальная задача, где сервисы имеют больший смысл чем просто получение объекта (но проблемы описанные в примере никуда не пропадают). И уж поскольку в реальной задаче у меня имеются сервисы, то те объекты которые юзают сервисы уж точно не должны знать об объектах DAO, соответсвенно это достигается путем делегирования нужных методов DAO в сервис.
Объедините их в одном сервисном методе, например, getPersonAddressesByPersonId. Объединить то не проблема, не ясно только что писать в том объединении. Я хочу понять можно ли как то в рамках сервиса добиться такого(в том условно объединенном методе) кода : List<Adress> getAdressPerson(Person person){ return person.getAdress(); } вместо без кайфового такого: ... getHibernateTemplate().refresh(person); getHibernateTemplate().initialize(person.getAdresses()); .... Вот если мне станет ясно, как сделать рабочим(без возбуждения LazyException..) такой метод исходя из условия, что объект person может придти откуда угодно, то это будет шИкарно. В моем понимании, обернутый в проксю бин вот с этим методом, работать должне так: у интерфейса сервиса вызывается метод List<Adress> getAdressPerson(Person person) . На самом деле реализация этого метода - прокся с делегатом в роле которого родная имплементациия моего сервиса. Так вот эта прокся, перед вызовом метода getAdressPerson() у моей иплементации должна проверить сессию для объекта person и затем и если нужно открыть , иопять же, если нужно - проинициализировать объект, и за тем уже передать объект методу в моей имплементации где без всякого напряга для меня вызавется без траблов person.getAdress().. весь вопрос в том есть ли такая волщебная прокся и если есть - как ее юзать. Это сообщение отредактировал(а) PovAnd - 24.3.2008, 18:15 |
|||
|
||||
mindflyer |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 113 Регистрация: 20.10.2004 Где: Smolensk, Russia Репутация: 1 Всего: 4 |
Вообще, узнать открыта/закрыта ли сессия и проинициализирован ли объект/коллекция можно через хибернейтовые интерфейсы: org.hibernate.proxy.HibernateProxy для объекта и org.hibernate.collection.AbstractPersistentCollection для коллекции.
Со спрингом никогда не работал и не могу посоветовать как делать в его случае, но в случае EJB SessionBean имел сходную проблему, которую можно решить навесив особый интерсептор на SB. Этот интерсептор будет смотреть проинициализирован ли объект/коллекция - если нет, то он может подменять объект на объект из бд в рамках текущей сессии/транзакции и в сам SB уйдёт уже новый объект, который может корректно проинициализироваться. А в коде метода тогда будет именно что простейший код типа "return person.getAdress();". Однако, это решение именно что для простейших ситуаций. А если подходить более абстрактно, то присоединяюсь к:
|
|||
|
||||
![]() ![]() ![]() |
Правила форума "Java" | |
|
Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic. |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Java: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |