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;
}