JPA: однонаправленное многоканальное и каскадное удаление

Скажем, что у меня есть однонаправленное отношение @ManyToOne, подобное следующему:

@Entity
public class Parent implements Serializable {

    @Id
    @GeneratedValue
    private long id;
}

@Entity
public class Child implements Serializable {

    @Id
    @GeneratedValue
    private long id;

    @ManyToOne
    @JoinColumn
    private Parent parent;  
}

Если у меня есть родительский P и дети C 1... C n, ссылающийся на P, есть ли чистый и красивый способ в JPA, чтобы автоматически удалить дети C 1... C n, когда P удаляется (т.е. entityManager.remove(P))?

Я ищу функциональность, похожую на ON DELETE CASCADE в SQL.

Ответ 1

Отношения в JPA всегда однонаправлены, если вы не связываете родителя с ребенком в обоих направлениях. Каскадные операции REMOVE из родительского элемента в дочерние объекты потребуют отношения от родителя к ребенку (а не только наоборот).

Вам нужно будет сделать это:

  • Либо измените однонаправленную связь @ManyToOne на двунаправленную @ManyToOne, либо однонаправленную @OneToMany. Затем вы можете каскадировать операции REMOVE, чтобы EntityManager.remove удалил родителя и детей. Вы также можете указать orphanRemoval как true, чтобы удалить всех детей-сирот, когда дочерний объект в родительской коллекции установлен в null, то есть удалить если он отсутствует в какой-либо родительской коллекции.
  • Или укажите ограничение внешнего ключа в дочерней таблице как ON DELETE CASCADE. Вам нужно будет вызвать EntityManager.clear() после вызова EntityManager.remove(parent), поскольку необходимо обновить контекст персистентности - дочерние сущности не должны существовать в контексте персистентности после их удаления в базе данных.

Ответ 2

Если вы используете hibernate как поставщик JPA, вы можете использовать аннотацию @OnDelete. Эта аннотация добавит к соединению триггер ON DELETE CASCADE, который делегирует удаление дочерних элементов в базу данных.

Пример:

public class Parent {

        @Id
        private long id;

}


public class Child {

        @Id
        private long id;

        @ManyToOne
        @OnDelete(action = OnDeleteAction.CASCADE)
        private Parent parent;
}

С этим решением достаточно одностороннего отношения от ребенка к родительскому, чтобы автоматически удалить всех детей. Это решение не нуждается ни в каких слушателях и т.д. Также запрос, например DELETE FROM Parent WHERE id = 1, удалит дочерние элементы.

Ответ 3

Создайте двунаправленное отношение, например:

@Entity
public class Parent implements Serializable {

    @Id
    @GeneratedValue
    private long id;

    @OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE)
    private Set<Child> children;
}

Ответ 4

Я видел в однонаправленном @ManytoOne, удаление не работает, как ожидалось. Когда родительский объект удален, в идеале также должен быть удален дочерний элемент, но удаляется только родительский элемент, а дочерний элемент НЕ удаляется и остается сиротой

Используются следующие технологии: Spring Boot/Spring Data JPA/Hibernate

Спринт Boot: 2.1.2.RELEASE

Spring Data JPA/Hibernate используется для удаления строки .eg

parentRepository.delete(parent)

ParentRepository расширяет стандартный CRUD-репозиторий, как показано ниже ParentRepository extends CrudRepository<T, ID>

Ниже приведен мой класс сущности

@Entity(name = 'child')
public class Child  {

    @Id
    @GeneratedValue
    private long id;

    @ManyToOne( fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = 'parent_id", nullable = false)
    @OnDelete(action = OnDeleteAction.CASCADE)
    private Parent parent;
}

@Entity(name = 'parent')
public class Parent {

    @Id
    @GeneratedValue
    private long id;

    @Column(nullable = false, length = 50)
    private String firstName;


}

Ответ 5

Используйте этот способ, чтобы удалить только одну сторону

    @ManyToOne(cascade=CascadeType.PERSIST, fetch = FetchType.LAZY)
//  @JoinColumn(name = "qid")
    @JoinColumn(name = "qid", referencedColumnName = "qid", foreignKey = @ForeignKey(name = "qid"), nullable = false)
    // @JsonIgnore
    @JsonBackReference
    private QueueGroup queueGroup;

Ответ 6

@Cascade (org.hibernate.annotations.CascadeType.DELETE_ORPHAN)

Данная аннотация сработала для меня. Можно попробовать

Например: -

     public class Parent{
            @Id
            @GeneratedValue(strategy=GenerationType.AUTO)
            @Column(name="cct_id")
            private Integer cct_id;
            @OneToMany(cascade=CascadeType.REMOVE, fetch=FetchType.EAGER,mappedBy="clinicalCareTeam", orphanRemoval=true)
            @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
            private List<Child> childs;
        }
            public class Child{
            @ManyToOne(fetch=FetchType.EAGER)
            @JoinColumn(name="cct_id")
            private Parent parent;
    }