[英]Hibernate Many-to-many association: left hand side collection contains elements, but right hand side collection is empty
I got a problem with a many to many association in my persistence layer. 我的持久层中出现了多对多关联的问题。 My scenario is the following:
我的方案如下:
A user can has several roles and a role can have several user attached to it. 用户可以拥有多个角色,角色可以拥有多个用户。 During the tests I encountered a strange behavior.
在测试期间,我遇到了一个奇怪的行为。 I created role object and several user objects.
我创建了角色对象和几个用户对象。 The role was set to each of the users.
角色已设置给每个用户。 After this the users were saved using a DAO.
在此之后,使用DAO保存用户。 Then one of the user gets loaded to check whether he got the role that was passed to him before saving the user object.
然后加载一个用户以检查他是否在保存用户对象之前获得了传递给他的角色。 Calling
getRoles()
on the user shows that the role was set correctly. 在用户上调用
getRoles()
表明角色设置正确。
To check whether the inverse direction also works the role object gets loaded from the database using a role DAO. 要检查反向是否也有效,角色对象将使用角色DAO从数据库加载。 But calling
getUsers()
on the role object just returns an empty set, although it should contain all the users with this role. 但是在角色对象上调用
getUsers()
只会返回一个空集,尽管它应该包含具有此角色的所有用户。
I double checked the database table but everything seems all right. 我仔细检查了数据库表,但一切似乎都没问题。 User, role and user_role table were all filled correctly.
用户,角色和user_role表都已正确填充。
So why doesn't the role object contain any user? 那么为什么角色对象不包含任何用户呢?
I'm using Hibernate and Spring with the following classes. 我正在使用Hibernate和Spring以下类。
User class 用户类
@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>();
...
}
Role class 角色类
@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>();
...
}
To test I'm using the following code in a JUnit test class. 测试我在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 UPDATE
Sorry I forgot to mention one thing. 对不起,我忘了提一件事。 When I tested the code I of course didn't mark the many to many association twice in each class file.
当我测试代码时,我当然没有在每个类文件中标记多次关联。 Instead I used either option 1 or option 2 in each class file.
相反,我在每个类文件中使用选项1或选项2。
The problem probably comes from the fact that you mapped the same bidirectional association twice. 问题可能来自于您两次映射相同的双向关联。 If you tell Hibernate twice about the same join table or join column, there is a problem.
如果你告诉Hibernate两次关于同一个连接表或连接列,就会出现问题。 In a bidirectional association, one of the ends of the association must map the association, and the other one must tell Hibernate that it's the inverse of the other end, using the
mappedBy
attribute. 在双向关联中,关联的一端必须映射关联,另一端必须使用
mappedBy
属性告诉Hibernate它是另一端的反转。
Since a many-to-many is completely symmetric, choose one of the end to be the owner (ie the end which maps the association, and thus have the @JoinTable
annotation). 由于多对多是完全对称的,因此选择一个结尾作为所有者(即映射关联的结尾,因此具有
@JoinTable
注释)。 The other side is just the inverse, and thus doesn't have a @JoinTable
annotation, but has a mappedBy
attribute. 另一方面是反向的,因此没有
@JoinTable
注释,但有一个mappedBy
属性。
Example: 例:
@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>();
...
}
Additional notes: 补充说明:
Set
. Set
。 It would be useful if the Set was a Set<SomeInterface>
Set<SomeInterface>
那将会很有用 mappedBy
attribute) to persist the association. mappedBy
属性的结尾)来持久化关联。 When dealing with a bidirectional many-to-many association you have to maintain both ends of the association. 在处理双向多对多关联时,您必须维护关联的两端。 In your case, you have to add the user to the role as well.
在您的情况下,您还必须将用户添加到角色。 Adding the role to the user isn't sufficient to establish a bidirectional association as you can read in book Java Persistance with Hibernate :
将角色添加到用户不足以建立双向关联,您可以在书籍Java Persistance with Hibernate中阅读 :
As always, a bidirectional association (no matter of what multiplicity) requires that you set both ends of the association.
与往常一样,双向关联(无论多重性)要求您设置关联的两端。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.