我在持久层中存在多对多关联的问题。我的情况如下:
一个用户可以具有多个角色,并且一个角色可以具有与其连接的多个用户。在测试期间,我遇到了一个奇怪的行为。我创建了角色对象和几个用户对象。该角色已设置给每个用户。此后,使用DAO保存了用户。然后,加载一个用户,以检查他是否在保存用户对象之前获得了传递给他的角色。呼叫getRoles()用户表明角色设置正确。
getRoles()
要检查反方向是否也起作用,可以使用角色DAO从数据库中加载角色对象。但是,调用getUsers()角色对象只会返回一个空集,尽管它应包含具有该角色的所有用户。
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()); }
更新
对不起,我忘了提到一件事。当我测试代码时,我当然没有在每个类文件中两次标记多对多关联。相反,我在每个类文件中使用了选项1或选项2。
问题可能是由于您两次映射了相同的双向关联。如果您对相同的联接表或联接列两次告诉Hibernate,则存在问题。在双向关联中,关联的一端必须映射该关联,而另一端必须使用mappedBy属性告诉Hibernate这是另一端的逆。
mappedBy
由于多对多是完全对称的,因此请选择一端作为所有者(即,映射关联并因此具有@JoinTable注释的一端)。另一侧只是反面,因此没有@JoinTable注释,但具有mappedBy属性。
@JoinTable
例:
@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>(); ... }
补充笔记:
Set
Set<SomeInterface>