在 Hibernate 中重新附加分离对象的正确方法是什么?

2022-08-31 06:10:16

我遇到这样的情况:我需要将分离的对象重新附加到休眠会话,尽管会话中可能已经存在具有相同标识的对象,这将导致错误。

现在,我可以做两件事之一。

  1. getHibernateTemplate().update( obj )当且仅当休眠会话中尚不存在对象时,此方法才有效。当我以后需要时,会引发异常,指出会话中已存在具有给定标识符的对象。

  2. getHibernateTemplate().merge( obj )当且仅当休眠会话中存在对象时,此方法才有效。当我需要稍后将对象放在会话中时,如果我使用它,则会引发异常。

给定这两种情况,如何一般地将会话附加到对象?我不想用异常来控制这个问题的解决方案的流程,因为一定有一个更优雅的解决方案......


答案 1

因此,似乎没有办法在JPA中重新附加一个过时的独立实体。

merge()将过时状态推送到数据库,并覆盖任何干预更新。

refresh()不能在分离的实体上调用。

lock()不能在分离的实体上调用,即使它可以,它确实重新附加了实体,用参数“LockMode.NONE”调用“lock”,暗示你正在锁定,但不是锁定,这是我见过的最违反直觉的API设计。

所以你被困住了。有一种方法,但没有或 .对象生命周期中的明显步骤对您不可用。detach()attach()reattach()

从关于JPA的类似问题的数量来看,似乎即使JPA确实声称有一个连贯的模型,它也肯定与大多数程序员的心智模型不匹配,他们被诅咒浪费了很多时间试图了解如何让JPA做最简单的事情,并最终在他们的应用程序中缓存管理代码。

似乎唯一的方法是丢弃过时的分离实体,并使用相同的ID执行查找查询,这将命中L2或DB。

米克


答案 2

所有这些答案都忽略了一个重要的区别。update() 用于(重新)将对象图附加到会话。您传递给它的对象是托管的对象。

merge() 实际上不是一个(重新)附件 API。通知 merge() 有返回值?这是因为它返回托管图形,该图形可能不是您传递的图形。merge() 是一个 JPA API,其行为由 JPA 规范控制。如果您传递给merge()的对象已经被管理(已经与会话相关联),那么这就是Hibernate使用的图形;传入的对象是从 merge() 返回的同一对象。但是,如果传递给 merge() 的对象已分离,则 Hibernate 会创建一个托管的新对象图,并将状态从分离的图形复制到新的托管图上。同样,这一切都由 JPA 规范规定和管理。

就“确保此实体得到管理,或使其得到管理”的通用策略而言,这取决于您是否也要考虑尚未插入的数据。假设你这样做,使用类似的东西

if ( session.contains( myEntity ) ) {
    // nothing to do... myEntity is already associated with the session
}
else {
    session.saveOrUpdate( myEntity );
}

注意我使用了 saveOrUpdate() 而不是 update()。如果您不希望在此处处理尚未插入的数据,请改用 update() ...


推荐