在 JPA 中,CascadeType.REMOVE 和孤儿移除有什么区别?

这两者有什么区别

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

还有

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

这个例子来自 JavaEE 教程,但是我仍然不明白细节。

104814 次浏览

给你:-

级联移除

用 CascadeType.Remove (或 CascadeType.ALL, (包括 REMOVE)指示移除操作应该是 对象引用的实体对象 字段(集合可以引用多个实体对象 字段) :

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

孤儿搬迁

JPA2支持一种附加的、更积极的移除级联模式 属性的孤立删除元素指定 @ OneToOne and@OneTomany 注释:

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

区别:-

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

  • 如果指定了 移除孤儿 = 真,断开连接的 Address 实例将被自动删除。这对于清理非常有用 依赖对象(例如 Address) ,如果没有 来自所有者对象的引用(例如,Employee)。
  • 如果只指定了 级联 = 级联类型,则不会采取自动操作,因为断开关系不是删除操作
    行动。

一个简单的方法来理解 CascadeType.REMOVEorphanRemoval=true之间的区别。

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

移除级联: 如果调用 setOrders(null),相关的 Order实体将在 db 中自动删除 没有

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

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

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

如果无法从其父级访问子级,则该子级为孤儿。 例如,如果我们删除 Person 对象集(将其设置为空集)或者将其替换为新集,那么父对象就不能再访问旧集中的子对象,而且子对象是孤立的,因此子对象也注定要在数据库中被删除。

REMOVE 是一个数据库级别的概念,它告诉我们如果父节点被删除,那么子表中的所有相关记录都应该被删除。

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

假设你删除了 customer比使用 cascade=REMOVE还会删除那些看起来有意义和有用的客户订单。

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

现在,让我们说你更新一个 customerorphanRemoval="true"它将删除所有以前的订单,并取代他们与一个提供。(以 REST API计算的 PUT)

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

如果没有 orphanRemoval,旧的订单将被保留

级联类型,移除

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

由于 CascadeType.REMOVE策略,PostComment子实体被删除了,这就好像我们也删除了子实体一样。

移除孤儿的策略

孤儿清除策略,需要通过 orphanRemoval属性设置:

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

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

因此,如果我们加载 Post实体及其 comments集合,并从 comments集合中删除第一个 PostComment:

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