如何使用cascade =“all,delete-orphan”制作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.AB 

可以安全地假设发生此错误,因为我有一个在实体A中定义的B实体的集合:

      

怎么可能制作一个实体A的副本,包括它的实体B的集合,而没有hibernate对它不满意?

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

对我来说,将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克隆 。

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

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

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

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

关于集合重新分配问题,您不能将相同的B实例集合分配给两个不同的A ,因为它在逻辑上可以是many-to-many关联。 这也是您需要手动处理的事情,因为它特定于您的业务用例。

此外,请确保不要在其他实体实例中重用相同的集合代理,因为Hibernate会在内部将它们与原始父代绑定 – 始终在新实例中创建新集合(可能包含也可能不包含相同的元素)。

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

 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; }