JPA:单向多对一和级联删除
Posted
技术标签:
【中文标题】JPA:单向多对一和级联删除【英文标题】:JPA: unidirectional many-to-one and cascading delete 【发布时间】:2011-11-04 01:27:11 【问题描述】:假设我有一个 单向 @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 和子 C1...Cn 引用回 P,JPA 中是否有一种干净漂亮的方法来自动删除删除 P 时的子项 C1...Cn(即entityManager.remove(P)
)?
我正在寻找的是类似于 SQL 中的ON DELETE CASCADE
的功能。
【问题讨论】:
即使只有 'Child' 有对 'Parent' 的引用(这样引用是单向的),添加带有 '@OneToMany' 映射的 'Child' 列表是否有问题和“父级”的“级联=全部”属性?我认为 JPA 应该解决即使是艰难的只有一方持有参考。 @kvDennis,在某些情况下,您不想将多面紧密耦合到一侧。例如。在类似 ACL 的设置中,安全权限是透明的“附加组件” 【参考方案1】:如果您使用 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
这样的 JPQL 查询将删除子代。
【讨论】:
我不能让它这样工作,有没有特定版本的hibernate或其他更详细的例子? 很难说为什么它不适合你。要使其正常工作,您可能需要重新生成架构,或者您必须手动添加级联删除。 @OnDelete 注释似乎已经存在了一段时间,因此我不会猜测版本是一个问题。 感谢您的回答。快速注意:只有在通过休眠启用 DDL 生成时才会创建数据库级联触发器。否则,您必须以另一种方式添加它(例如 liquibase),以允许直接针对数据库运行临时查询,例如“从父级 WHERE id = 1 删除”执行级联删除。 当关联为@OneToOne
时,这不起作用有什么想法可以用@OneToOne
解决它吗?
@ThomasHunziker 这不适用于 orphanRemoval 对吧?【参考方案2】:
JPA 中的关系始终是单向的,除非您在两个方向上将父级与子级关联起来。从父级到子级的级联 REMOVE 操作将需要从父级到子级的关系(而不仅仅是相反)。
因此您需要这样做:
或者,将单向@ManyToOne
关系更改为双向 @ManyToOne
或单向 @OneToMany
。然后,您可以级联 REMOVE 操作,以便EntityManager.remove
将删除父级和子级。您还可以将 orphanRemoval
指定为 true,以在父集合中的子实体设置为 null 时删除任何孤立子实体,即当子实体不存在于任何父集合中时将其删除。
或者,将子表中的外键约束指定为ON DELETE CASCADE
。您需要在调用 EntityManager.remove(parent)
后调用 EntityManager.clear()
,因为需要刷新持久性上下文 - 子实体在数据库中被删除后不应存在于持久性上下文中。
【讨论】:
有没有办法用 JPA 注释做 No2? 如何使用 Hibernate xml 映射做 No2?【参考方案3】:创建双向关系,如下所示:
@Entity
public class Parent implements Serializable
@Id
@GeneratedValue
private long id;
@OneToMany(mappedBy = "parent", cascade = CascadeType.REMOVE)
private Set<Child> children;
【讨论】:
错误答案,JPA 中的双向关系很糟糕,因为在大型子集上操作需要花费大量时间 是否有证据表明双向关系很慢? @enerccio 如果双向关系是一对一的怎么办?另外,请展示一篇说明双向关系很慢的文章?慢什么?检索?删除?更新? @saran3h 每个操作(添加、删除)都会加载所有子项,因此这是一个巨大的数据负载,可能是无用的(例如添加一个值不需要从数据库中加载所有子项,这正是这个映射确实)。 @Enerccio 我认为每个人都在连接上使用延迟加载。那么它怎么还是一个性能问题呢?【参考方案4】:我在单向@ManytoOne 中看到,删除不能按预期工作。 删除父级时,理想情况下也应删除子级,但仅删除父级,子级未删除并保留为孤儿
使用的技术是 Spring Boot/Spring Data JPA/Hibernate
Sprint 启动: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;
【讨论】:
我找到了为什么删除不起作用的解决方案。显然休眠没有使用 mysql Engine -INNODB ,你需要引擎 INNODB 为 mysql 生成外键约束。在 application.properties 中使用以下属性,使 spring boot/hibernate 使用 mysql 引擎 INNODB。所以外键约束有效,因此也删除级联 错过的属性在之前的评论中使用。以下是使用的弹簧属性spring.jpa.hibernate.use-new-id-generator-mappings=true spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
仅供参考,您在代码中有错误的"
。见name= "parent"
【参考方案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;
【讨论】:
【参考方案7】:您不需要使用双向关联来代替您的代码,您只需将 CascaType.Remove 作为属性添加到 ManyToOne 注释,然后使用 @OnDelete(action = OnDeleteAction.CASCADE),它适用于我。
【讨论】:
以上是关于JPA:单向多对一和级联删除的主要内容,如果未能解决你的问题,请参考以下文章
关于多对多关系表做一个级联更新的问题(MYSQL),求高手解答SQL语句