JPA Hibernate 多对多级联。

67

我正在使用 JPA 2.0 和 Hibernate。我有一个 User 类和一个 Group 类,如下所示:

public class User implements Serializable {
    @Id
    @Column(name="USER_ID")
    private String userId;

    @ManyToMany
    @JoinTable(name = "USER_GROUP",
               joinColumns = {
                   @JoinColumn(name = "GROUP_ID")
               },
               inverseJoinColumns = {
                   @JoinColumn(name = "USER_ID")
               }
    )
    private Set<Group> groupList;

    //get set methods
}

public class Group
{
    @Id
    @Column(name="GROUP_ID")
    private String groupId;

    @ManyToMany(mappedBy="groupList")
    private Set<User> memberList;
    //get set methods
}

然后,我创建了一个用户和组,并将用户分配到该组。

我想要的是当我删除该组时,该组将被删除(当然),并且该组拥有的所有用户-组关系将自动从USER_GROUP联接表中删除,但该用户本身不会从USER表中删除。

使用我上面提供的代码,只有GROUP表中的行将在删除组时被删除,而用户仍将在USER_GROUP联接表中保留与已删除组的条目。

如果我像这样在User类中放置级联:

@ManyToMany(cascade=CascadeType.ALL)
@JoinTable(name = "USER_GROUP",
joinColumns =
{
    @JoinColumn(name = "GROUP_ID")
},
inverseJoinColumns =
{
    @JoinColumn(name = "USER_ID")
})
private Set<Group> groupList;

当我删除该组时,用户也会被删除!

有没有办法实现我想要的效果?

4个回答

47

按照您的映射方式,User 是关系的管理方,因此它将负责更新联接表。

JoinTable 映射从 User 改为 Group,并将 UsergroupList 属性设置为具有 mappedBy 属性。这将把群组改为关系的管理方,并使持久化/更新调用由群组来管理连接表。

但请注意,您将不能简单地将群组添加到用户中、保存用户并继续操作,而是必须将用户添加到群组中并保存群组才能看到更改。但是通过双向多对多,希望您能密切管理该关系。


嗯,如果我按照你说的做(我假设没有任何级联,因为你没有提到任何内容),当我将用户分配给某个组时,如果我删除该用户,会发生什么?连接表中的用户-组关系会被删除吗?组本身会被删除吗? - Hery
2
如果按照我说的方式操作,用户组关系将完全由组管理。这意味着在从所有组中删除用户之前,您可能根本无法删除用户。为了使其生效,您必须先将用户从组中移除,然后保存该组。仅删除用户将不会删除用户组关系或连接表。没有级联,删除组将删除关联但不会删除用户,这是您在原始问题中所要求的。 - digitaljoel
2
我明白了...所以我必须使用组作为管理方...有没有办法让两边都成为管理组?换句话说,当我删除一个用户时,用户和用户组也会被删除,但不会删除组;当我删除一个组时,组和用户组将被删除,但不会删除用户。 - Hery
继续上一个评论。或者只有一种困难的方法可用,即如果该组是管理方,则要删除用户,我必须使用组对象先删除用户,保存组对象,最后才能删除用户? - Hery
13
任何双向关系中只能有一方充当管理方。我尽可能避免双向关系,因为它们很难维护。你最好将其映射到满足您最常用情况的一侧,然后将该操作封装在服务方法中,以确保发生应该发生的所有事情,例如更新非管理方面的关系等。 - digitaljoel

6

如前所述,如果您不需要从用户方面进行级联操作,请将组所有者设为关系的所有者。

然后,尝试使用 cascade=CascadeType.MERGE,以便在删除组时不会级联删除用户。


5
从另一个角度看问题:
你可以在多对多映射表中添加外键约束。实际上,出于数据完整性的原因,你应该始终在数据库中使用外键。例如,在这种情况下,至少用户不能被删除,同时在映射表中留下孤立的行。
当你有外键时,你可以在约束上指定删除级联引用操作,数据库将自动管理映射表的删除,但不会删除实体,就像你所要求的那样。

1
你可以这样做。
@PreRemove
private void removeMembers() {
    this.memberList.clear();
}

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