如何使用cascade =" all,delete-orphan"

时间:2017-03-22 12:58:24

标签: java hibernate

我正在尝试制作一个像这样的hibernate实体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

可以安全地假设发生此错误,因为我有一个在实体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的集合,而没有hibernate对它不满意呢?

3 个答案:

答案 0 :(得分:4)

我之前在项目中遇到了同样的问题。

对我而言,将list-entries的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 clone

原因是,hibernate尝试重用&#34;已经保留了&#34;复制&#34;的列表条目实体。但是这个已经存在的列表条目链接到原始实体。所以它试图改变&#34;列表本身在实体中,因为实体已更改。但如果使用&#34; delete-orphans&#34;进行注释,则无法更改列表。 (您只能修改列表的条目,但不能修改列表本身)。所以抛出异常。

如果我将list-entry-IDs设置为null,它们也将被插入newley。并且对象层次结构(而不​​仅仅是主实体)是重复的。所以不再抛出这个例子。

答案 1 :(得分:1)

没有自动方式来复制这样的对象。通常,复制的对象可以与进一步与其他对象相关联的其他对象相关联,因此您最终可能会复制数据库的大部分内容。您用于复制的任何工具都需要知道对象图中停止复制的位置,不复制的内容(就Hibernate实体而言,它们是ID,版本列和类似内容)等等。

但是,您可以使用Dozer之类的框架来避免手动编写的大部分样板代码。

关于集合重新分配问题,您不能将grep -rl '\.delay' | xargs sed -i ... 个实例的同一集合分配给两个不同的B,因为它在逻辑上可能是A关联。这也是您需要手动处理的事情,因为它特定于您的业务用例。

此外,请确保您不会在其他实体实例中重复使用相同的集合代理,因为Hibernate会在内部将它们与原始父项联系起来 - 始终在新实例中创建新集合(可能包含也可能不包含相同的元素)。

答案 2 :(得分: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;
}
相关问题