我的持久层中存在多对多关联的问题。我的方案如下:
用户可以拥有多个角色,角色可以附加多个用户。在测试期间,我遇到了一个奇怪的行为。我创建了角色对象和几个用户对象。角色已设置给每个用户。在此之后,使用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());
}
更新
抱歉,我忘了提一件事。当我测试代码时,我当然没有在每个类文件中标记多次关联。相反,我在每个类文件中使用选项1或选项2。
答案 0 :(得分: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>();
...
}
附加说明:
Set
的泛型类型。如果Set为Set<SomeInterface>
mappedBy
属性的结尾)来保持关联。答案 1 :(得分:2)
在处理双向多对多关联时,您必须维护关联的两端。在您的情况下,您还必须将用户添加到角色。将角色添加到用户不足以建立双向关联,您可以阅读书籍Java Persistance with Hibernate:
与往常一样,双向关联(无论多重性)要求您设置关联的两端。