Hibernate - коллекция с каскадом = ”all-delete-orphan” больше не ссылалась на экземпляр объекта-владельца


225

У меня возникла следующая проблема при попытке обновить мою сущность:

"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
@ mel3kings, указанная вами ссылка больше не активна.
опал


попробуйте использовать изменяемые коллекции при удалении элементов. Например, не используйте, something.manyother.remove(other)если manyotherэто List<T>. Сделайте много других изменчивых, как ArrayList<T>и использоватьorphanDelete = true
nurettin

Ответы:


220

Проверьте все места, где вы назначаете что-то 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);
}

8
На самом деле, моя проблема была о равных и хэш-коде моих сущностей. Устаревший код может принести много проблем, не забудьте проверить его. Все, что я сделал, это просто сохранил стратегию удаления-сироты и исправил «равно» и хэш-код.
Axcdnt

6
Я рад, что ты решил свою проблему. equals и hashcode меня укусили несколько раз в Hibernate. Вместо того, чтобы обновлять заголовок вопроса с помощью «[Решено]», вы должны опубликовать свой ответ и пометить его как принятый ответ.
brainimus

Спасибо, я столкнулся с чем-то похожим и оказалось, что мой сеттер для этого Сета пуст.
Сиддхартха

да, даже в пустых списках вы получаете эту ошибку. я не ожидал бы этого, потому что нет сирот (список был пуст).
Тиби

4
Обычно вы хотите «новый» набор только один раз в конструкторе. Каждый раз, когда вы хотите добавить или удалить что-то в списке, вы должны изменить содержимое списка, а не назначать новый список. Самый бес
Нихил Саху

109

Метод:

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

1
@Skuld У меня похожая проблема, и я применил ваше решение (в методе сеттера я очищаю коллекцию дочерних объектов - this.children.clear () - и добавил новые дочерние элементы - this.children.addAll (children)). Это изменение не решило мою проблему. Я все еще получаю исключение "Коллекция с cascade =" all-delete-orphan "больше не ссылается на экземпляр экземпляра объекта-владельца. У тебя есть идея почему? Большое спасибо!
ovdsrn

@ovdsrn Извините, это не мой ответ, я только разобрался с форматированием ответа, оригинальный автор (kmmanu) мог бы помочь вам (или, возможно, вы захотите начать новый вопрос, если ваш сценарий отличается от оригинальный вопрос, заданный здесь) Удачи
Skuld

1
Это работает хорошо, но не так хорошо, когда дочерние элементы содержатся во вложенном компоненте гибернации, и компонент становится пустым в своем Entity. Тогда вы получите ту же ошибку. Это связано с тем, что владельцем дочерних элементов является корневая сущность, а не компонент, для которого задано значение NULL ... Таким образом, компоненту никогда не разрешается принимать значение NULL, а скорее должен привести к перенаправлению в метод destroy () в Дитя ... По крайней мере, я не знаю лучшего решения ... Это своего рода конструкция коллекции clear (), но затем для компонента с помощью destroy () ...
edbras

См. Также этот пост для более подробной информации выше: stackoverflow.com/questions/4770262/…
edbras

7
используйте this.sonEntities.retainAll (aSet) вместо sonEntities.clear (), потому что если aSet == this.sonEntities (то есть тот же объект), вы очистите набор перед добавлением в него!
Мартин

33

Когда я читал в разных местах, что hibernate не хотел, чтобы вы назначали коллекцию, я предполагал, что самым безопасным, очевидно, было бы сделать его окончательным, как это:

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 и хочет, чтобы здесь был установлен специальный класс коллекции, и не будет принимать ваш класс коллекции. Это поставило меня в тупик на долгое время, несмотря на то, что я прочитал все предупреждения о не назначении вашей коллекции в вашем методе set.

Итак, я изменился на это:

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-компонента, вам, вероятно, нужен работающий установщик, и это, по крайней мере, похоже, работает.


2
Почему вы вызываете retainAll ()? Почему не clear () и addAll ()?
edbras

1
«Почему вы вызываете retainAll ()? Почему не clear (), а затем addAll ()?» Хороший вопрос, я думаю, я работал в предположении, что hibernate с меньшей вероятностью увидит это как обновление базы данных, если вы не удалили элементы перед их повторным добавлением. Но я подозреваю, что так или иначе это не работает.
xpusostomos

2
Вы должны использовать retainAll () вместо clear (), иначе вы можете уничтожить роли, если вам случится передать идентичный набор объектов. Например: user.setRoles (user.getRoles ()) == user.roles.clear ()
Мартин

2
Когда вы устанавливаете orphanRemoval = true и создаете запись, в которой эта коллекция равна нулю, вы также получаете эту ошибку. Итак: a имеет oneToMany b с orphanremoval = true. Когда вы создаете A, где B = null, эта проблема вызывается. Ваше решение инициализировать и сделать его окончательным кажется лучшим.
Лоуренс

Спасибо! Я инициализировал мой список как List<String> list = new ArrayList<>();. Изменяя его, чтобы List<String> list = null;решить проблему :)
Радикальный

19

На самом деле, моя проблема была о равных и хэш-коде моих сущностей. Устаревший код может принести много проблем, не забудьте проверить его. Все, что я сделал, это просто сохранил стратегию удаления-сироты и исправил равно и хэш-код.


