如何使Hibernate 4停止抛出"删除分离的实例"?

Ale*_*xSC 3 java hibernate

我必须在我的项目中使用Hibernate 4.1.7.不幸的是,我不能转向更新的版本.

在我的背景下,我有一个主要的细节情况.因此,我加载主对象并在网页上显示它的所有细节.我可以按照自己喜欢的方式更改主人和详细信息,甚至包括新的细节.我的保存代码如下:

@Override
@Transactional
public void save(Entity entity) {
    Session session = entityManager.unwrap(Session.class);
    session.saveOrUpdate(entity);
    session.flush();
}
Run Code Online (Sandbox Code Playgroud)

当我尝试删除细节时出现问题.下面的代码显示了它是如何完成的:

myMaster.getDetails().remove(myDetail);
Run Code Online (Sandbox Code Playgroud)

我希望Hibernate将跟踪主对象的更改(它从列表成员中丢失了一个详细实例),但是当我调用save()它抛出的方法时java.lang.IllegalArgumentException: Removing a detached instance xxxx#yyyy.

我理解这个概念,如果试图删除一个分离的实例,但我不明白为什么刚刚删除的实例被Hibernate认为是分离的.

有任何想法吗?

TIA!

Dra*_*vic 5

你需要做的就是让它按照你期望的方式工作(让Hibernate检查整个对象图)是这样的:

@Override
@Transactional
public Entity save(Entity entity) {
    return entityManager.merge(entity);
}
Run Code Online (Sandbox Code Playgroud)

当然,请确保MERGE操作从主服务器级联到详细信息(CascadeType.MERGECascadeType.ALL),orphanRemoval并设置为详细信息收集.如果orphanRemoval不合适,那么你必须明确地删除细节(否则Hibernate会在他们停止与父母联系时删除相关的孩子,这显然是不可取的).

此外,请确保之后使用merge操作的结果,因为它始终返回传入的分离实例的副本(传入的实例未被修改).这在持久化新实例时尤其重要,因为在副本中设置了ID.

但是,您需要支付从db重新加载对象图的惩罚,但是,如果您希望自动完成所有操作,除了将其与db中的当前状态进行比较之外,Hibernate没有其他方法可以检查实际更改的内容.

现在解释你在尝试中观察到的内容:

  1. 您保存的主实例已分离.您在一个事务(持久性上下文/会话)中加载它,并将其保存在另一个事务中.

  2. saveOrUpdate更新时它可以正常工作:

    保存(对象)或更新(对象)给定实例,具体取决于未保存值检查的分辨率(有关未保存值检查的讨论,请参阅手册).

    参数:

    object - 包含新状态或更新状态的瞬态或分离实例

    如文档中所述,它旨在处理分离和瞬态实例.

  3. 您尝试合并,但是有一个例外,告诉您已经有一个具有相同ID的对象.唯一的解释是你尝试过这样的事情:

    @Override
    @Transactional
    public void save(Entity entity) {
        entity = entityManager.merge(entity);
        Session session = entityManager.unwrap(Session.class);
        session.saveOrUpdate(entity);
        session.flush();
    }
    
    Run Code Online (Sandbox Code Playgroud)

    然后抛出异常,如updatejavadoc 中所述:

    使用给定分离实例的标识符更新持久性实例.如果存在具有相同标识符的持久性实例,则抛出异常.

    因此,您将实例合并到当前持久性上下文中,然后尝试使用saveOrUpdate它(委派给它update)并导致异常,因为当前持久化上下文中已存在持久实例时,您不得更新分离实例(否则持久的会变得陈旧).

    您不必这样做,只需合并,并在事务结束时Hibernate将脏对象并自动将更改刷新到db.