关于JPA级联的问题

时间:2009-10-07 22:32:49

标签: java jpa annotations

我的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实体将被删除。)

这是我的问题: -

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

4 个答案:

答案 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)

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

@OneToMany(orphanRemoval=true)

查看文档here了解详情。