Hibernate: недостаток слияния () по сравнению с обновлением ()
У меня проблемы с NonUniqueObjectException
брошенный Hibernate.
Читая документы и этот пост в блоге, я заменил звонок с update()
в merge()
и это решило проблему.
Я полагаю, что понимаю причину исключения и почему изменение метода решило проблему с точки зрения отключенных объектов и границ сеанса.
Мой вопрос: учитывая, что merge()
всегда разрешает объект сеанса или извлекает его, если он не существует, вызывает merge(), как правило, более безопасную альтернативу, чем update()
?
В чем недостаток использования merge()
над update()
?
3 ответа
Является ли вызов merge() вообще более безопасной альтернативой, чем update()?
Как способ избежать исключения NonUniqueObjectException, да. Я думаю, это объясняет, почему JPA не разрешает метод обновления.
В чем недостаток использования merge() вместо update()?
Пользователь, не прошедший консультации, может подумать, что он или она имеет новый управляемый объект. Что-то вроде
// myEntity (passed as parameter does not become managed)
// Only the one returned by the merge operation is a managed entity
session.merge(myEntity);
// "newValue" is not commited because myEntity is not managed
myEntity.setMyProperty("newValue");
И если ваш постоянный контекст не содержит вашу сущность, возможно, вам не нужно выбирать поведение по умолчанию перед обновлением. Но этого можно избежать
- Добавить версию (@Version) столбца. 0 или NULL версия указывает, что экземпляр является новым и должен быть вставлен, а не обновлен
- Используйте перехватчик Hibernate
- Если вы уверены, что хотите обновить вместо вставки, вы можете использовать следующий подход
...
public void updateMyEntity(MyEntity updateableMyEntity);
// load does not hit the database
MyEntity myEntity = (MyEntity) session.load(MyEntity.class, updateableMyEntity.getId());
BeanUtils.copyProperties(myEntity, updateableMyEntity);
}
Таким образом, вы можете обновить вашу сущность без слияния или обновления метода. См. Этот вопрос для получения дополнительной информации: Лучший способ обновить некоторые поля отдельного объекта в Hibernate?
Используйте update(), если вы уверены, что сеанс не содержит уже существующего экземпляра с тем же идентификатором, и merge(), если вы хотите объединить свои изменения в любое время без учета состояния сеанса. Другими словами, update() обычно является первым методом, который вы вызываете в новом сеансе, гарантируя, что присоединение ваших отсоединенных экземпляров является первой выполняемой операцией.
SessionFactory factory = cfg.buildSessionFactory();
Session session1 = factory.openSession();
Student s1 = null;
Object o = session1.get(Student.class, new Integer(101));
s1 = (Student)o;
session1.close();
s1.setMarks(97);
Session session2 = factory.openSession();
Student s2 = null;
Object o1 = session2.get(Student.class, new Integer(101));
s2 = (Student)o1;
Transaction tx=session2.beginTransaction();
session2.merge(s1);
объяснение
Как видно из строк 4–7, мы только что загрузили один объект s1 в кэш сеанса 1 и закрыли сеанс 1 в строке 7, так что теперь объект s1 в кеше сеанса 1 будет уничтожен, поскольку срок действия кеша сеанса 1 истечет, когда мы скажем session1.close()
,
Теперь объект s1 будет находиться в некотором месте в ОЗУ, а не в кеше session1. Здесь s1 находится в отдельном состоянии, и в строке номер 8 мы изменили этот отдельный объект s1, теперь, если мы вызываем update()
Затем метод hibernate выдаст ошибку, потому что мы можем обновить объект только в сеансе.
Поэтому мы открыли еще один сеанс [session2] в строке № 10 и снова загрузили тот же объект ученика из базы данных, но с именем s2. Итак, в этой сессии2 мы назвали session2.merge(s1);
теперь в s2 объект s1 изменения будут объединены и сохранены в базе данных