9
пожалуйста, пример хорошо сделанных методов equals и hashCode? потому что у меня много проблем: или я не могу обновить свой набор, или я получаю ошибку StackOverflow. я открыл вопрос здесь: stackoverflow.com/questions/24737145/… . спасибо
SaganTheBest

11

У меня была такая же ошибка. Проблема для меня заключалась в том, что после сохранения сущности сопоставленная коллекция была все еще нулевой, и при попытке обновить сущность было сгенерировано исключение. Что мне помогло: сохранение сущности, затем обновление (коллекция больше не равна нулю) и затем обновление. Может быть, инициализация коллекции с новым ArrayList () или что-то может также помочь.


Отправка нового ArrayList вместо NULL, работал для меня. Спасибо
rpajaziti

4

ИМЕЕТ ОТНОШЕНИЕ ТИПА:


Не пытайтесь создавать экземпляр коллекции, когда она объявлена hasMany, просто добавляйте и удаляйте объекты.

class Parent {
    static hasMany = [childs:Child]
}

ТИП ИСПОЛЬЗОВАНИЯ:


Но коллекция может быть нулевой только тогда, когда она объявлена ​​как свойство (отношение использования) и не инициализирована в объявлении.

class Parent {
    List<Child> childs = []
}

4

Я использовал @ 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);
            }
        }
    }
}

3

У меня была эта проблема при попытке использования 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можно здесь


Возможно, для пользователя Collections.emptySet () лучше использовать одиночный пустой список
Рыжман

3

Единственный раз, когда я получаю эту ошибку, это когда я пытаюсь передать NULL в сеттер для коллекции. Чтобы предотвратить это, мои сеттеры выглядят так:

public void setSubmittedForms(Set<SubmittedFormEntity> submittedForms) {
    if(submittedForms == null) {
        this.submittedForms.clear();
    }
    else {
        this.submittedForms = submittedForms;
    }
}

3

Я столкнулся с этим при обновлении сущности с помощью пост-запроса JSON. Ошибка произошла, когда я обновил сущность без данных о дочерних элементах, даже когда их не было. Добавление

"children": [],

чтобы тело запроса решило проблему.


1

Еще одной причиной может быть использование ломбок.

@Builder- причины для сохранения, Collections.emptyList()даже если вы говорите.myCollection(new ArrayList());

@Singular- игнорирует значения класса по умолчанию и покидает поле, nullдаже если поле класса было объявлено какmyCollection = new ArrayList()

Мои 2 цента, просто провели 2 часа с таким же :)


1

Я получал, 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" больше не упоминалась экземпляром объекта-владельца .


1

Я использую 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);
  }

1

У меня была та же самая проблема, но это было, когда набор был нулевым. Только в коллекции Set в списке работы найди. Вы можете попробовать аннотацию в спящем режиме @LazyCollection (LazyCollectionOption.FALSE), вставленную из аннотации JPA fetch = FetchType.EAGER.

Мое решение: это моя конфигурация и отлично работает

@OneToMany(mappedBy = "format", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
private Set<Barcode> barcodes;

@OneToMany(mappedBy = "format", cascade = CascadeType.ALL, orphanRemoval = true)
@LazyCollection(LazyCollectionOption.FALSE)
private List<FormatAdditional> additionals;

1
@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);

Теперь ребенок оживает и с другими деталями, и он работал нормально.


0

Добавляю мой тупой ответ. Мы используем 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 дети были частью основной структуры, а не in _embedded. Обновление родительского элемента может вызвать проблемы.


0

Следующее решение сработало для меня

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

0

Вместо назначения новой коллекции

public void setChildren(Set<ChildEntity> children) {
    this.children = children;
}

Заменить все элементы на

public void setChildren(Set<ChildEntity> children) {
    Collections.replaceAll(this.children,children);
}


0

Это может быть вызвано hibernate-enhance-maven-plugin. Когда я включил enableLazyInitializationсвойство, это исключение началось в моей ленивой коллекции. Я использую hibernate 5.2.17. Наконец.

Обратите внимание на две проблемы гибернации:


0

Мой был совершенно другим с Spring Boot! Для меня это не было связано с установкой свойства коллекции.

В своих тестах я пытался создать сущность и получал эту ошибку для другой неиспользованной коллекции!

После стольких попыток я просто добавил @Transactionalтестовый метод, и он решил это. Хотя нет причины.


0

Это в отличие от предыдущих ответов, у меня была точно такая же ошибка: «Коллекция с cascade =” all-delete-orphan ”больше не упоминалась ....», когда моя функция установки выглядела так:

public void setTaxCalculationRules(Set<TaxCalculationRule> taxCalculationRules_) {
    if( this.taxCalculationRules == null ) {
        this.taxCalculationRules = taxCalculationRules_;
    } else {
        this.taxCalculationRules.retainAll(taxCalculationRules_);
        this.taxCalculationRules.addAll(taxCalculationRules_);
    }
}

И затем он исчез, когда я изменил его на простую версию:

public void setTaxCalculationRules(Set<TaxCalculationRule> taxCalculationRules_) {
    this.taxCalculationRules = taxCalculationRules_;
}

(Версии hibernate - пробовал и 5.4.10 и 4.3.11. Потратил несколько дней, пробуя всевозможные решения, прежде чем вернуться к простому назначению в установщике. Теперь смущен, почему это так.)

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.