如何使用 cascade=“all,delete-orphan” 复制休眠集合

2022-09-04 23:01:17

我正在尝试制作休眠实体A的副本,如下所示:

A a = (A) session.get(A.class, id);
session.evict(a);
a.setId(null);
session.save(a);

然而,这不起作用,我得到以下解释:

org.hibernate.HibernateException: Don't change the reference to a collection with cascade="all-delete-orphan": com.test.A.B

可以安全地假设会发生此错误,因为i在实体A中定义了B实体的集合:

<list name="B"  table="B" lazy="false" cascade="all,delete-orphan">
    <key column="A_ID" not-null="true"/>
    <index column="X"/>            
    <one-to-many class="com.test.B"/>
</list>

如何制作实体A的副本,包括其实体B的集合,而不会休眠而不为此感到不满?


答案 1

如果要复制现有实体 ( 修改它 - 并将其另存为新实体,则需要深度复制该对象。可以通过序列化和反序列化对象来执行对象的深度复制。

public Object deepCopy(Object input) {

    Object output = null;
    try {
        // Writes the object
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(input);

        // Reads the object
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        output = objectInputStream.readObject();

    } catch (Exception e) {
        e.printStackTrace();
    }
    return output;
}

答案 2

我以前在我的项目中遇到过同样的问题。

对我来说,将列表条目的ID设置为null是有效的。

// make copy a of aOriginal by using serialisation clone 
a.setId(null);
for (B b : a.getBs()) {
    b.setId(null);
}
session.save(a);

对于克隆本身,我使用了Apache SerializationUtils克隆

原因是,休眠尝试“重用”已保留的“实体的列表条目。但是,这个已经持久保存的列表条目链接到原始实体。因此,它试图“更改”实体中的列表本身,因为实体已更改。但是,如果列表带有“删除孤立项”注释,则无法更改列表(您只能修改列表的条目,而不能修改列表本身)。因此,将引发异常。

如果我将列表条目ID设置为null,它们也会入newley。对象层次结构(而不仅仅是主实体)是重复的。因此,不再抛出执行。


推荐