Hibernate - коллекция с каскадом = "all-delete-orphan" больше не ссылалась на экземпляр объекта-владельца
У меня возникла следующая проблема при попытке обновить мою сущность:
"A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance".
У меня есть родительский объект, и у него есть Set<...>
некоторых дочерних объектов. Когда я пытаюсь обновить его, я получаю все ссылки на эти коллекции и устанавливаю его.
Следующий код представляет мое отображение:
@OneToMany(mappedBy = "parentEntity", fetch = FetchType.EAGER)
@Cascade({ CascadeType.ALL, CascadeType.DELETE_ORPHAN })
public Set<ChildEntity> getChildren() {
return this.children;
}
Я пытался очистить только Set <..>, в соответствии с этим: Как "возможно" решить проблему, но это не сработало.
Если у вас есть идеи, пожалуйста, дайте мне знать.
Спасибо!
Ответы
Ответ 1
На самом деле, моя проблема заключалась в равных и хэш-кодах моих сущностей. Унаследованный код может принести много проблем, никогда не забывайте его проверять. Все, что я сделал, это просто сохранить стратегию удаления-сирота и правильные равенства и хэш-код.
Ответ 2
Проверьте все места, где вы назначаете что-то для sonEntities. Ссылка, на которую вы ссылаетесь, четко указывает на создание нового HashSet, но вы можете получить эту ошибку в любое время при переназначении набора. Например:
public void setChildren(Set<SonEntity> aSet)
{
this.sonEntities = aSet; //This will override the set that Hibernate is tracking.
}
Обычно вы хотите только "новый" набор один раз в конструкторе. Каждый раз, когда вы хотите добавить или удалить что-то в списке, вам нужно изменить содержимое списка, а не назначать новый список.
Чтобы добавить детей:
public void addChild(SonEntity aSon)
{
this.sonEntities.add(aSon);
}
Чтобы удалить детей:
public void removeChild(SonEntity aSon)
{
this.sonEntities.remove(aSon);
}
Ответ 3
Метод:
public void setChildren(Set<SonEntity> aSet) {
this.sonEntities = aSet;
}
работает, если parentEntity
отсоединен, и снова, если мы обновляем его.
Но если сущность не отделена от контекста (то есть операции поиска и обновления выполняются в одной транзакции), работает приведенный ниже метод.
public void setChildren(Set<SonEntity> aSet) {
//this.sonEntities = aSet; //This will override the set that Hibernate is tracking.
this.sonEntities.clear();
if (aSet != null) {
this.sonEntities.addAll(aSet);
}
}
Ответ 4
Когда я читал в разных местах, которые спящий режим не любил, чтобы вы назначали коллекцию, я предположил, что наиболее безопасная вещь, очевидно, должна состоять в том, чтобы сделать ее окончательной:
class User {
private final Set<Role> roles = new HashSet<>();
public void setRoles(Set<Role> roles) {
this.roles.retainAll(roles);
this.roles.addAll(roles);
}
}
Однако это не работает, и вы получаете страшную ошибку "больше не ссылаетесь", которая на самом деле довольно вводит в заблуждение в этом случае.
Оказывается, что hibernate вызывает ваш метод setRoles И он хочет, чтобы его специальный класс коллекции был установлен здесь и не будет принимать ваш класс коллекции. Это заставило меня долго ждать, несмотря на то, что я прочитал все предупреждения о том, что вы не назначаете свою коллекцию в своем методе набора.
Итак, я изменил это:
public class User {
private Set<Role> roles = null;
public void setRoles(Set<Role> roles) {
if (this.roles == null) {
this.roles = roles;
} else {
this.roles.retainAll(roles);
this.roles.addAll(roles);
}
}
}
Таким образом, при первом вызове hibernate устанавливает свой специальный класс, а при последующих вызовах вы можете использовать этот метод самостоятельно, не разрушая все. Если вы хотите использовать свой класс как bean, вам, вероятно, нужен рабочий сеттер, и это, по крайней мере, кажется, работает.
Ответ 5
У меня была такая же ошибка. Проблема для меня заключалась в том, что после сохранения объекта сопоставленная коллекция была по-прежнему нулевой, и при попытке обновить сущность было выбрано исключение. Что помогло мне: сохранение объекта, затем обновление (сбор больше не равен нулю), а затем выполнить обновление. Возможно, инициализация коллекции новым ArrayList() или что-то может помочь.
Ответ 6
ТИП СВЯЗИ:
Не пытайтесь создать экземпляр коллекции, когда она объявлена в hasMany
, просто добавьте и удалите объекты.
class Parent {
static hasMany = [childs:Child]
}
ИСПОЛЬЗОВАНИЕ ТИП СВЯЗИ:
Но сбор может быть нулевым, только если объявлен как свойство (отношение использования) и не инициализируется в объявлении.
class Parent {
List<Child> childs = []
}
Ответ 7
У меня была эта проблема при попытке использовать TreeSet
. Я инициализировал oneToMany
с помощью TreeSet
, который работает
@OneToMany(mappedBy = "question", fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval=true)
@OrderBy("id")
private Set<WizardAnswer> answers = new TreeSet<WizardAnswer>();
Но это приведет к ошибке, описанной выше в question
. Таким образом, кажется, что hibernate
поддерживается SortedSet
, и если просто изменить строку выше на
@OneToMany(mappedBy = "question", fetch = FetchType.EAGER, cascade = { CascadeType.ALL }, orphanRemoval=true)
@OrderBy("id")
private SortedSet<WizardAnswer> answers;
он работает как магия:)
более подробная информация о hibernate SortedSet
может быть здесь
Ответ 8
Единственный раз, когда я получаю эту ошибку, я пытаюсь передать NULL в setter для коллекции. Чтобы предотвратить это, мои сеттеры выглядят так:
public void setSubmittedForms(Set<SubmittedFormEntity> submittedForms) {
if(submittedForms == null) {
this.submittedForms.clear();
}
else {
this.submittedForms = submittedForms;
}
}
Ответ 9
Я использовал @user2709454 подход с небольшим улучшением.
public class User {
private Set<Role> roles;
public void setRoles(Set<Role> roles) {
if (this.roles == null) {
this.roles = roles;
} else if(this.roles != roles) { // not the same instance, in other case we can get ConcurrentModificationException from hibernate AbstractPersistentCollection
this.roles.clear();
if(roles != null){
this.roles.addAll(roles);
}
}
}
}
Ответ 10
Добавление моего немого ответа. Мы используем Spring Data Rest. Это были наши довольно стандартные отношения. Шаблон использовался в другом месте.
//Parent class
@OneToMany(mappedBy = 'parent',
cascade= CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
List<Child> children = new LinkedList<>()
//Child class
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = 'ParentID', updatable = false)
@JsonBackReference
Parent parent
С созданной нами связью всегда было предусмотрено, что дети будут добавлены через свое собственное репо. Я еще не добавил репо. Тестирование интеграции, которое у нас было, проходило полный жизненный цикл объекта через вызовы REST, чтобы транзакции закрывались между запросами. Репо для ребенка не означало, что у json были дети как часть основной структуры, а не в _embedded
. После этого обновления родителям возникнут проблемы.
Ответ 11
Следующее решение сработало для меня
//Parent class
@OneToMany(mappedBy = 'parent',
cascade= CascadeType.ALL, orphanRemoval = true)
@OrderBy(value="ordinal ASC")
List<Child> children = new ArrayList<>()
//Updated setter of children
public void setChildren(List<Children> children) {
this.children.addAll(children);
for (Children child: children)
child.setParent(this);
}
//Child class
@ManyToOne
@JoinColumn(name="Parent_ID")
private Parent parent;
Ответ 12
Вместо назначения новой коллекции
public void setChildren(Set<ChildEntity> children) {
this.children = children;
}
Замените все элементы на
public void setChildren(Set<ChildEntity> children) {
Collections.replaceAll(this.children,children);
}
Ответ 13
Еще одной причиной может быть использование ломбок.
@Builder
- вызывает сохранение Collections.emptyList()
даже если вы говорите .myCollection(new ArrayList());
@Singular
- игнорирует значения по умолчанию на уровне класса и оставляет поле null
даже если поле класса было объявлено как myCollection = new ArrayList()
Мои 2 цента, просто провели 2 часа с таким же :)
Ответ 14
Я получал A collection with cascade="all-delete-orphan" was no longer referenced by the owning entity instance
когда я устанавливал parent.setChildren(new ArrayList<>())
. Когда я перешел на parent.getChildren().clear()
, это решило проблему.
Проверьте более подробную информацию: HibernateException - Коллекция с cascade = "all-delete-orphan" больше не упоминалась экземпляром объекта-владельца.
Ответ 15
будь осторожен с
BeanUtils.copyProperties(newInsum, insumOld,"code");
Этот метод тоже нарушает спящий режим.
Ответ 16
Я использую Spring Boot, и у меня возникла эта проблема с коллекцией, несмотря на то, что она не перезаписывалась напрямую, потому что я объявляю дополнительное поле для той же коллекции с помощью настраиваемого сериализатора и десериализатора, чтобы обеспечить более дружественное для внешнего интерфейса представление данные:
public List<Attribute> getAttributes() {
return attributes;
}
public void setAttributes(List<Attribute> attributes) {
this.attributes = attributes;
}
@JsonSerialize(using = AttributeSerializer.class)
public List<Attribute> getAttributesList() {
return attributes;
}
@JsonDeserialize(using = AttributeDeserializer.class)
public void setAttributesList(List<Attribute> attributes) {
this.attributes = attributes;
}
Кажется, что даже если я не перезаписываю коллекцию сам, десериализация делает это под капотом, вызывая эту проблему все же. Решение состояло в том, чтобы изменить установщик, связанный с десериализатором, чтобы он очищал список и добавлял все, а не перезаписывал его:
@JsonDeserialize(using = AttributeDeserializer.class)
public void setAttributesList(List<Attribute> attributes) {
this.attributes.clear();
this.attributes.addAll(attributes);
}
Ответ 17
Это может быть вызвано hibernate-enhance-maven-plugin
. Когда я включил свойство enableLazyInitialization
это исключение началось в моей ленивой коллекции. Я использую hibernate 5.2.17. Наконец.
Обратите внимание на две проблемы гибернации:
Ответ 18
@OneToMany(mappedBy = 'parent', cascade= CascadeType.ALL, orphanRemoval = true)
List<Child> children = new ArrayList<>()
Я столкнулся с той же ошибкой при добавлении дочернего объекта в существующий список дочерних объектов.
childService.saveOrUpdate(child);
parent.addToChildren(child);
parentService.saveOrUpdate(parent);
Что решило мою проблему: child = childService.saveOrUpdate(child);
Теперь ребенок оживает и с другими деталями, и он работал нормально.
Ответ 19
Мой был совершенно другим с Spring Boot! Для меня это не было связано с установкой свойства коллекции.
В своих тестах я пытался создать сущность и получал эту ошибку для другой неиспользованной коллекции!
После стольких попыток я просто добавил @Transactional
к тестовому методу, и он решил это. Хотя нет причины.