簡體   English   中英

Hibernate多對多關聯:左側收集包含元素,但右側收集是空的

[英]Hibernate Many-to-many association: left hand side collection contains elements, but right hand side collection is empty

我的持久層中出現了多對多關聯的問題。 我的方案如下:

用戶可以擁有多個角色,角色可以擁有多個用戶。 在測試期間,我遇到了一個奇怪的行為。 我創建了角色對象和幾個用戶對象。 角色已設置給每個用戶。 在此之后,使用DAO保存用戶。 然后加載一個用戶以檢查他是否在保存用戶對象之前獲得了傳遞給他的角色。 在用戶上調用getRoles()表明角色設置正確。

要檢查反向是否也有效,角色對象將使用角色DAO從數據庫加載。 但是在角色對象上調用getUsers()只會返回一個空集,盡管它應該包含具有此角色的所有用戶。

我仔細檢查了數據庫表,但一切似乎都沒問題。 用戶,角色和user_role表都已正確填充。

那么為什么角色對象不包含任何用戶呢?

我正在使用Hibernate和Spring以下類。

用戶類

@Entity
@Table
public class User extends BusinessObject {

    ... 

    // Option 1
    @ManyToMany(fetch = FetchType.LAZY,
                cascade = CascadeType.ALL,
                targetEntity=Role.class)
    @JoinTable(name= "user_role",
               joinColumns = {@JoinColumn(name="user_id")},
               inverseJoinColumns = {@JoinColumn(name="role_id")})  

    // Option 2
    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinTable(name= "user_role", 
                   joinColumns = {@JoinColumn(name="user_id")},
           inverseJoinColumns = {@JoinColumn(name="role_id")})
    private Set<Role> roles = new HashSet<Role>();      

    ... 
}

角色類

@Entity
@Table
public class Role extends BusinessObject {
    ...

    // Option 1
    @ManyToMany(fetch = FetchType.LAZY, 
                cascade = CascadeType.ALL,
                mappedBy= "roles",
                targetEntity = User.class)

    // Option 2
    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinTable(name= "user_role", 
                   joinColumns = {@JoinColumn(name="role_id")},
                   inverseJoinColumns = {@JoinColumn(name="user_id")})
    private Set<User> users = new HashSet<User>();          

    ... 
}

測試我在JUnit測試類中使用以下代碼。

@Test
public void test(){     
    Transaction trans = sessionFactory.getCurrentSession().beginTransaction();

    Role userAdminRole = new Role();
    userAdminRole.setName(RoleName.USER_ADMIN);
    Role userRole = new Role();
    userRole.setName(RoleName.USER);

    User user1 = new User();
    user1.setEmail("user1@user.de");        
    user1.getRoles().add(userAdminRole);
    user1.getRoles().add(userRole);
    userDao.save(user1);

    User user2 = new User();
    user2.setEmail("user2@user.de");
    user2.getRoles().add(role);
    userDao.save(user2);

    User user3 = new User();
    user3.setEmail("user3@user.de");
    user3.getRoles().add(role);
    userDao.save(user3);            

    trans.commit();     

    User loadedUser = userDao.load(user1.getId());

            // Tests passes
    Assert.assertNotNull(loadedUser);
    Assert.assertEquals(user1, loadedUser);

    Set<Role> roles = loadedUser.getRoles();        

            // Tests passes
    Assert.assertEquals(2, roles.size());

    Role loadedUserAdminRole = roleDao.findByName(RoleName.USER_ADMIN);
    Set<User> users = loadedUserAdminRole.getUsers();

    // Test fails: Count is 0 instead of 3 !!!!!!!
    Assert.assertEquals(3, users.size());
}  

UPDATE

對不起,我忘了提一件事。 當我測試代碼時,我當然沒有在每個類文件中標記多次關聯。 相反,我在每個類文件中使用選項1或選項2。

問題可能來自於您兩次映射相同的雙向關聯。 如果你告訴Hibernate兩次關於同一個連接表或連接列,就會出現問題。 在雙向關聯中,關聯的一端必須映射關聯,另一端必須使用mappedBy屬性告訴Hibernate它是另一端的反轉。

由於多對多是完全對稱的,因此選擇一個結尾作為所有者(即映射關聯的結尾,因此具有@JoinTable注釋)。 另一方面是反向的,因此沒有@JoinTable注釋,但有一個mappedBy屬性。

例:

@Entity
@Table
public class User extends BusinessObject {

    ... 

    // This end is the owner of the association
    @ManyToMany
    @JoinTable(name= "user_role",
               joinColumns = {@JoinColumn(name="user_id")},
               inverseJoinColumns = {@JoinColumn(name="role_id")})  
    private Set<Role> roles = new HashSet<Role>();      
    ... 
}

@Entity
@Table
public class Role extends BusinessObject {
    ...

    // This end is not the owner. It's the inverse of the User.roles association
    @ManyToMany(mappedBy = "roles")
    private Set<User> users = new HashSet<User>();          

    ... 
}

補充說明:

  • targetEntity是沒有用的,因為Hibernate知道這多虧了泛型類型的Set 如果Set是Set<SomeInterface>那將會很有用
  • CascadeType.ALL肯定不是你想要的。 刪除用戶時是否要刪除用戶的所有角色? 具有這些角色的其他用戶應該怎么做?
  • 您幾乎應該始終初始化雙向關聯的兩端。 Hibernate考慮所有者端(即沒有mappedBy屬性的結尾)來持久化關聯。
  • 所有這些都在Hibernate參考文檔中進行了解釋。 閱讀它:它充滿了有用的信息,並不難理解。

在處理雙向多對多關聯時,您必須維護關聯的兩端。 在您的情況下,您還必須將用戶添加到角色。 將角色添加到用戶不足以建立雙向關聯,您可以在書籍Java Persistance with Hibernate中閱讀

與往常一樣,雙向關聯(無論多重性)要求您設置關聯的兩端。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM