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 из набора childs, он не будет удален из базы данных. Я попытался аннулировать child.parentссылку, но это тоже не сработало.

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

Ответы:


253

Поведение JPA правильное (имеется в виду согласно спецификации ): объекты не удаляются просто потому, что вы удалили их из коллекции OneToMany. Существуют специфичные для вендора расширения, которые делают это, но нативный JPA не учитывает это.

Отчасти это происходит потому, что JPA на самом деле не знает, следует ли удалять что-то, удаленное из коллекции. В терминах объектного моделирования это разница между составом и «агрегацией» *.

В композиции дочерняя сущность не существует без родителя. Классический пример - между Домом и Комнатой. Удалить дом и комнаты тоже.

Агрегация - более слабая ассоциация, типичная для курса и студента. Удалите курс, и студент все еще существует (возможно, в других курсах).

Таким образом, вам нужно либо использовать специфичные для поставщика расширения, чтобы вызвать это поведение (если оно доступно), либо явно удалить дочерний элемент И удалить его из родительского набора.

Я знаю о:


спасибо хорошее объяснение. так, как я боялся. (Я сделал поиск / чтение, прежде чем я спросил, просто хотел сохранить). как-то я начинаю сожалеть о решении использовать JPA API и сразу Hibernate .. Я попробую указатель Чандра Патни и использую каскадный тип delete_orphan hibernate.
Берт

У меня есть похожий вопрос по этому поводу. Будь добр, посмотри сюда этот пост, пожалуйста. stackoverflow.com/questions/4569857/…
Тханг Фам

78
В JPA 2.0 теперь вы можете использовать опцию orphanRemoval = true
itsadok

2
Отличное начальное объяснение и полезные советы по удалению сирот. Не знал, что JPA не учитывает этот тип удаления. Нюансы между тем, что я знаю о Hibernate, и тем, что на самом деле делает JPA, могут сводить с ума.
СМА

Приятно объяснено, разоблачая различия между Композицией и Агрегацией!
Фелипе Леан

73

В дополнение к ответу Cletus, 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 15.10.15

20

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

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

Я не мог просто использовать «ALL», так как это также удалило бы родителя.


4

Здесь каскад в контексте удаления означает, что дочерние элементы удаляются, если вы удаляете родительский элемент. Не ассоциация. Если вы используете Hibernate в качестве вашего JPA-провайдера, вы можете сделать это, используя специальный каскад hibernate .



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