четверг, 24 сентября 2009 г.

org.hibernate.LazyInitializationException

Возникает потому что сессия, в которой был получен объект - закрыта, а поля объекта были помечены как @XXXToXXX(fetch = FetchType.LAZY).
Лечение:
1)меняем аннотацию на @XXXToXXX(fetch =FetchType.EAGER) - самый простой способ но вызывает осложнения типа долгой загрузки объекта
2)вычитав объект впервые тут же инициализируем поле с FetchType.LAZY -
делаем это так - hibernateTemplate.initialize(entity) - самый бестолковый способ
3)даём новой сессии права на работу с объектом -    
if (!hibernateTemplate.contains(track)) {
                hibernateTemplate.lock(track, LockMode.READ);;
 - самый клёвый способ НО!
you only use lock() if you are sure that the object has not been modified - иначе 

org.springframework.dao.InvalidDataAccessApiUsageException: cannot lock an unsaved transient instance: 
4) делать всё в одной сессии
5)прикрепляем к новой сессии через
             if (!hibernateTemplate.contains(track)) {
                 hibernateTemplate.merge(track);}
6)если таки точно собрались загружать поля то можно сделать hql запрос типа:
List<Customer> list = hibernateTemplate.find(
            "select distinct с from Customer с " +
            "left join fetch c.orders where c=?", customer);
в sql
 select distinct from Customer c left join fetch c.orders
 http://bwinterberg.blogspot.com/2009/08/how-to-eagerly-fetch-associations-with.html
7)Есть спец фильтры для загрузки нужных частей сущностей см. тут
http://bwinterberg.blogspot.com/2009/09/hibernate-preload-pattern.html
8)держать сессию открытой(паттерн типа '...а после нас хоть потоп') http://alekseiko.blogspot.com/2010/02/hibernate-lazyinitializationexception.html
9)загрузить по id,  выставить необходимые поля, сохранить
10)Написать свою аннотацию, которая будет проверять необходимость переподключения коллекции к новой сессии: http://9mmedia.com/blog/?p=272

5 комментариев:

  1. http://docs.jboss.org/hibernate/core/3.3/reference/en/html/objectstate.html

    ОтветитьУдалить
  2. Это просто блеск! Сразу все и в одном месте. Спасибо!

    ОтветитьУдалить
  3. Нужен совет.

    Есть два класса user и role.
    User.id <-many-to-many-> Role.id

    И есть следующий class
    Factory
    -- static role = null (Это для синглетона)
    -- static user = null (Это для синглетона)
    -- session = null (Сессия)

    --> openSession() (открываю сессию openSession())
    --> closeSession() (закрываю сессию)
    --> getUserDAO() (singleton)
    --> getRoleDAO() (singleton)

    на уровне сервиса работаю так.
    factory.openSession();
    User user = factory.getUserDAO().getUserByLogin("Admin");
    roles = user.getRoles(); // вот тут lazy exception
    factory.closeSession();

    в getUserByLogin:
    Session session = HibernateUtil.getSessionFactory().getCurrentSession();
    session.beginTransaction();
    user = (User) session.get(User.class, login);
    session.getTransaction().commit();

    второй день уже пляшу вокруг этого LazyException.
    Вроде и сессия открыта. И в других местах не закрывается. Использовать агрессивную загрузку не хочу т.к. many-to-many.

    ОтветитьУдалить
  4. Попробуй делать в одной транзакции получение юзера и его полей, см как юзать TransactionCallbackWithoutResult, TransactionCallback

    ОтветитьУдалить
  5. Долго бился со своим убеждением в MVC необходимо строго разделять все части.
    Прочитав OpenSessionInView понял: есть инструменты которые возможно использовать в строгой MVC, а есть такие, которые пронизывают проект насквозь.

    В общем пришел к выводу: реализую п. 4. с оговрокой на транзакцию. По крайней мере это не противоречит моим убеждениям.

    ОтветитьУдалить