JPA中的CascadeType.REMOVE和orphanRemoval有什么区别?


103

之间有什么区别

@OneToMany(cascade=REMOVE, mappedBy="customer")
public List<Order> getOrders() { ... }

@OneToMany(mappedBy="customer", orphanRemoval="true")
public List<Order> getOrders() { ... }

此示例来自Java EE教程,但我仍然不了解详细信息。


孤立删除表示从属实体在与“父”实体的关系被破坏时也被删除。
拉胡尔·特里帕西

1
编写了一个可以说明这一概念的测试用例
马丁·安德森

Answers:


153

这里

级联删除

用CascadeType.REMOVE(或CascadeType.ALL,包括REMOVE)标记引用字段,指示删除操作应自动级联到该字段引用的实体对象(多个实体对象可以被collection字段引用):

@Entity
class Employee {
     :
    @OneToOne(cascade=CascadeType.REMOVE)
    private Address address;
     :
}

移除孤儿

JPA 2支持附加的,更积极的删除级联模式,可以使用@OneToOne和@OneToMany批注的orphanRemoval元素来指定它:

@Entity
class Employee {
     :
    @OneToOne(orphanRemoval=true)
    private Address address;
     :
}

区别:-

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

  • 如果指定了orphanRemoval = true,则会自动删除断开连接的Address实例。这对于清理没有所有者对象(例如Employee)的引用不应该存在的依赖对象(例如地址)很有用。
  • 如果仅指定了cascade = CascadeType.REMOVE,则不会执行自动操作,因为断开关系不是删除
    操作。

87

一个简单的方法来理解之间的差异CascadeType.REMOVEorphanRemoval=true

对于孤立删除:如果调用setOrders(null),相关Order实体将自动在db中删除。

对于删除级联:如果调用setOrders(null)Order不会自动在db中删除相关实体。


2
删除===删除
Abdull

9

假设我们有一个子实体和一个父实体。父母可以有几个孩子。

@Entity
class parent {
  //id and other fields
 @OneToMany (orphanRemoval = "true",cascade = CascadeType.REMOVE)
   Set<Person> myChildern;
}

orphanRemoval是一个ORM概念,它告诉孩子是否为孤儿。它也应该从数据库中删除。

如果无法从其父母访问孩子,则该孩子成为孤儿。例如,如果我们删除“人”对象集(将其设置为空集)或将其替换为新集,那么父级将无法再访问旧集中的子级,并且子级将变为孤儿,因此子级注定是也从数据库中删除。

CascadeType.REMOVE是数据库级别的概念,它指示是否删除了父级,则应该删除其在子表中的所有相关记录。


2

实际上,区别在于您是要尝试更新数据(PATCH)还是要完全替换数据(PUT)

假设您删除而customer不是使用cascade=REMOVE还将删除那些看起来既有用又有用的客户订单。

@OneToMany(cascade=REMOVE, mappedBy="customer")
public List<Order> getOrders() { ... }

现在,假设您使用进行更新customerorphanRemoval="true"它将删除所有先前的订单,并用提供的订单替换它们。(PUT就而言REST API

@OneToMany(mappedBy="customer", orphanRemoval="true")
public List<Order> getOrders() { ... }

没有orphanRemoval旧的命令将被保留。(PATCH就而言REST API


1

由于这个问题非常普遍,因此此答案基于我在博客上写的这篇文章

CascadeType.REMOVE

CascadeType.REMOVE您可以显式配置的策略:

@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.REMOVE
)
private List<PostComment> comments = new ArrayList<>();

或从CascadeType.ALL策略中隐式继承:

@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.ALL
)
private List<PostComment> comments = new ArrayList<>();

允许您将remove操作从父实体传播到其子实体。

因此,如果我们获取父Post实体及其comments集合,然后删除该post实体:

Post post = entityManager.createQuery("""
    select p
    from Post p
    join fetch p.comments
    where p.id = :id
    """, Post.class)
.setParameter("id", postId)
.getSingleResult();

entityManager.remove(post);

Hibernate将执行三个delete语句:

DELETE FROM post_comment 
WHERE id = 2

DELETE FROM post_comment 
WHERE id = 3

DELETE FROM post 
WHERE id = 1

PostComment子实体是因为删除的CascadeType.REMOVE策略,表现得好像我们去掉了孩子实体。

孤儿搬迁策略

孤儿移除策略,需要通过以下orphanRemoval属性进行设置:

@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();

允许您在从集合中删除子实体时删除子表行。

因此,如果我们将Post实体及其comments集合一起加载,然后PostCommentcomments集合中删除第一个实体:

Post post = entityManager.createQuery("""
    select p
    from Post p
    join fetch p.comments c
    where p.id = :id
    order by p.id, c.id
    """, Post.class)
.setParameter("id", postId)
.getSingleResult();

post.remove(post.getComments().get(0));

Hibernate将为关联的post_comment表行执行DELETE语句:

DELETE FROM post_comment 
WHERE id = 2

有关此主题的更多详细信息,请同时阅读本文

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.