@ManyToOne JPA关联中的CascadeType.ALL是什么意思?

306

我认为我在@ManyToOne关系上误解了级联的含义。

情况如下:

public class User {

   @OneToMany(fetch = FetchType.EAGER)
   protected Set<Address> userAddresses;

}

public class Address {

   @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
   protected User addressOwner;

}

cascade = CascadeType.ALL 的含义是什么?例如,如果我从数据库中删除了某个地址,那么我添加了 cascade = CascadeType.ALL 会如何影响我的数据(我猜是 User)?

6个回答

477
CascadeType.ALL 的含义是持久化将会把所有 EntityManager 操作(PERSIST, REMOVE, REFRESH, MERGE, DETACH) 联级至相关联的实体。
在你的情况下,这似乎是个不好的想法,因为删除一个 Address 将导致相关的 User 也被删除。由于一个用户可以有多个地址,其他地址将成为孤儿。然而反向情况(注释 User)将是有意义的——如果一个地址只属于单个用户,那么安全地聚合属于该用户的所有地址的移除,如果该用户被删除。
顺便提一下:你可能想在你的User中添加一个 mappedBy="addressOwner" 属性,以向持久性提供程序发出信号,表明连接列应该存在于 ADDRESS 表中。

13
虽然@OneToMany一侧拥有CascadeType.ALL可能会很好。 - mvmn

128

@ManyToOne上使用CascadeType.ALL是不合适的,因为实体状态转换应该从父实体传播到子实体,而不是相反。

@ManyToOne位于关联的子级,因为它映射了底层的外键列。

因此,您应该将CascadeType.ALL@ManyToOne关联移到@OneToMany一侧,同时还应该使用mappedBy属性,因为它是最有效的一对多表关系映射。


2
谢谢Vlad,我不确定为什么这不是最佳答案。 - Slava Imeshev

60

点击此处查看来自OpenJPA文档的示例。CascadeType.ALL表示它将执行所有操作。

引用:

CascadeType.PERSIST:在持久化实体时,还要持久化其字段中包含的实体。我们建议广泛应用这个级联规则,因为如果EntityManager在刷新期间找到一个引用新实体的字段,并且该字段不使用CascadeType.PERSIST,则会出现错误。

CascadeType.REMOVE:在删除实体时,也会删除此字段中保存的实体。

CascadeType.REFRESH:在刷新实体时,也会刷新此字段中保存的实体。

CascadeType.MERGE:在合并实体状态时,也会合并此字段中保存的实体。

Sebastian


4
JPA中的新内容很有用,但是关于“Detach”呢? - Sarz
1
在CascadeType.DETACH中,当分离一个实体时,em也会分离由父实体持有的实体。 - Dorian Mejer

23

来自EJB3.0规范:

使用级联注解元素可以将操作的效果传播到关联实体。级联功能通常在父子关系中使用。

如果X是受管实体,则删除操作会导致其被删除。 如果从X到其他实体的关系用cascade=REMOVE或cascade=ALL注解元素值进行了注释,则删除操作会级联到X引用的实体。

简而言之,使用 CascadeType.All 定义的实体关系将确保所有发生在父项上的持久性事件(如persist、refresh、merge和remove)都将传递给子项。定义其他CascadeType选项为开发人员提供了更细粒度的控制能力,以便处理实体关联的持久性。

例如,如果我有一个包含页面列表的图书对象,并且我在此列表中添加一个页面对象。如果定义了Book与Page之间的@OneToMany注解,并标记为CascadeType.All,则持久化Book将导致Page也被持久化到数据库中。


16
在JPA 2.0中,如果你想从一个用户实体中删除地址,可以将CascadeType.REMOVE 替换为 orphanRemoval=true 并添加到你的@OneToMany注解中。
关于orphanRemoval=trueCascadeType.REMOVE之间的更多解释可以在这里查看。

10

如果您只想删除分配给用户的地址,而不影响用户实体类,您可以尝试以下操作:

@Entity
public class User {
   @OneToMany(mappedBy = "addressOwner", cascade = CascadeType.ALL)
   protected Set<Address> userAddresses = new HashSet<>();
}

@Entity 
public class Addresses {
   @ManyToOne(cascade = CascadeType.REFRESH) @JoinColumn(name = "user_id")
   protected User addressOwner;
}

使用这种方式,您无需担心在注释中使用fetch。但是请记住,当删除用户时,您也将删除连接到用户对象的地址。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接