从DB中删除cascade和orphan有什么区别?

时间:2013-09-15 14:12:29

标签: java jpa cascading-deletes

之间有什么区别
@OneToMany(cascade=REMOVE, mappedBy="customer")
public List<Order> getOrders() { ... }

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

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

5 个答案:

答案 0 :(得分:126)

来自here: -

  

级联删除

     

使用CascadeType.REMOVE(或CascadeType.ALL)标记引用字段,   其中包括REMOVE)表示删除操作应该是   自动级联到由其引用的实体对象   字段(集合可以引用多个实体对象)   场):

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

孤儿删除

     

JPA 2支持额外且更积极的删除级联模式   可以使用的orphanRemoval元素指定   @OneToOne和@OneToMany注释:

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

<强>区别: -

     

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

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

答案 1 :(得分:70)

了解CascadeType.REMOVEorphanRemoval=true之间差异的简便方法。

孤儿摘除: 如果您调用setOrders(null),则相关的Order实体将自动在数据库中删除。

删除级联: 如果您调用setOrders(null),相关的Order实体将自动在数据库中删除 NOT

答案 2 :(得分:3)

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

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

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

如果无法从其父级访问该子级,则该子级将成为孤儿。 例如,如果我们删除索引i处的obj(使用myChildernList.remove(i))或将其设置为null或将其替换为新的(personnelList.set(i,newChild)),则父级将无法再访问该子级并且该孩子是孤儿,因此该孩子也注定要从数据库中删除(这是令人心碎的:()

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

答案 3 :(得分:2)

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

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集合,并从PostComment集合中删除第一个comments

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

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

答案 4 :(得分:0)

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

假设您删除customer而不是使用cascade=REMOVE也会删除客户的订单,这些订单似乎是有意的和有用的。

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

现在,假设您将customer更新为orphanRemoval="true",它将删除所有先前的订单,并将其替换为提供的订单。 (按PUT的{​​{1}})

REST API

没有@OneToMany(mappedBy="customer", orphanRemoval="true") public List<Order> getOrders() { ... } 的旧订单将被保留。 (按orphanRemoval的{​​{1}})