Hibernate

Hibernate: Ошибка при сохранении объекта NonUniqueObjectException

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

Данная ошибка проявляется в момент вызова saveOrUpdate() у hibernate template. Суть проблемы в том, что в один момент времени у нас есть два разных объекта с одинаковым идентификатором: один в сессии, а второй тот, что мы пытаемся сохранить.

Person p1 = (Person)getHibernateTemplate().get(Person.class, 1);

...

// здесь мы хотим сохранить Person с именем Alex
// и id = 1 (при том, что у нас уже есть объект p1 в сессии)
Person p2 = new Person();
p2.setId(1);
p2.setName("Alex");

hibernateTemplate.saveOrUpdate(p2); // NonUniqueObjectException

Lazy Init или принцип Open session in view

В самом начале своего нелегкого пути по изучению Hibernate и фреймворка Spring для Java столкнулся с проблемой возникновения некого LazyInitException, когда пытался получить некий объект, ссылку на инстанс которого содержал другой, полученный от хибернейта.

Как всегда, горел очередной проект и читать документации особо было некогда, поэтому, пробежавшись быстро по поисковикам, уловил основной смысл данного поведения - при описания меппинга одного объекта по-умолчанию все его связи с другими объектами воспринимаются Hibernate как "ленивые". Иными словами, при наполнении данными объекта Group, который, к примеру, содержит в себе List users и связан с таблицей USER как One-To-Many, Hibernate не пойдет по этой связи наполнять данными список. Сделает он это лишь тогда, когда мы обратимся к этому списку. Здесь то и кроется корень проблемы - в моем приложении обращение к списку шло в тот момент, когда хибернейт сессия работы с базой, в которой я получил объект Group была уже закрыта. Отсюда и LazyInitException.

Наследование в Hibernate

Есть несколько вариантов организации иерархии при работе с Hibernate ORM. На данный момент выделяют три подхода:

  • One table per concrete class - для каждого наследника создается отдельная таблица со всеми полями, включая пронаследованные.
  • One table per class hierarchy - создаем одну "родительскую" таблицу, каждая строка которой должна содержать все поля родительского класса, плюс поля всех наследников, т.е. потенциально кучу null значений.
  • One table per subclass - мэппим базовый класс и наследников на отдельные таблицы и создаем иерархию с помощью внешних ключей.

Остановимся на 3м варианте - One table per subclass, наиболее удачном на мой взгляд, т.к. в базе данных не будет никакой избыточности полей, как в первом варианте, и не будет проблемы масштабирования, как в варианте 2.