JPA OneToMany не удаляет ребенка - PullRequest
146 голосов
/ 06 января 2010

У меня проблема с простым @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. У меня нет списка удаленных дочерних элементов при нажатии кнопки сохранения, поэтому я не могу удалить их неявно.

Ответы [ 7 ]

241 голосов
/ 06 января 2010

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

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

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

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

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

Мне известно:

69 голосов
/ 06 января 2010

В дополнение к ответу cletus, JPA 2.0 , последний с декабря 2010 года, вводит атрибут orphanRemoval в аннотациях @OneToMany. Для более подробной информации смотрите эту запись в блоге .

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

41 голосов
/ 04 апреля 2013

Вы можете попробовать это:

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

18 голосов
/ 06 января 2010

Как объяснено, с 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>();

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

3 голосов
/ 06 января 2010

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

2 голосов
/ 27 июля 2016
@Entity 
class Employee {
     @OneToOne(orphanRemoval=true)
     private Address address;
}

См. здесь .

2 голосов
/ 24 декабря 2015

Вы можете попробовать это:

@OneToOne(cascade = CascadeType.REFRESH) 

или

@OneToMany(cascade = CascadeType.REFRESH)
...