JPA 2.0 orphanRemoval = true删除Cascade时的VS.

时间:2010-12-01 21:58:15

标签: hibernate jpa jpa-2.0 cascade cascading-deletes

我对JPA 2.0 orphanRemoval属性感到有些困惑。

我认为当我使用JPA提供程序的数据库生成工具创建底层数据库DDL以在特定关系上拥有ON DELETE CASCADE时,我可以看到它是必需的。

但是,如果数据库存在且关系上已经有ON DELETE CASCADE,这是不是足以级联删除? orphanRemoval另外做了什么?

干杯

8 个答案:

答案 0 :(得分:235)

orphanRemovalON DELETE CASCADE无关。

orphanRemoval完全是特定于ORM的内容。它标记“子”实体,当它不再从“父”实体引用时被删除,例如当您从父实体的相应集合中删除子实体时。

ON DELETE CASCADE特定于数据库的事物,当删除“父”行时,它会删除数据库中的“子”行。

答案 1 :(得分:77)

here采取的示例:

删除Employee实体对象后,删除操作将级联到引用的Address实体对象。在这方面,orphanRemoval=truecascade=CascadeType.REMOVE是相同的,如果指定了orphanRemoval=true,则CascadeType.REMOVE是多余的。

两个设置之间的区别在于断开关系的响应。例如,将地址字段设置为null或另一个Address对象时。

  • 如果orphanRemoval=true指定了已断开连接的Address实例 自动删除。这对清理依赖很有用 在没有引用的情况下不应该存在的对象(例如Address) 所有者对象(例如Employee)。

  • 如果仅指定cascade=CascadeType.REMOVE,则不执行自动操作 因为断开关系不是一个删除 操作

为避免因孤立删除而悬挂引用,只应为包含私有非共享依赖对象的字段启用此功能。

我希望这更清楚。

答案 2 :(得分:44)

从集合中删除子实体的那一刻,您也将从数据库中删除该子实体。 orphanRemoval也暗示你不能改变父母;如果有一个部门有员工,一旦你删除该员工将其放入另一个部门,你就会在刷新/提交时无意中从数据库中删除该员工(无论哪个先行)。士气是将orphanRemoval设置为true,只要您确定该父级的子女在其存在期间不会迁移到不同的父级。启用orphanRemoval还会自动将REMOVE添加到级联列表。

答案 3 :(得分:17)

DDL ON DELETE CASCADE的等效JPA映射为cascade=CascadeType.REMOVE。孤立删除意味着在与其“父”实体的关系被销毁时删除依赖实体。例如,如果从@OneToMany关系中删除子项而未在实体管理器中明确删除它。

答案 4 :(得分:13)

由于这是一个非常常见的问题,所以我写了this article,此答案基于此。

实体状态转换

JPA将实体状态转换转换为SQL语句,例如INSERT,UPDATE或DELETE。

JPA entity state transitions

persist实体中,您计划将在自动EntityManager刷新remove时执行INSERT语句。

Post实体时,您正在计划DELETE语句,该语句将在刷新持久性上下文时执行。

级联实体状态转换

为方便起见,JPA允许您传播从父实体到子实体的实体状态转换。

因此,如果您的父PostComment实体与comments子实体具有@OneToMany association,则:

Post and PostComment entities

Post实体中的@OneToMany( mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true ) private List<Comment> comments = new ArrayList<>(); 集合的映射如下:

cascade

CascadeType.ALL

Post属性告诉JPA提供者将实体状态从父PostComment实体传递到comments集合中包含的所有Post实体。

因此,如果您删除Post post = entityManager.find(Post.class, 1L); assertEquals(2, post.getComments().size()); entityManager.remove(post); 实体:

PostComment

JPA提供者将首先删除Post实体,并且在删除所有子实体时,它也会同时删除DELETE FROM post_comment WHERE id = 1 DELETE FROM post_comment WHERE id = 2 DELETE FROM post WHERE id = 1 实体:

orphanRemoval

移除孤儿

当您将true属性设置为remove时,当从集合中删除子实体时,JPA提供程序将安排Post post = entityManager.find(Post.class, 1L); assertEquals(2, post.getComments().size()); PostComment postComment = post.getComments().get(0); assertEquals(1L, postComment.getId()); post.getComments().remove(postComment); 操作。

