2012-05-18 88 views
2

這是一個關於Hibernate生成的關於在多對多映射下刪除一個關係的問題,而不是「級聯」問題。刪除JPA中多對多關係時的奇怪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; 
} 

和映射效果很好,休眠自動創建三個表這種映射

table user 
table role 
table user_role 

現在這裏是問題,我想要的只是從一個用戶刪除一個角色(不刪除用戶或角色,只是一個用戶和一個角色之間的一個關係,意味着只需要從表user_role刪除一個記錄) 。這裏是我試過的代碼:

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); 
} 

當我執行這段代碼的時候,它的作用就是把這個角色從用戶上移除了。但是,當我檢查該冬眠它生成的SQL,這不是我所期待的,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}) 

所以從一個用戶刪除一個角色,休眠首先從用戶刪除所有角色,然後將那些不應該被刪除的角色逐個添加回給用戶。

而我想到的是隻是一個SQL語句歸檔:

delete from user_role where user_id = {userId} and role_id = {role_id} 

我知道有一些其他的方式,我可以像引入另一個實體UserRoleMapping其映射表user_role歸檔此,則直接刪除一個UserRoleMapping實例將從一個用戶中刪除一個角色;但我想知道是否有任何解決方案可以滿足當前解決方案的需求。

+0

隨着您當前的映射,這是它的方式。可能會有一些特定於hibernate的「提示」/ etc來改變這種行爲。但請記住,「過早優化是所有邪惡(tm)的根源」 – esej

+0

這僅僅是一個例子,而不是我們應用程序中的真實情況。在我們的應用程序中,一個模型可能有很多其他模型,因此刪除一個關係時會引發大量的sql語句(可能會有數百個)。 – donnior

回答

2

我還沒有檢查this explanation是否屬實,但它有很好的一點。

沒有任何索引列,List實際上是一個bag:no order,並且允許重複。所以Hibernate認爲你有可能在用戶的角色列表中有兩次相同的角色。

因此發佈delete from user_role where user_id = ? and role_id = ?是不可能的,因爲它可能會刪除多個角色,而不是您從列表中刪除的角色。

嘗試添加索引列或使用Set<Role>而不是List<Role>

+0

謝謝我稍後再試。 – donnior

+0

它的工作原理,再次感謝! – donnior

+0

切換到列表設置適用於我,但我寧願使用列表。連接表的主鍵已經包含連接和反向連接外鍵,這兩個外鍵是表中唯一的兩列,所以我不明白爲什麼這不起作用。還有別的嗎? – Ryan