Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > Java: Общие вопросы > Вопрос по Hibernate и Spring |
Автор: PovAnd 24.3.2008, 13:34 | ||||||||||||||||||
Хочу для себя окончательно определить следующую вещь, которая уже давно не дает пакоя, а ответа еще на нее я так и не нашел: как себя правильно вести, работая с 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?(ДА/НЕТ) |
Автор: Kangaroo 24.3.2008, 16:20 |
Присоединяюсь к вопросу. У нас в проекте сделано через *. Мы тянули все сразу, а там где нужно было меньше информации делали еще один маппинг. Хочется узнать как это правильно делать. |
Автор: fixxer 24.3.2008, 17:02 |
Попробую высказать свое ХО. На мой взгляд тут есть два решения. 1. Open Session In View, когда сессия закрывается только после того, как view отрендерится. (В фильре, интерсепторе, еще где-то) 2. Command, когда в контекст сессии помещаются не атомарные CRUD операции из DAO, а более осмысленный законченный набор операций, так называемый Unit Of Work. А у Вас, насколько я понял, сервисный слой функционально дублирует дао слой. |
Автор: PovAnd 24.3.2008, 17:25 |
1. Open Session In View, когда сессия закрывается только после того, как view отрендерится. (В фильре, интерсепторе, еще где-то) Суть ведь вопроса в том не в том, что я не знаю как обойти проблему закрытия/открытия session, а в том как это легше всего сделать в рамках использования Spring. Именно кайф в том чтобы вообще забыть о том что есть? какая-то сессия, пускай об этом думает правильно настроенный Spring. Если конечно это возможно. 2. Command, когда в контекст сессии помещаются не атомарные CRUD операции из DAO, а более осмысленный законченный набор операций, так называемый Unit Of Work. А у Вас, насколько я понял, сервисный слой функционально дублирует дао слой. Согласно п. 2, если это возможно, покажите в рамках описанного примера, как возможно получать лист Adress для Person. |
Автор: fixxer 24.3.2008, 17:40 | ||
Ну вот смотрите. У вас какая задача? По заданному Person#id получить Person, а по нему список Address. Два шага. Но у вас они разнесены по двум разным сервисам. Объедините их в одном сервисном методе, например, getPersonAddressesByPersonId. Дело в том, что по моему мнению, делить сервисы стоит не по принципу сущности, с которой работает сервис, а по принципу бизнес-задачи. Ведь смысл сервисов в том, что они добавляют дополнительный уровень косвенности. |
Автор: PovAnd 24.3.2008, 18:14 |
В реальности задача несколько сложнее. В данном примере согласен, наличие сервисов имеет минимальный смылс, поскольку в рамках примера нет никаких особых задач, есть только запроса 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().. весь вопрос в том есть ли такая волщебная прокся и если есть - как ее юзать. |