我的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实体将被删除。)
这是我的问题: -
答案 0 :(得分:16)
你的问题本身就是错误的,这是所有混乱的源头所在。亚瑟的答案做得很好,但从评论中可以清楚地看出这种混乱仍然存在,所以让我在这里采取措施。
只有在我指定时,才会保持级联 他们在拥有该实体的实体上 关系?
“cascade”是您在一个关系结束时指定的属性(或者在双向情况下可能两者)。它确定在上执行的操作 端将传播到其他端。在JPA中定义了many different types个动作,在Hibernate扩展中定义了even more。这种区别非常重要 - 您应该只讨论传播的特定行为,而不是“级联”。
PERSIST,MERGE,REFRESH正常传播(从最后声明到另一个)。
然而,删除是棘手的,因为它可能意味着两件事。如果 A 与 B 之间存在关联,并且您尝试删除 A ,则可以删除 B 在另一端或者您可以删除关联,但保留 B 。
Hibernate明确区分了两者 - 您可以分别声明REMOVE(DELETE)和DELETE_ORPHAN
级联类型; JPA规范没有。请注意,单值关系(OneToOne / ManyToOne)不支持DELETE_ORPHAN
。
因此,REMOVE的传播(单独或当它是ALL的一部分)取决于关系是否具有明确的所有者(单向始终如此;如果使用 mappedBy 进行映射,则双向进行)如果它是通过连接表映射的,那么它不会在这种情况下从所有者传播到拥有的OR没有所有者,在这种情况下,它会在任一方向上传播但没有DELETE_ORPHAN
语义,除非明确指定。后者的典型例子是双向多对多。
答案 1 :(得分:4)
如上所述
当我尝试删除UserProfile实体时,相应的用户实体仍然保留
当您尝试删除UserProfile时,您会从数据库中获得完整性约束违规 - 您是否在MySQL中使用MyISAM引擎?
但是你没有说什么。也许您的UserProfile实体没有对User实体的引用。
如JPA规范中所述
删除操作级联到X引用的实体,如果从X到这些其他实体的关系使用cascade = REMOVE或cascade = ALL注释元素值注释
像
这样的东西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是对的
答案 2 :(得分:1)
当你有双向关系时,这是正确的,所有者决定级联规则,因为它是“所有者”。 “拥有”实体基本上遵循订单,它不能发出订单 - 可以这么说。
答案 3 :(得分:0)