在Hibernate中使用瞬态实体来更新/合并现有的持久对象

时间:2013-03-15 14:54:23

标签: java hibernate merge xstream transient

我正在处理数据库中相当复杂的对象图。我正在使用XStream来序列化和反序列化这个对象图,它工作正常。当我导入数据库中存在的对象的对象图时,它最初是瞬态的,因为没有ID,而hibernate对此一无所知。然后我有业务逻辑,通过确定新瞬态导入对象中的哪些对象映射到现有持久对象,在我的对象图的部分上设置ID。然后我使用Hibernate的merge()和saveOrUpdate()。

一些伪代码让你更好地了解我在做什么:

ComplexObject transObj = xstream.import("object.xml");
ComplexObject persistObj = someService.getObjByName(transObj.getName());
for (OtherObject o : c.getObjects()) {
    if (persistObj.getObjects().contains(o.getName())) {
        o.setId(persistObj.getObjectByName(o.getName()).getId())
    }
    ... set a bunch of other IDs deeper in the object graph ...
}

transObj = session.merge(transObj);
session.saveOrUpdate(transObj);

现在这不起作用,因为我收到错误,例如:

   org.springframework.dao.InvalidDataAccessApiUsageException: deleted object would be re-saved by cascade (remove deleted object from associations): [com.......SomeObject#353296]; nested exception is org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): [com.......SomeObject#353296]

似乎hibernate merge并不意味着将瞬态对象与持久对象相关联。

有没有办法实现我想做的事情而不必在会话中获取持久对象,并修改它,而不是修改瞬态对象,并尝试保存并覆盖现有的持久对象?

2 个答案:

答案 0 :(得分:8)

  

似乎hibernate merge并不意味着关联瞬态   持久性的对象

Merge是JPA标准 - 将“新的和分离的实体实例”合并到“(持久性上下文 - )管理的实体实例”。将在标记为Cascade.MERGE或Cascade.ALL的FK关系中级联。

JPA 角度来看 - 不,合并并不意味着将您的Xstream流式瞬态对象与持久性对象关联。 JPA打算将合并与普通的JPA生命周期一起工作 - 创建新对象,持久化它们,获取/找到它们,分离它们,修改它们(包括添加新对象),合并它们,可选地修改它们,然后保存/保存它们。这是经过深思熟虑的设计,因此JPA是流线型的。 performant - 每个单独的对象持久存储到数据库之前不需要检索对象状态,以确定是否/插入/更新什么。 JPA持久性上下文对象状态已经包含足够的细节来确定这一点。

您的问题是您有新的实体实例,您想要将 分离 - 并且这不是JPA方式。

SaveOrUpdate是一个hibernate专有操作 - 如果一个实体有一个标识它会被更新,但是如果它没有实体那么它就会被插入。

hibernate 的角度来看 - 是的,合并后跟saveOrUpdate理论上可以用于将(Xstream流)临时对象关联到持久性,但是当与JPA操作结合使用时它可能有限制。 saveOrUpdate DOES在每个对象持久存在于数据库之前,带有检索以确定是否/插入/更新 - 它很聪明,但它不是JPA,并且它不是最高性能的。即,您应该能够使用一些小心和正确的配置 - 并在发生冲突时使用休眠操作而不是JPA操作。

  

我收到错误,例如:

     

org.hibernate.ObjectDeletedException:将重新保存已删除的对象   通过级联(从关联中删除已删除的对象):   [COM ....... SomeObject#353296]

我认为这可能是由两个因素造成的:

  • 在您的(Xstream创建的)对象图中的某处,如果您使用JPA进行(级联)检索,则会丢失一个子实体实例:这会删除对象
  • 在你的其他地方(Xstream创建的)对象图中,存在同一个子实体:这使得对象被重新保存

    如何调试:(a)从Xstream创建对象图并将其打印出来 - 每个实体,每个字段; (b)通过JPA加载相同的对象图,级联来自顶层实体的检索&将它打印出来 - 每个实体,每个领域(c)比较两个 - 是(a)中缺少的东西(b)中存在的东西

  

有没有办法实现我想做的事情而不必得到   会话中的持久对象,并修改它,而不是   修改瞬态,并尝试保存并覆盖   现有的持久性?

通过刚刚建议的调试/修复 - 希望。

或者我的蛮力建议(愚蠢地)支持这个错误(虽然减慢了性能):在对象图中填充ID后,调用EntityManager.clear(),然后继续merge()和saveOrUpdate()方法。但UNIT测试数据库结果 - 确保您没有忽略填充Xstream图形中的重要内容。

对于测试/作为最后的手段,尝试不带merge()的saveOrUpdate(),但是您可能会被迫清除()实体管理器以避免Hibernate异常,例如NonUniqueObjectException。

如果您了解更多/了解更多信息,请告知我们。^)

答案 1 :(得分:1)

    Hibernate merge was not meant for associating transient objects 
to persistent ones.

merge()理想情况下应该适用于您,因为您实际上正在处理分离的对象。因为你在' transObj'中设置了ID。在调用merge之前,hibernate会认为它们是分离的(不是瞬态的)。

我认为代码的问题在于它正在绊倒hibernate。

在您的代码中,您正在加载' persistObj'来自数据库。现在,hibernate持有这个'persistObj'在会议中。然后,您将设置一些来自' persistObj'在' transObj'然后调用merge。 “persistObj'中的一些子对象。和' transObj'拥有相同的ID,hibernate会感到困惑。

ComplexObject transObj = xstream.import("object.xml");
ComplexObject persistObj = someService.getObjByName(transObj.getName());
for (OtherObject o : c.getObjects()) {
    if (persistObj.getObjects().contains(o.getName())) {
        o.setId(persistObj.getObjectByName(o.getName()).getId())
     }
    ... set a bunch of other IDs deeper in the object graph ...
}
transObj = session.merge(transObj);
session.saveOrUpdate(transObj);

在加载' persistObj'之后尝试调用session.clear(),以便hibernate删除persistObj并仅考虑您的分离对象。

ComplexObject transObj = xstream.import("object.xml");
ComplexObject persistObj = someService.getObjByName(transObj.getName());
// Clear the session, so that hibernate removes 'persistObj' from it's cache
session.clear();
for (OtherObject o : c.getObjects()) {
    if (persistObj.getObjects().contains(o.getName())) {
        o.setId(persistObj.getObjectByName(o.getName()).getId())
     }
    ... set a bunch of other IDs deeper in the object graph ...
}
transObj = session.merge(transObj);
session.saveOrUpdate(transObj);
相关问题