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&lt;T, ID&gt;

以下是我的实体类

@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语句

sql级联更新和级联删除不起作用

SQL 级联删除与级联更新的方法

重复条目异常:Spring Hibernate/JPA 级联保存多对一

hibernate之一对多,多对一

代码优先 TPT 和级联删除