I am trying to make a copy of a hibernate entity A like this:
A a = (A) session.get(A.class, id);
session.evict(a);
a.setId(null);
session.save(a);
This however does not work and i get the following expetion:
org.hibernate.HibernateException: Don't change the reference to a collection with cascade="all-delete-orphan": com.test.A.B
Its safe to assume this error happens because of i have a collection of B entity defined in entity A:
<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>
How is it possible to make a copy of entity A, including its collection of entitys B without hibernate being unhappy about it?
I had the same problem previously in my project.
For me it works, to set the ID of the list-entries to null.
// make copy a of aOriginal by using serialisation clone
a.setId(null);
for (B b : a.getBs()) {
b.setId(null);
}
session.save(a);
For the clone itself I used the Apache SerializationUtils clone.
The reason is, that hibernate tries to "reuse" already persisted list-entries for the "copied" entity. But this already persisted list-entries are linked to a original entity. So it tries to "change" the list itself in the entity, because the entity changed. But you can't change the list if it is annotated with "delete-orphans" (you only can modify the entries of the list, but not the list itself). So the exception is thrown.
If I set the list-entry-IDs to null, they will also be inserted newley. And the object hierarchy (instead of only the main entity) is duplicated. So the exeption is no longer thrown.
If you want to copy an existing entity - modify it - and save it as new entity, then you need to deep copy that object. You can do deep copy of a object by Serializing and then De-Serializing that object.
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;
}
There is no automatic way to copy objects like that. In general, the copied object could be associated with other objects which are further associated with other objects, etc, so you could end up copying a large portion of the database. Any tool you use for copying needs to be aware where in the object graph to stop copying, what not to copy (in terms of Hibernate entities those are ids, version columns and similar), and so on.
You can however utilize frameworks like Dozer
to avoid most of boilerplate code you would need to write manually.
Regarding collection reassignment issue, you cannot have the same collection of B
instances being assigned to two different A
s, as it would logically be a many-to-many
association then. It is also something you would need to take care of manually, as it is specific to your business use case.
Also, make sure you don't reuse the same collection proxies from one entity instance in others, as Hibernate ties them to their original parents internally - always create a new collection in new instances (which may or may not contain the same elements).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With