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 к тестовому методу, и он решил это. Хотя нет причины.