JPA OneToMany не видаляє дочірню


158

У мене проблема з простим @OneToManyвідображенням між батьком і дочірнім об'єктом. Все працює добре, лише те, що дочірні записи не видаляються, коли я видаляю їх із колекції.

Батько:

@Entity
public class Parent {
    @Id
    @Column(name = "ID")
    private Long id;

    @OneToMany(cascade = {CascadeType.ALL}, mappedBy = "parent")
    private Set<Child> childs = new HashSet<Child>();

 ...
}

Дитина:

@Entity
public class Child {
    @Id
    @Column(name = "ID")
    private Long id;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name="PARENTID", nullable = false)
    private Parent parent;

  ...
}

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

Суб'єкти використовуються у веб-програмі, видалення відбувається як частина запиту Ajax. У мене немає списку видалених дітей при натисканні кнопки збереження, тому я не можу їх видалити неявно.

Відповіді:


253

Поведінка JPA правильна (що означає специфікацію ): об'єкти не видаляються просто тому, що ви видалили їх з колекції OneToMany. Існують розширення, що стосуються конкретних постачальників, які роблять це, але рідний JPA не задовольняє це.

Частково це відбувається тому, що JPA насправді не знає, чи слід видалити щось, що було видалено з колекції. У термінах об'єктного моделювання це різниця між складом та "агрегацією *".

За складом дитяче утворення не існує без батька. Класичний приклад - між будинком та кімнатою. Видаліть будинок і Номери також піде.

Агрегація - це вільний вид асоціацій, який характеризується курсом та студентом. Видаліть курс, і студент все ще існує (можливо, в інших курсах).

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

Мені відомо:


дякую приємному поясненню. так що я побоювався. (Я робив деякий пошук / читання, перш ніж я запитав, просто бажаючи зберегти). якось я починаю шкодувати про рішення використовувати JPA API та nit Hibernate безпосередньо .. Я спробую вказівник Chandra Patni і використовувати каскад типу hibernate delete_orphan.
bert

У мене є подібне запитання щодо цього. Будь ласка, погляньте на цю публікацію, будь ласка? stackoverflow.com/questions/4569857/…
Thang Pham

77
З JPA 2.0 тепер ви можете використовувати опцію orphanRemoval = true
itsadok

2
Чудове початкове пояснення та хороша порада щодо усунення сиріт. Ніякої ідеї JPA не враховував цей вид видалення. Нюанси між тим, що я знаю про сплячку, і тим, що насправді робить JPA, може звести з розуму.
sma

Чудово пояснюється викриттям відмінностей між складом та угодою!
Феліпе Лео

73

Окрім відповіді клетуса, JPA 2.0 , остаточний з грудня 2010 року, містить orphanRemovalатрибут на @OneToManyанотації. Детальніше дивіться у цій статті .

Зауважте, оскільки специфікація є відносно новою, не всі постачальники JPA 1 мають остаточну реалізацію JPA 2. Наприклад, випуск Hibernate 3.5.0-Beta-2 ще не підтримує цей атрибут.


запис у блозі - посилання порушено.
Стеффі

1
І магія зворотної машини рятує нас: web.archive.org/web/20120225040254/http://javablog.co.uk/2009/…
Луї Якомет

43

Ви можете спробувати це:

@OneToOne(orphanRemoval=true) or @OneToMany(orphanRemoval=true).


2
Дякую. Але питання повертається з JPA 1 раз. І цей варіант був недоступний назад.
bert

9
добре знати, тим не менше, для тих, хто шукає рішення для цього в JPA2 рази :)
alex440

20

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

@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}, mappedBy = "parent")
@Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE,
            org.hibernate.annotations.CascadeType.DELETE,
            org.hibernate.annotations.CascadeType.MERGE,
            org.hibernate.annotations.CascadeType.PERSIST,
            org.hibernate.annotations.CascadeType.DELETE_ORPHAN})
private Set<Child> childs = new HashSet<Child>();

Я не міг просто використати "ВСЕ", оскільки це також видалило б батьківський.




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