Как сделать копию коллекции спящего режима с помощью cascade = "all, delete-orphan"
Я пытаюсь сделать копию объекта спящего режима 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
Безопасно предположить, что эта ошибка происходит из-за того, что у меня есть совокупность объекта B, определенного в сущности 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>
Как можно сделать копию сущности A, включая ее коллекцию сущностей B без спящего режима, которая была недовольна этим?
Ответы
Ответ 1
У меня была такая же проблема ранее в моем проекте.
Для меня это работает, чтобы установить идентификатор списка-записей в значение 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 пытается "повторно использовать" уже сохраненные записи списка для "скопированного" объекта. Но эти уже сохраненные записи списка связаны с исходным объектом. Поэтому он пытается "изменить" сам список в сущности, потому что сущность изменилась. Но вы не можете изменить список, если он аннотируется с помощью "delete-orphans" (вы можете только изменить записи списка, но не сам список). Таким образом, исключение выбрано.
Если я установил идентификаторы списка-списка равными нулю, они также будут вставлены newley. И иерархия объектов (а не только основная сущность) дублируется. Таким образом, освобождение больше не бросается.
Ответ 2
Нет автоматического способа копирования таких объектов. В общем случае скопированный объект может быть связан с другими объектами, которые в дальнейшем связаны с другими объектами и т.д., Поэтому вы можете копировать большую часть базы данных. Любой инструмент, который вы используете для копирования, должен знать, где в графе объектов прекратить копирование, что не копировать (в терминах объектов Hibernate - это идентификаторы, столбцы версий и т.д.) И т.д.
Однако вы можете использовать фреймворки, такие как Dozer
, чтобы избежать большей части кода шаблона, который вам нужно будет писать вручную.
Что касается проблемы переназначения коллекции, вы не можете иметь один и тот же набор экземпляров B
, назначаемых двум различным A
s, так как это логически является ассоциацией many-to-many
. Это также то, что вам нужно будет позаботиться вручную, так как оно специфично для вашего бизнес-использования.
Кроме того, убедитесь, что вы не повторно используете одни и те же прокси файлы из одного экземпляра объекта в других, поскольку Hibernate связывает их со своими родителями внутри себя - всегда создавайте новую коллекцию в новых экземплярах (которые могут содержать или не содержать одинаковые элементы).
Ответ 3
Если вы хотите скопировать существующий объект - изменить его - и сохранить его как новый объект, то вам нужно глубоко скопировать этот объект. Вы можете выполнить глубокую копию объекта путем сериализации, а затем де-сериализовать этот объект.
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;
}