Hibernate - Збірка з cascade = "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це a List<T>. Зробіть багато інших змінних, як ArrayList<T>і користуйтесяorphanDelete = true
Нуреттін

Відповіді:


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
Я радий, що ти вирішив свою проблему. рівний і хеш-код покусав мене кілька разів зі сплячого. Замість того, щоб оновлювати назву питання за допомогою "[Розв’язано]", слід заздалегідь і опублікувати свою відповідь, а потім позначити її як прийняту відповідь.
brainimus

Дякую, я зіткнувся з чимось подібним і виявився, що мій сетер виявився порожнім ..
Siddhartha

так, навіть у порожніх списках ви отримуєте цю помилку. я б не очікував цього, оскільки немає сиріт (список був порожнім).
тибі

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 У мене є подібна проблема, і я застосував ваше рішення (в методі setter я очищаю дитячу колекцію - this.children.clear () - і додав нових дітей - this.children.addAll (діти)). Ця зміна не вирішила моєї проблеми. Я все одно отримую виняток "Колекція з cascade =" all-delete-orphan "більше не посилалася на екземпляр власного об'єкта". У вас є ідея, чому? Велике спасибі!
ovdsrn

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

1
Це працює добре, але не так добре, коли діти містяться в вкладеному сплячому компоненті і компонент стає недійсним у своїй Entity. Тоді ви отримуєте ту саму помилку. Це тому, що власник дітей - це коренева сутність, а не той компонент, який робиться недійсним ... Такий компонент ніколи не може стати нульовим, а, скоріше, повинен призвести до переходу на метод знищення () в дитина ... Принаймні, я не знаю кращого рішення ... Це своєрідна конструкція колекції ясно (), але потім про компонент через знищити () ...
edbras

Дивіться також цей пост для більш докладної інформації про вище: stackoverflow.com/questions/4770262 / ...
edbras

7
використовуйте this.sonEntities.retainAll (aSet) над sonEntities.clear (), тому що якщо aSet == this.sonEntities (тобто той самий об’єкт), ви очистите набір перед тим, як додати його до нього!
Мартін

33

Коли я читав у різних місцях, які впадають у сплячку, тобі не призначається колекція, я припускав, що найбезпечніше, що потрібно зробити, очевидно, було б зробити її остаточним:

class User {
  private final Set<Role> roles = new HashSet<>();

public void setRoles(Set<Role> roles) {
  this.roles.retainAll(roles);
  this.roles.addAll(roles);
}
}

Однак це не працює, і ви отримуєте жахливу помилку "більше не посилається", що насправді є досить оманливим у цьому випадку.

Виявляється, сплячий називає ваш метод 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);
  }
}
}

Так що при першому дзвінку сплячка встановлює свій спеціальний клас, а при наступних дзвінках ви можете використовувати метод самостійно, не руйнуючи все. Якщо ви хочете використовувати свій клас як боб, вам, напевно, потрібен робочий сетер, і це, принаймні, спрацює.


2
Чому ви викликаєте retainAll ()? Чому не чітко (), а за ним додається addAll ()?
edbras

1
"Чому ви викликаєте retainAll ()? Чому не clear (), а за ним додається addAll ()?" Хороше запитання, я думаю, що я працював при припущенні, що сплячий стан буде рідше сприймати це як оновлення бази даних, якщо ви не видалите елементи, перш ніж додавати їх знову. Але я підозрюю, що це все одно не працює.
xpusostomos

2
Вам слід використовувати retainAll () over 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
будь ласка, приклад добре зробленого рівняння та методів 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 post. Помилка сталася, коли я оновлював об'єкт без даних про дітей, навіть коли таких не було. Додавання

"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

У мене була така ж проблема, але це було, коли набір був недійсним. Лише у колекції "Набір" у "Список" знайдіть роботу. Ви можете спробувати передати сплячу анотацію @LazyCollection (LazyCollectionOption.FALSE), вкладену в групу анотацій JPA = 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властивість, цей виняток почався з моєї лінивої колекції. Я використовую сплячку 5.2.17.Закінчення.

Зверніть увагу на ці два сплячі проблеми:


0

Моя була зовсім інша з весняним черевиком! Для мене це було не через встановлення властивостей колекції.

У своїх тестах я намагався створити об'єкт і отримував цю помилку для іншої колекції, яка не використовувалася!

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


0

Це на відміну від попередніх відповідей, у мене була точно така ж помилка: "Колекція з cascade =" all-delete-сирота "більше не посилалася ....", коли моя функція сеттера виглядала так:

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

(сплячі версії - спробували і 5.4.10, і 4.3.11. Провели кілька днів, намагаючись усілякі рішення, перш ніж повернутися до простого завдання в сетері. Тепер плутати, чому це так.)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.