powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Java [игнор отключен] [закрыт для гостей] / LazyInitializationException, и как с ним бороться
15 сообщений из 15, страница 1 из 1
LazyInitializationException, и как с ним бороться
    #38412069
SkyFire
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Нет, я конечно гуглил (и уже пересмотрел туеву хучу вариантов как с этим бороться), но есть вопрос в моей голове и он не даёт мне покоя.

Суть: Имеем 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 не используется.
...
Рейтинг: 0 / 0
LazyInitializationException, и как с ним бороться
    #38412085
cdtyjv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В сервисном слое _нормально_ "знать" о слое данных. Именно в сервисном слое обычно открывают сессии/транзакции Хибера/JPA, а потом внутри них дергают ДАО.
...
Рейтинг: 0 / 0
LazyInitializationException, и как с ним бороться
    #38412093
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
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 вообще имеют забавную фичу. Там запросы генерятся из имени метода и параметров. "не правильно" это какой-то мутные термин. Чем именно это плохо?
...
Рейтинг: 0 / 0
LazyInitializationException, и как с ним бороться
    #38412099
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
BlazkowiczА если ещё открыть JavaDoc класса HibernateSession, то можно обнаружить что scope для сессии обычно совпадает со scope транзации.
Полагаю очевидно, что имя класса Session, а не HibernateSession. Кто-то просто провтыкал пробелы и знаки пунктуацию.
...
Рейтинг: 0 / 0
LazyInitializationException, и как с ним бороться
    #38412149
SkyFire
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Благодарю за столь развёрнутый и полный ответ. Очень многое прояснилось. Сейчас убрал из слоя "DAO" (Переименовал в Repository) все аннотации @Transactional и перенёс их в сервис слой - всё заработало. Но вот вопрос, почему возникал LIE когда и в слое Repository и в сервисном слое были указаны @Transactional? На сколько я знаю по умолчанию транзакционный атрибут выставляется как "Required", что означает если уже открыта транзакция то будет использоваться область видимости существующей транзакции.
...
Рейтинг: 0 / 0
LazyInitializationException, и как с ним бороться
    #38412168
Leonidv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Сам по себе паттерн DAO/Repository полезен если вы планируете менять СУБД. И, возможно,полезен при тестировании слоя сервисов. В принципе ничего плохого в том, чтобы прямо в сервисе писать session.find не вижу.

При этом стоит иметь ввиду, что Hibernate и есть DAO. А то что обычно называют DAO, это Repository.

SkyFireДля другой задачи внезапно нам понадобились их родители, нет проблем делаем в DAO findУченинкиСРодителями.
Вопрос: Что делать если дальше Мне понадобиться вытаскивать учеников с родителями и их адресами и т. д. и т. п.?
Реализовывать очередной сто-первый метод в DAO что то вроде findУчиеникСРодителямиСАдресами?


ORM как таковой нам дает возможность в сервисе не думать о том, откуда берутся данные. Т.е., если у вас правильно прописаны маппинги и указаны связи, то findУченикиСРодителяеми не нужен, так как вы просто пишите в сервисе (пишу с условными методами):

Код: java
1.
2.
3.
4.
5.
Students students = session.query("from Student where age > 10").list();
for (Student student : students) {
 println(student.getParents().size());
 println(student.getAddress().getStreet))
}



По этому коду видно, что:
1. Мы используем hibernate именно как DAO.
2. Мы не забоимся в коде о загрузке свойств student.

Важным моментом при работе с Hibernate/ORM является понятие времени жизни сессии. Так вот, я считаю, что время жизни сессии должно быть равно времени жизни объекта, полученного из этой сессии. Если это так, то LazyException вы не получите и всегда можете писать код, не думая о том - загружены уже свойства из БД или нет. Самый простой способ добиться этого - делать время жизни равным времени обработки запроса. В вашем случае - времени обработки запроса от таймера. Открыть сессию перед методом вызова таймера и закрыть сессию после этого метода. Делается это, как правило, через AOP.
...
Рейтинг: 0 / 0
LazyInitializationException, и как с ним бороться
    #38412171
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
SkyFireБлагодарю за столь развёрнутый и полный ответ. Очень многое прояснилось. Сейчас убрал из слоя "DAO" (Переименовал в Repository) все аннотации @Transactional и перенёс их в сервис слой - всё заработало.
В Spring Data есть много вкусного. Правда только для JPA API. Но можно с хибером и через него бодаться.


