Hibernate:更新()下的merge()的缺点

时间:2010-01-22 15:25:36

标签: java hibernate session

我遇到Hibernate抛出NonUniqueObjectException的问题。

阅读文档和this博文,我将来自update()的电话替换为merge(),并解决了问题。

我相信我理解异常的原因,以及为什么在断开连接的对象和会话边界方面更改方法解决了问题。

我的问题是:假设merge()将始终解析为会话对象,或者如果它不存在则检索它,调用merge()通常是比update()更安全的替代方法吗?

使用merge()而不是update()会有什么缺点?

3 个答案:

答案 0 :(得分:41)

  

调用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);

}

这样您就可以在没有合并或更新方法的情况下更新您的实体。有关详细信息,请参阅此问题:Best way to update some fields of a detached object on Hibernate ?

答案 1 :(得分:4)

如果您确定会话不包含具有相同标识符的已持久实例,则使用update()如果要在不考虑会话状态的情况下随时合并修改,请使用merge()。换句话说,update()通常是您在新会话中调用的第一个方法,确保重新附加已分离的实例是第一个执行的操作。

答案 2 :(得分:0)

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加载到session1缓存中,并在第7行关闭了session1,所以现在session1缓存中的对象s1将被销毁,因为当我们说{{{}时,session1缓存将到期。 1}}。

现在s1对象将位于某个RAM位置,而不是在session1缓存中。这里s1处于分离状态,而第8行我们修改了分离对象s1,现在如果我们调用session1.close()方法,那么hibernate会抛出一个错误,因为我们只能在会话中更新对象。

因此我们在第10行打开了另一个会话[session2],并再次从数据库加载了相同的学生对象,但名称为s2。所以在这个session2中,我们调用update()现在进入s2对象s1的更改将被合并并保存到数据库中