Спящий режим: нижняя сторона слияния() над обновлением()
У меня возникают проблемы с NonUniqueObjectException
, сбрасываемыми с помощью Hibernate.
Чтение документов и это сообщение в блоге, я заменил вызов от update()
до merge()
, и он решил проблему.
Я считаю, что понимаю причину исключения, и почему изменение метода устранило проблему в терминах отключенных объектов и границ сеанса.
Мой вопрос: учитывая, что merge()
всегда будет разрешать объект сеанса или получить его, если он не существует, вызывает слияние merge(), как правило, более безопасную альтернативу, чем update()
?
В чем недостаток использования merge()
над update()
?
Ответы
Ответ 1
Является ли вызов merge() обычно более безопасной альтернативой, чем update()?
Как исключить исключение NonUniqueObjectException, да. Я думаю, это объясняет, почему JPA не разрешает метод обновления.
Что является недостатком использования функции merge() over 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");
И если ваш контекст persistence не содержит вашу сущность, , возможно, вы не хотите, чтобы поведение по умолчанию было выбрано до-обновления. Но этого можно избежать
- Добавить столбец версии (@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?
Ответ 2
Используйте update(), если вы уверены, что сессия не содержит уже сохраняющийся экземпляр с тем же идентификатором и merge(), если вы хотите объединить свои изменения в любое время без учета состояния сеанса. Другими словами update() обычно является первым методом, который вы вызывали бы в новом сеансе, гарантируя, что повторная привязка ваших отдельных экземпляров является первой выполняемой операцией.
Ответ 3
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 и закрыли session1 в строке 7, так что теперь объект s1 в кеше session1 будет уничтожен, поскольку кеш сеанса1 истечет, когда мы говорим session1.close()
.
Теперь объект s1 будет находиться в некотором местоположении RAM, а не в кеше session1. Здесь s1 находится в состоянии отсоединения, а в строке 8 мы модифицировали этот отдельный объект s1, теперь, если мы вызываем метод update()
, тогда hibernate выдает ошибку, потому что мы можем обновлять объект только в сеансе.
Итак, мы открыли еще один сеанс [session2] в строке 10 и снова загрузили тот же объект-ученик из базы данных, но с именем s2. Итак, в этом сеансе2 мы вызвали session2.merge(s1);
теперь в s2-объект. Изменения s1 будут объединены и сохранены в базе данных