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

org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session

   Ошибка возникает потому что пытаемся сохранить/удалить/проапдейтить объект из другой сессии, который уже загружен в нашей сессии(закэширован).
 Лечение:
1) 100% лекарство использовать hibernateTemplate.merge(object);
2) 100% лекарство которое приводит к обнулению всего кэша

      hibernateTemplate.flush();
      hibernateTemplate.clear();
      hibernateTemplate.saveOrUpdate(object);


3)   лекарство может не помочь

      hibernateTemplate.evict(object); - убирает объекты из кэша сравнивая их по объектной ссылке
      hibernateTemplate.saveOrUpdate(object); - проверяет наличие объектов в кэше(прежде чем их сохранить) по айдишнику. Вывод: такая пара смысла не имеет(ну, имеет смысл в рамках одной сессии и совсем для другого).

      Можно написать так:
      Class dataObjectClass = object.getClass();
      long id = object.getId();
      T dataObject = (T) hibernateTemplate.get(dataObjectClass, id);

      if (dataObject != null) {
        hibernateTemplate.evict(dataObject );
      }

Но это спасёт нас только в том случае - если в ентитях нигде нету перекрёстных ссылок. Если они есть - то при каскадном сохранении будут ентити добавляться в кэш по нескольку раз и в результате получим тот же иксэпшн(Зы: иксэпшн будет валиться непосредственно при комите транзакции и всякие flush перед evict тут непомогут).

    
  
4) делать всё в одной сессии
5)загрузить по id,  выставить необходимые поля, сохранить

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

среда, 23 сентября 2009 г.

Тик-так, ж-ж-ж

Тик-так, ж-ж-ж.
Огромный серый грибулонский разведывательный корабль тихо плыл сквозь черную пустоту (а может, пустую черноту?). Он несся с невероятной, захватывающей дух скоростью, и все же на фоне биллионов далеких-далеких звезд казалось, будто он не движется вовсе. Так, темная крапинка, примерзшая к бархатной подкладке бриллиантовой ночи.
На борту корабля все шло так, как было тысячелетиями. Тишь-гладь-темень.
Тик-так, ж-ж-ж.
Ну-у-у, редкие исключения не в счет.
Тик-тик-так, ж-ж-ж.
Тик-ж-ж-так-ж-ж-тик-ж-ж.
Тик-тик-тик-тик-так-ж-ж.
Хм-м-м.