我必须在我的项目中使用Hibernate 4.1.7。不幸的是,我不能转向更新的版本。
在我的背景下,我有一个掌握细节的情况。因此,我加载主对象并在网页上显示它的所有细节。我可以按照自己喜欢的方式更改主人和详细信息,甚至包括新的细节。我的保存代码如下:
@Override
@Transactional
public void save(Entity entity) {
Session session = entityManager.unwrap(Session.class);
session.saveOrUpdate(entity);
session.flush();
}
当我尝试删除细节时出现问题。下面的代码显示了它是如何完成的:
myMaster.getDetails().remove(myDetail);
我希望Hibernate会跟踪主对象的更改(它从列表成员中丢失了一个详细信息实例),但是当我调用save()
方法时,它会抛出java.lang.IllegalArgumentException: Removing a detached instance xxxx#yyyy
。
我理解这个概念,如果试图删除一个分离的实例,但我不明白为什么刚刚删除的实例被Hibernate认为是分离的。
有什么想法吗?
TIA!
答案 0 :(得分:5)
所有你需要做的就是让它按照你期望的方式工作(让Hibernate检查整个对象图)是这样的:
@Override
@Transactional
public Entity save(Entity entity) {
return entityManager.merge(entity);
}
当然,请确保将MERGE
操作从主数据库级联到详细信息(CascadeType.MERGE
或CascadeType.ALL
),并设置orphanRemoval
以进行详细信息收集。如果orphanRemoval
不合适,那么你必须明确地删除细节(否则Hibernate会在他们停止与父母联系时删除相关的孩子,这显然是不可取的)。
此外,请确保之后使用merge
操作的结果,因为它始终返回传入的分离实例的副本(传入的实例未被修改)。这在持久化新实例时尤其重要,因为在副本中设置了ID。
但是,您需要支付从db重新加载对象图的惩罚,但是,如果您希望自动完成所有操作,除了将其与db中的当前状态进行比较之外,Hibernate没有其他方法可以检查实际更改的内容。
现在解释你在尝试中观察到的内容:
您保存的主实例已分离。您可以在一个事务(持久性上下文/会话)中加载它,并将其保存在另一个事务中。
更新时,saveOrUpdate
可以正常使用:
保存(Object)或更新(Object)给定实例,具体取决于 解决未保存价值检查(参见手册) 关于未保存值检查的讨论。)
<强>参数:强>
object - 包含新状态或更新状态的瞬态或分离实例
如文档中所述,它旨在使用分离和瞬态实例。
您尝试合并,但是有一个例外,告诉您已经有一个具有相同ID的对象。唯一的解释是你尝试过这样的事情:
@Override
@Transactional
public void save(Entity entity) {
entity = entityManager.merge(entity);
Session session = entityManager.unwrap(Session.class);
session.saveOrUpdate(entity);
session.flush();
}
然后抛出异常,如update
javadoc:
使用给定的标识符更新持久化实例 分离的实例。 如果存在具有相同的持久性实例 标识符,抛出异常。
因此,您将实例合并到当前持久性上下文中,然后尝试saveOrUpdate
它(委托给update
)并导致异常,因为您不能更新分离的实例虽然在当前的持久化上下文中已经有一个持久化的(否则持久的上下文会变得陈旧)。
您不必执行此操作,只需合并,并在事务结束时Hibernate将脏对象并自动将更改刷新到db。