У меня проблема со связью многие ко многим в моем слое постоянства. Мой сценарий следующий:
Пользователь может иметь несколько ролей, и к роли может быть прикреплено несколько пользователей. Во время тестов я столкнулся со странным поведением. Я создал объект роли и несколько пользовательских объектов. Роль была назначена каждому из пользователей. После этого пользователи были сохранены с использованием 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 в каждом файле класса.