在JPA中删除多对多关系时的奇怪的SQL行为

时间:2012-05-18 16:27:54

标签: java hibernate jpa-2.0

这是关于Hibernate生成的关于在多对多映射下删除一个关系而不是“级联”问题的sql的问题。

我使用JPA 2和hibernate作为其实现。

我有两个模型,用户和角色。一个用户可以拥有多个角色,一个角色可以拥有多个用户,因此它们是多对多映射:

@Entity
class User{
    @Id Long id;

    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
    @JoinTable(name = "user_role", inverseJoinColumns = @JoinColumn(name = "role_id"),
                joinColumns = @JoinColumn(name = "user_id"))
    private List<Role> roles;
 }

@Entity
class Role{
    @Id Long id;

    @ManyToMany(fetch = FetchType.LAZY,cascade = CascadeType.REFRESH, mappedBy = "roles")
    private List<User> users; 
 }

并且映射效果很好,hibernate会自动为这个映射创建三个表

table user
table role
table user_role

现在问题就出现了,我想要的只是从一个用户中删除一个角色(不是删除用户或角色,只是一个用户和一个角色之间的一个关系,意味着只需要从表中删除一个记录{{ 1}})。这是我试过的代码:

user_role

当我执行此代码时,它的作用已从用户中删除了。但是当我检查为它生成hibernate的sql时,它不是我所期望的,hibernate生成的sql是:

public void removeOneRoleFromUser(long userId, long roleId){
    User user = userService.getById(userId);
    Role role = roleService.getById(roleId);
    user.getRoles().remove(role);     //here
    userService.update(user);
}

因此,要从一个用户删除一个角色,hibernate首先删除用户的所有角色,然后将那些不应该逐个删除的角色添加回用户。

而我所期待的只是一个sql语句存档它:

delete from user_role where user_id = {userId}
insert into user_role values({user_id}, {role_id_not_removed}) 
...
insert into user_role values({user_id}, {another_role_id_not_removed}) 

我知道还有其他一些方法可以归档这个,比如引入映射到表delete from user_role where user_id = {userId} and role_id = {role_id} 的另一个实体UserRoleMapping,然后直接删除一个UserRoleMapping实例将从一个用户中删除一个角色;但我想知道有什么解决方案可以让我对目前的解决方案有所期待。

1 个答案:

答案 0 :(得分:2)

我没有检查this explanation是否属实,但它有好处。

没有任何索引列,List实际上是一个包:没有订单,允许重复。因此,Hibernate认为您可能在用户角色列表中两次拥有相同的角色。

因此无法发布delete from user_role where user_id = ? and role_id = ?,因为它可能会删除多个角色,而不仅仅是您从列表中删除的角色。

尝试添加索引列,或使用Set<Role>代替List<Role>