关于JPA级联的问题

我的datamodel中有两个名为User和UserProfile的实体。 以下是它们的映射方式。

用户实体的代码:

@OneToOne(cascade=CascadeType.ALL) @PrimaryKeyJoinColumn public UserProfile getUserProfile(){ return this.userProfile; } public void setUserProfile(UserProfile userProfile){ this.userProfile=userProfile; } 

来自UserProfile实体的代码:

 @OneToOne(mappedBy="userProfile",cascade=CascadeType.ALL) public User getUser(){ return this.user; } public void setUser(User user){ this.user=user; } 

如您所见,我在UserProfile中有一个用于user属性的cascadetype.all。 但是当我尝试删除UserProfile实体时,相应的用户实体仍然存在。 (当我尝试删除User实体时,相应的UserProfile实体被删除。)

这是我的问题: –

  • 只有当我在拥有这种关系的实体上指定级联时才进行级联吗?

你的问题本身就是错误的,这就是所有困惑所源自的地方。 亚瑟的答案很好,但从评论中可以清楚地看出这种混乱仍然存在,所以让我在这里采取措施。

只有当我在拥有这种关系的实体上指定级联时才进行级联吗?

“cascade”是您在一个关系结束时指定的属性(或者在双向情况下可能都指定)。 它确定在端执行的操作将传播到另一端。 JPA中定义了许多不同类型的操作, 甚至在Hibernate扩展中定义了更多 。 这种区别很重要 – 您应该只讨论传播的特定行为,而不是“级联”。

PERSIST,MERGE,REFRESH正常传播(从最后声明到另一个)。

然而,删除是棘手的,因为它可能意味着两件事。 如果你有AB之间的关系并且你试图删除A ,你可以删除另一端的B或者你可以删除关联但保持B完整。 Hibernate明确区分了两者 – 你可以分别声明REMOVE(DELETE)和DELETE_ORPHAN级联类型; JPA规范没有。 请注意,单值关系(OneToOne / ManyToOne)不支持DELETE_ORPHAN

因此,REMOVE的传播(单独或当它是ALL的一部分时)取决于关系是否具有明确的所有者(单向总是如此;如果使用mappedBy映射则是双向的,如果通过连接表映射则不是)在哪种情况下,它从所有者传播到拥有者或没有所有者,在这种情况下,它在任一方向传播但没有DELETE_ORPHAN语义,除非明确指定。 后者的典型示例是双向多对多。

如上所述

当我尝试删除UserProfile实体时,相应的用户实体仍然保留

也许当您尝试删除UserProfile时,您会从数据库中获得完整性约束违规 – 您是否在MySQL中使用MyISAM引擎?

但是你没有说什么。 也许您的UserProfile实体没有对User实体的引用。

如JPA规范中所述

如果从X到这些其他实体的关系使用cascade = REMOVE或cascade = ALL注释元素值进行注释,则删除操作会级联到X引用的实体

就像是

 UserProfile up = entityManager.find(UserProfile.class, id); entityManager.close(); // Notice User is null outside a persistence context // So user will be not removed from the database because UserProfile does not have a reference to it up.setUser(null); entityManager.getTransaction().begin(); entityManager.remove(up); entityManager.getTransaction().commit(); 

或者你有类似的东西

 entityManager.getTransaction().begin(); UserProfile up = entityManager.find(UserProfile.class, id); // throws UPDATE USER_PROFILE SET USER_ID = NULL up.setUser(null); // up.getUser() is null // So user is not removed entityManager.remove(up); entityManager.getTransaction().commit(); 

回应ChhsPly的评论:

在Java Persistence with Hibernate一书中,您将看到以下内容

级联属性是方向性的: 它仅适用于关联的一端

我认为它会更好

它仅适用于每个操作的关联的一端

因此,即使在双向关系中,您也可以同时在两侧放置级联属性。 所以ChssPly是对的。

mappdeBy属性设置双向关系 。 mappedBy属性将Address实体指定为关系的反面。 这意味着客户实体是关系的拥有方。

当他说mappedBy与级联无关时,ChssPly是对的

当你有一个双向关系时,这是正确的,所有者决定级联规则,因为它是“所有者”。 “拥有”实体基本上遵循订单,它不能给订单 – 可以这么说。

使用JPA 2.x,如果要删除级联,请使用orphanRemoval属性:

@OneToMany(orphanRemoval=true)

查看此处的文档了解更多信息。