Hibernate多对多关联:左侧收集包含元素,但右侧收集是空的

Flo*_*Flo 7 java many-to-many hibernate hibernate-mapping

我的持久层中出现了多对多关联的问题.我的方案如下:

用户可以拥有多个角色,角色可以拥有多个用户.在测试期间,我遇到了一个奇怪的行为.我创建了角色对象和几个用户对象.角色已设置给每个用户.在此之后,使用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>();      

    ... 
}
Run Code Online (Sandbox Code Playgroud)

角色类

@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>();          

    ... 
}
Run Code Online (Sandbox Code Playgroud)

测试我在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());
}  
Run Code Online (Sandbox Code Playgroud)

UPDATE

对不起,我忘了提一件事.当我测试代码时,我当然没有在每个类文件中标记多次关联.相反,我在每个类文件中使用选项1或选项2.

JB *_*zet 9

问题可能来自于您两次映射相同的双向关联.如果你告诉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>();          

    ... 
}
Run Code Online (Sandbox Code Playgroud)

补充说明:

  • targetEntity没用,因为Hibernate知道它的泛型类型Set.如果Set是a,那将会很有用Set<SomeInterface>
  • CascadeType.ALL肯定不是你想要的.删除用户时是否要删除用户的所有角色?具有这些角色的其他用户应该怎么做?
  • 您几乎应该始终初始化双向关联的两端.Hibernate考虑所有者端(即没有mappedBy属性的结束)来持久化关联.
  • 所有这些都在Hibernate参考文档中进行了解释.阅读它:它充满了有用的信息,并不难理解.