|
|
|
LazyInitializationException, и как с ним бороться
|
|||
|---|---|---|---|
|
#18+
Нет, я конечно гуглил (и уже пересмотрел туеву хучу вариантов как с этим бороться), но есть вопрос в моей голове и он не даёт мне покоя. Суть: Имеем Spring Framework, а также небольшая БД. В ней (в бд) порядка 15 таблиц довольно тесно друг с другом связанных внешними ключами. Есть Scheduler который периодически что-то синхронизирует с другими БД (это не суть, просто чтобы было понятно что используется не только MVC). Имеется слоистая архитектура(домены с описанными связями, сервисный леер, дао). На сколько я понимаю, сервисный слой не должен ничего знать о БД (это работа DAO), соответственно открывать там сессию не есть хорошо (поправьте если ошибаюсь). Получается что в DAO мы должны реализовать методы возвращающие нам наиболее полные, проинициализированные proxy domain объекты. К примеру для одной задачи ("пример с потолка") мне нужно вытащить из БД учеников класса. Ok, реализовали метод findУченики. Для другой задачи внезапно нам понадобились их родители, нет проблем делаем в DAO findУченинкиСРодителями. Вопрос: Что делать если дальше Мне понадобиться вытаскивать учеников с родителями и их адресами и т. д. и т. п.? Реализовывать очередной сто-первый метод в DAO что то вроде findУчиеникСРодителямиСАдресами? Как быть если в сервисном слое на определённом этапе бизнес-логики мне понадобилось узнать адреса родителей, но вот незадача для поиска учеников я использовал метод findУченинкиСРодителями, и если я делаю обращение dao.getPuple().getParent().getAddress() - то ловлю LazyInitializationException. Мне кажется подход в создании методов в ДАО на каждый "чих" не правильный и не логичный (для чего вообще тогда этот ORM). Как поступаете вы в подобных случаях? PS. Про dbp.hibernate.enable_lazy_load_no_trans читал и пробовал, но боюсь использовать, т.к. хороших отзывов о нём не много. PPS. Про открытие сессии во view тоже читал, но в данном случае MVC не используется. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 30.09.2013, 16:41:56 |
|
||
|
LazyInitializationException, и как с ним бороться
|
|||
|---|---|---|---|
|
#18+
В сервисном слое _нормально_ "знать" о слое данных. Именно в сервисном слое обычно открывают сессии/транзакции Хибера/JPA, а потом внутри них дергают ДАО. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 30.09.2013, 16:53:12 |
|
||
|
LazyInitializationException, и как с ним бороться
|
|||
|---|---|---|---|
|
#18+
SkyFireЕсть Scheduler который периодически что-то синхронизирует с другими БД (это не суть, просто чтобы было понятно что используется не только MVC). Не суть, но головняк реальный. SkyFireНа сколько я понимаю, сервисный слой не должен ничего знать о БД (это работа DAO), соответственно открывать там сессию не есть хорошо (поправьте если ошибаюсь). Посыл правильный, вывод - ошибочный. Итак - DAO инкапсулирует детали работы с источником данных (БД+JDBC). Сервису этих деталей знать не нужно. Это правильно. Но сервис при этом тесно связан с ORM. Без этого, к сожалению, уже никуда не дется. Нюансы ORM сервису нужны. Открытие и закрытие сессии не нужно делать в сервисе. Это правильно. Но. Сессия, обычно, свяазана с транзакцией. Поэтому открытие и комит транзакции делает обслуживающий код вокруг сервиса, зачастую через AOP. А сессия тупо привязывается к текущему потоку. Нет сессии - создаётся новая. Есть сессия - берется текущая из ThreadLocal (а поток уже прибит к запросу из MVC или скедулеру) SkyFire Получается что в DAO мы должны реализовать методы возвращающие нам наиболее полные, проинициализированные proxy domain объекты. К примеру для одной задачи ("пример с потолка") мне нужно вытащить из БД учеников класса. Ok, реализовали метод findУченики. Для другой задачи внезапно нам понадобились их родители, нет проблем делаем в DAO findУченинкиСРодителями. На самом деле DAO - инкапсулирует работу с источником данных. А вот то что вы описываете, называется Repository. Этот паттерн инкапсулирует сложные запросы. В том числе и ORM запросы. SkyFire Вопрос: Что делать если дальше Мне понадобиться вытаскивать учеников с родителями и их адресами и т. д. и т. п.? Реализовывать очередной сто-первый метод в DAO что то вроде findУчиеникСРодителямиСАдресами? Да. Это не так сложно, если использовать управление через Fetch/FetchMode. Можно пере-использовать одни и те же запросы, но в разных сервисных методах, указывать fetch для тех ассоциаций, которые вам нужно прогрузить сразу. SkyFireКак быть если в сервисном слое на определённом этапе бизнес-логики мне понадобилось узнать адреса родителей, но вот незадача для поиска учеников я использовал метод findУченинкиСРодителями, и если я делаю обращение dao.getPuple().getParent().getAddress() - то ловлю LazyInitializationException. "Сервис" это "простонародное" название паттерна, который у Фаулера называется Transaction Script. Из названия очевидно что Transaction Script реализует некоторую бизнес-транзакцию. Бизнес-транзакция зачастую связана с транзакцией в базе. А если ещё открыть JavaDoc класса HibernateSession, то можно обнаружить что scope для сессии обычно совпадает со scope транзации. Поэтому вывод о том что Session создаётся и закрывается в DAO - ошибочен. Сессия создаётся тогда когда она первыя раз нужна и закрывается, обычно, при выходе из метода Transaction Script, вместе с коммитом сессии и, затем, транзакции. SkyFireМне кажется подход в создании методов в ДАО на каждый "чих" не правильный и не логичный (для чего вообще тогда этот ORM). Как поступаете вы в подобных случаях? Criteria API, Repository. Актуальные версии Spring Data вообще имеют забавную фичу. Там запросы генерятся из имени метода и параметров. "не правильно" это какой-то мутные термин. Чем именно это плохо? ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 30.09.2013, 16:59:43 |
|
||
|
LazyInitializationException, и как с ним бороться
|
|||
|---|---|---|---|
|
#18+
BlazkowiczА если ещё открыть JavaDoc класса HibernateSession, то можно обнаружить что scope для сессии обычно совпадает со scope транзации. Полагаю очевидно, что имя класса Session, а не HibernateSession. Кто-то просто провтыкал пробелы и знаки пунктуацию. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 30.09.2013, 17:06:31 |
|
||
|
LazyInitializationException, и как с ним бороться
|
|||
|---|---|---|---|
|
#18+
Благодарю за столь развёрнутый и полный ответ. Очень многое прояснилось. Сейчас убрал из слоя "DAO" (Переименовал в Repository) все аннотации @Transactional и перенёс их в сервис слой - всё заработало. Но вот вопрос, почему возникал LIE когда и в слое Repository и в сервисном слое были указаны @Transactional? На сколько я знаю по умолчанию транзакционный атрибут выставляется как "Required", что означает если уже открыта транзакция то будет использоваться область видимости существующей транзакции. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 30.09.2013, 17:48:53 |
|
||
|
LazyInitializationException, и как с ним бороться
|
|||
|---|---|---|---|
|
#18+
Сам по себе паттерн DAO/Repository полезен если вы планируете менять СУБД. И, возможно,полезен при тестировании слоя сервисов. В принципе ничего плохого в том, чтобы прямо в сервисе писать session.find не вижу. При этом стоит иметь ввиду, что Hibernate и есть DAO. А то что обычно называют DAO, это Repository. SkyFireДля другой задачи внезапно нам понадобились их родители, нет проблем делаем в DAO findУченинкиСРодителями. Вопрос: Что делать если дальше Мне понадобиться вытаскивать учеников с родителями и их адресами и т. д. и т. п.? Реализовывать очередной сто-первый метод в DAO что то вроде findУчиеникСРодителямиСАдресами? ORM как таковой нам дает возможность в сервисе не думать о том, откуда берутся данные. Т.е., если у вас правильно прописаны маппинги и указаны связи, то findУченикиСРодителяеми не нужен, так как вы просто пишите в сервисе (пишу с условными методами): Код: java 1. 2. 3. 4. 5. По этому коду видно, что: 1. Мы используем hibernate именно как DAO. 2. Мы не забоимся в коде о загрузке свойств student. Важным моментом при работе с Hibernate/ORM является понятие времени жизни сессии. Так вот, я считаю, что время жизни сессии должно быть равно времени жизни объекта, полученного из этой сессии. Если это так, то LazyException вы не получите и всегда можете писать код, не думая о том - загружены уже свойства из БД или нет. Самый простой способ добиться этого - делать время жизни равным времени обработки запроса. В вашем случае - времени обработки запроса от таймера. Открыть сессию перед методом вызова таймера и закрыть сессию после этого метода. Делается это, как правило, через AOP. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 30.09.2013, 17:57:34 |
|
||
|
LazyInitializationException, и как с ним бороться
|
|||
|---|---|---|---|
|
#18+
SkyFireБлагодарю за столь развёрнутый и полный ответ. Очень многое прояснилось. Сейчас убрал из слоя "DAO" (Переименовал в Repository) все аннотации @Transactional и перенёс их в сервис слой - всё заработало. В Spring Data есть много вкусного. Правда только для JPA API. Но можно с хибером и через него бодаться. SkyFireНо вот вопрос, почему возникал LIE когда и в слое Repository и в сервисном слое были указаны @Transactional? На сколько я знаю по умолчанию транзакционный атрибут выставляется как "Required", что означает если уже открыта транзакция то будет использоваться область видимости существующей транзакции. На то может быть несколько причин. Надо разбираться. Первая - управление сессией, как-то отвязано от управления транзакцией. И не смотря на открытую транзакицю, сессия оказалась закрыта. Вторая - часто бывает как раз во всяких скедулерах. Вызов метода произошел мимо Spring AOP прокси. Например, через this. Поэтому @Transactional успешно был проигнорирован. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 30.09.2013, 17:58:52 |
|
||
|
LazyInitializationException, и как с ним бороться
|
|||
|---|---|---|---|
|
#18+
BlazkowiczВторая - часто бывает как раз во всяких скедулерах. Вызов метода произошел мимо Spring AOP прокси. Например, через this. Поэтому @Transactional успешно был проигнорирован. т.е. в данном случае транзакция будет проигнорирована? Код: java 1. 2. 3. 4. 5. 6. 7. 8. 9. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 30.09.2013, 18:10:48 |
|
||
|
LazyInitializationException, и как с ним бороться
|
|||
|---|---|---|---|
|
#18+
SkyFireт.е. в данном случае транзакция будет проигнорирована? Код: java 1. 2. 3. 4. 5. 6. 7. 8. 9. Да. Типа этого. Зависит, конечно, от используемых фреймверков. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 30.09.2013, 18:31:16 |
|
||
|
LazyInitializationException, и как с ним бороться
|
|||
|---|---|---|---|
|
#18+
Проверил в Spting 3.2 - LIE Не подскажите какой подход будет правильным? Если в некотором методе (Task) мне нужно последовательно вызвать несколько методов этого же класса, но при этом одна общая транзакция не нужна. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 30.09.2013, 18:38:30 |
|
||
|
LazyInitializationException, и как с ним бороться
|
|||
|---|---|---|---|
|
#18+
SkyFireПроверил в Spting 3.2 - LIE Не подскажите какой подход будет правильным? Если в некотором методе (Task) мне нужно последовательно вызвать несколько методов этого же класса, но при этом одна общая транзакция не нужна. Отделить Scheduled Task (методы запускаемые по расписанию) и Transaction Script (бизнес-транзакции). И тогда не будет этих проблемых вызовов через this. Можно было бы захачить, если бы спринг позволял нормально заинжектить ссылку на себя из контекста. Но, я не помню, реализована ли эта фича уже. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 30.09.2013, 18:41:54 |
|
||
|
LazyInitializationException, и как с ним бороться
|
|||
|---|---|---|---|
|
#18+
Вроде, ещё не сделали: http://stackoverflow.com/questions/5107505/can-i-inject-same-class-using-spring http://stackoverflow.com/questions/5152686/self-injection-with-spring https://jira.springsource.org/browse/SPR-8450 ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 30.09.2013, 18:44:18 |
|
||
|
LazyInitializationException, и как с ним бороться
|
|||
|---|---|---|---|
|
#18+
Понятно. Спасибо ещё раз за ответы. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 30.09.2013, 18:51:23 |
|
||
|
LazyInitializationException, и как с ним бороться
|
|||
|---|---|---|---|
|
#18+
SkyFireт.е. в данном случае транзакция будет проигнорирована? Код: java 1. 2. 3. 4. 5. 6. 7. 8. 9. [/quot] Так, насколько я понимаю, должна запуститься транзакция. Если Scheduler тоже каким-то образом имеет отношение к Spring'у. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 30.09.2013, 18:55:37 |
|
||
|
LazyInitializationException, и как с ним бороться
|
|||
|---|---|---|---|
|
#18+
LeonidvТак, насколько я понимаю, должна запуститься транзакция. Если Scheduler тоже каким-то образом имеет отношение к Spring'у. SkyFire мне нужно последовательно вызвать несколько методов этого же класса, но при этом одна общая транзакция не нужна. ... |
|||
|
:
Нравится:
Не нравится:
|
|||
| 30.09.2013, 21:21:56 |
|
||
|
|

start [/forum/topic.php?fid=59&msg=38412249&tid=2128508]: |
0ms |
get settings: |
10ms |
get forum list: |
19ms |
check forum access: |
7ms |
check topic access: |
7ms |
track hit: |
287ms |
get topic data: |
18ms |
get forum data: |
5ms |
get page messages: |
86ms |
get tp. blocked users: |
2ms |
| others: | 282ms |
| total: | 723ms |

| 0 / 0 |