所以在我们的例子中

post_comment

JPA提供者将删除关联的PostComment记录,因为comments集合中不再引用DELETE FROM post_comment WHERE id = 1 实体:

ON DELETE CASCADE

删除级联

ALTER TABLE post_comment ADD CONSTRAINT fk_post_comment_post_id FOREIGN KEY (post_id) REFERENCES post ON DELETE CASCADE; 是在FK级别定义的:

post

这样做之后,如果您删除DELETE FROM post WHERE id = 1 行:

post_comment

数据库引擎会自动删除所有关联的cascade实体。但是,如果误删除根实体,这将是非常危险的操作。

结论

JPA orphanRemovalON DELETE CASCADE选项的优点在于,您还可以从optimistic locking中受益,以防止lost updates

如果使用JPA级联机制,则无需使用DDL级的{{1}},如果删除具有多个子级实体的根实体,这将是非常危险的操作。 / p>

有关此主题的更多详细信息,请查看this article

答案 5 :(得分:4)

@GaryK答案非常棒,我花了一个小时寻找orphanRemoval = true vs CascadeType.REMOVE的解释,这让我明白了。

总结:orphanRemoval = true的工作方式与CascadeType.REMOVE相同仅在我们删除对象(entityManager.delete(object))时,我们也希望删除子对象。

在完全不同的情境中,当我们提取某些数据(例如List<Child> childs = object.getChilds())然后使用entityManager.remove(childs.get(0)移除子项(orphanRemoval=true)时,将导致与childs.get(0)对应的实体从数据库中删除。

答案 6 :(得分:3)

在以下情形中,

孤立删除与ON DELETE CASCADE具有相同的效果: - 假设我们在学生实体和指南实体之间有一个简单的多对一关系,其中许多学生可以映射到同一指南,在数据库中我们在学生和指南表之间有外键关系,这样学生表的id_guide为FK

    @Entity
    @Table(name = "student", catalog = "helloworld")
    public class Student implements java.io.Serializable {
     @Id
     @GeneratedValue(strategy = IDENTITY)
     @Column(name = "id")
     private Integer id;

    @ManyToOne(cascade={CascadeType.PERSIST,CascadeType.REMOVE})
    @JoinColumn(name = "id_guide")
    private Guide guide;

//父实体

    @Entity
    @Table(name = "guide", catalog = "helloworld")
    public class Guide implements java.io.Serializable {

/**
 * 
 */
private static final long serialVersionUID = 9017118664546491038L;

@Id
@GeneratedValue(strategy = IDENTITY)
@Column(name = "id", unique = true, nullable = false)
private Integer id;

@Column(name = "name", length = 45)
private String name;

@Column(name = "salary", length = 45)
private String salary;


 @OneToMany(mappedBy = "guide", orphanRemoval=true) 
 private Set<Student> students = new  HashSet<Student>(0);

在这种情况下,关系是学生实体是关系的所有者,因此我们需要保存学生实体以保持整个对象图,例如。

    Guide guide = new Guide("John", "$1500");
    Student s1 = new Student(guide, "Roy","ECE");
    Student s2 = new Student(guide, "Nick", "ECE");
    em.persist(s1);
    em.persist(s2);

这里我们使用两个不同的学生对象映射相同的指南,并且由于使用了CASCADE.PERSIST,因此对象图将在数据库表中保存如下(在我的情况下为MySql)

学生表: -

ID Name Dept Id_Guide

1 Roy ECE 1

2尼克ECE 1

指南表: -

ID NAME Salary

约翰一书$ 1500

现在如果我想删除其中一个学生,请使用

      Student student1 = em.find(Student.class,1);
      em.remove(student1);

当删除学生记录时,也应删除相应的指南记录,即学生实体中的CASCADE.REMOVE属性进入图片及其作用的位置;它会删除标识为1的学生以及相应的指南对象(标识符1)。但是在这个例子中,还有一个学生对象被映射到同一个指南记录,除非我们在指南实体中使用 orphanRemoval = true 属性,否则上面的删除代码将不起作用。

答案 7 :(得分:2)

区别在于:
- orphanRemoval = true:“Child”实体在不再引用时被删除(其父级可能不会被删除)。
- CascadeType.REMOVE:仅当删除“父”时才删除“子”实体。