SkyFireНо вот вопрос, почему возникал LIE когда и в слое Repository и в сервисном слое были указаны @Transactional? На сколько я знаю по умолчанию транзакционный атрибут выставляется как "Required", что означает если уже открыта транзакция то будет использоваться область видимости существующей транзакции.
На то может быть несколько причин. Надо разбираться.
Первая - управление сессией, как-то отвязано от управления транзакцией. И не смотря на открытую транзакицю, сессия оказалась закрыта.
Вторая - часто бывает как раз во всяких скедулерах. Вызов метода произошел мимо Spring AOP прокси. Например, через this. Поэтому @Transactional успешно был проигнорирован.
...
Рейтинг: 0 / 0
LazyInitializationException, и как с ним бороться
    #38412191
SkyFire
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
BlazkowiczВторая - часто бывает как раз во всяких скедулерах. Вызов метода произошел мимо Spring AOP прокси. Например, через this. Поэтому @Transactional успешно был проигнорирован.

т.е. в данном случае транзакция будет проигнорирована?

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
    @Scheduled(fixedDelay = 60000)
    public void task() {
        this.synchStudents();
    }

    @Transactional
    public void synchStudents() {
        //....
    }
...
Рейтинг: 0 / 0
LazyInitializationException, и как с ним бороться
    #38412223
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
SkyFireт.е. в данном случае транзакция будет проигнорирована?

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
    @Scheduled(fixedDelay = 60000)
    public void task() {
        this.synchStudents();
    }

    @Transactional
    public void synchStudents() {
        //....
    }


Да. Типа этого. Зависит, конечно, от используемых фреймверков.
...
Рейтинг: 0 / 0
LazyInitializationException, и как с ним бороться
    #38412240
SkyFire
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Проверил в Spting 3.2 - LIE
Не подскажите какой подход будет правильным? Если в некотором методе (Task) мне нужно последовательно вызвать несколько методов этого же класса, но при этом одна общая транзакция не нужна.
...
Рейтинг: 0 / 0
LazyInitializationException, и как с ним бороться
    #38412246
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
SkyFireПроверил в Spting 3.2 - LIE
Не подскажите какой подход будет правильным? Если в некотором методе (Task) мне нужно последовательно вызвать несколько методов этого же класса, но при этом одна общая транзакция не нужна.
Отделить Scheduled Task (методы запускаемые по расписанию) и Transaction Script (бизнес-транзакции). И тогда не будет этих проблемых вызовов через this. Можно было бы захачить, если бы спринг позволял нормально заинжектить ссылку на себя из контекста. Но, я не помню, реализована ли эта фича уже.
...
Рейтинг: 0 / 0
LazyInitializationException, и как с ним бороться
    #38412249
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
LazyInitializationException, и как с ним бороться
    #38412259
SkyFire
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Понятно. Спасибо ещё раз за ответы.
...
Рейтинг: 0 / 0
LazyInitializationException, и как с ним бороться
    #38412265
Leonidv
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
SkyFireт.е. в данном случае транзакция будет проигнорирована?

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
    @Scheduled(fixedDelay = 60000)
    @Transactional
    public void task() {
        this.synchStudents();
    }

    public void synchStudents() {
        //....
    }


[/quot]
Так, насколько я понимаю, должна запуститься транзакция. Если Scheduler тоже каким-то образом имеет отношение к Spring'у.
...
Рейтинг: 0 / 0
LazyInitializationException, и как с ним бороться
    #38412356
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LeonidvТак, насколько я понимаю, должна запуститься транзакция. Если Scheduler тоже каким-то образом имеет отношение к Spring'у.
SkyFire мне нужно последовательно вызвать несколько методов этого же класса, но при этом одна общая транзакция не нужна.
...
Рейтинг: 0 / 0
15 сообщений из 15, страница 1 из 1
Форумы / Java [игнор отключен] [закрыт для гостей] / LazyInitializationException, и как с ним бороться
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]