关于JPA级联的问题

soo*_*red 11 java annotations jpa

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

用户实体的代码:

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

public void setUserProfile(UserProfile userProfile){
    this.userProfile=userProfile;
}
Run Code Online (Sandbox Code Playgroud)

来自UserProfile实体的代码:

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

public void setUser(User user){
    this.user=user;
}
Run Code Online (Sandbox Code Playgroud)

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

这是我的问题: -

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

Chs*_*y76 16

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

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

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

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

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

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


Art*_*ald 4

正如所说

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

也许当您尝试删除 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();
Run Code Online (Sandbox Code Playgroud)

或者你有类似的东西

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();
Run Code Online (Sandbox Code Playgroud)

回应 ChhsPly 的评论:

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

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

我认为这会更好

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

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

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

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