JPA OneToMany, не удаляющий ребенка

У меня проблема с простым сопоставлением @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;

  ...
}

Если я теперь удаляю и дочерний элемент из набора childs, он не удаляется из базы данных. Я попытался свернуть ссылку child.parent, но это тоже не сработало.

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

Ответ 1

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

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

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

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

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

Я знаю:

Ответ 2

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

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

Ответ 3

Вы можете попробовать следующее:

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

Ответ 4

Как объяснялось, невозможно сделать то, что я хочу с 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>();

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

Ответ 5

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

Ответ 6

@Entity 
class Employee {
     @OneToOne(orphanRemoval=true)
     private Address address;
}

Смотрите здесь.

Ответ 7

Вы можете попробовать следующее:

@OneToOne(cascade = CascadeType.REFRESH) 

или

@OneToMany(cascade = CascadeType.REFRESH)