Ho riscontrato un problema con un'associazione da molti a molti nel mio livello di persistenza. Il mio scenario è il seguente:Hibernate Associazione many-to-many: la raccolta a sinistra contiene elementi, ma la raccolta a destra è vuota
Un utente può avere diversi ruoli e un ruolo può avere diversi utenti collegati ad esso. Durante le prove ho riscontrato uno strano comportamento. Ho creato oggetti ruolo e diversi oggetti utente. Il ruolo è stato impostato su ciascuno degli utenti. Successivamente, gli utenti sono stati salvati utilizzando un DAO. Quindi viene caricato uno degli utenti per verificare se ha ricevuto il ruolo che gli è stato passato prima di salvare l'oggetto utente. Chiamando getRoles()
per l'utente viene visualizzato che il ruolo è stato impostato correttamente.
Per verificare se la direzione inversa funziona anche il ruolo oggetto viene caricato dal database utilizzando un ruolo DAO. Ma chiamare getUsers()
sull'oggetto ruolo restituisce solo un set vuoto, sebbene debba contenere tutti gli utenti con questo ruolo.
Ho controllato la tabella del database ma tutto sembra a posto. Le tabelle utente, ruolo e user_role sono state tutte compilate correttamente.
Quindi, perché l'oggetto ruolo non contiene alcun utente?
Sto utilizzando Hibernate e Spring con le seguenti classi.
classe User
@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>();
...
}
classe Ruolo
@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>();
...
}
Per provare Sto utilizzando il seguente codice in una classe di test 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("[email protected]");
user1.getRoles().add(userAdminRole);
user1.getRoles().add(userRole);
userDao.save(user1);
User user2 = new User();
user2.setEmail("[email protected]");
user2.getRoles().add(role);
userDao.save(user2);
User user3 = new User();
user3.setEmail("[email protected]");
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
dispiace ho dimenticato di dire una cosa. Quando ho testato il codice, ovviamente non ho contrassegnato l'associazione molti a molte due volte in ciascun file di classe. Invece ho usato l'opzione 1 o l'opzione 2 in ogni file di classe.
Dove stai iniziando e terminando le sessioni (o 'EntityManager's in JPA)? Sospetto che se chiudi una sessione dopo il 'save's e ne inizi una nuova prima di' load's, le cose funzioneranno, perché estraggono oggetti dal database piuttosto che dalla cache di sessione. –
La sessione viene aperta durante l'intero metodo di prova. Chiamare getSession() in uno dei metodi di DAO restituisce sempre lo stesso oggetto di sessione purché vengano chiamati in un metodo di test. – Flo