Bonsoir,
J'ai un problème avec JBoss/Hibernate concernant les relations "manyToMany"...
J'ai 2 beans entités:
- user
- role
En toute logique, un "user" peut posséder 1 ou plusieurs "role" ... Pareil pour un "role"; je dirais meme plus: cela peut varier de 0 à plusieurs ...
Donc j'ai créé mes beans entité de la sorte:
User.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| //by loopx
//08/01/08
package be.loopx.pixmania.ejb.entity;
import java.io.Serializable;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
@Entity
@Table(name="user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
private String login;
private String password;
private List<Role> roles=new ArrayList<Role>();
public void setLogin(String login) {
this.login = login;
}
@Column(length=20, nullable=false, unique=true)
public String getLogin() {
return login;
}
public void setPassword(String password) {
this.password = password;
}
@Column(length=20, nullable=false)
public String getPassword() {
return password;
}
public void setId(int id) {
this.id = id;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
public void setRoles(List<Role> roles) {
this.roles=roles;
}
@ManyToMany(mappedBy="users", cascade={CascadeType.ALL})
public List<Role> getRoles() {
return this.roles;
}
} |
Role.java:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| package be.loopx.pixmania.ejb.entity;
import java.io.Serializable;
@Entity
@Table(name="role")
public class Role implements Serializable {
private static final long serialVersionUID = 1L;
private int id;
private String name;
private List<User> users=new ArrayList<User>();
public void setName(String n) {
this.name = n;
}
@Column(length=50, nullable=false, unique=true)
public String getName() {
return name;
}
public void setId(int id) {
this.id = id;
}
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
public void setUsers(List<User> users) {
this.users = users;
}
@ManyToMany
public List<User> getUsers() {
return users;
}
} |
J'ai ajouté (dans les 2 entités) des List contenant les beans entité qui lui sont lié.
J'ai eu une erreur pendant un moment, je viens de comprendre ce que c'était. Voici l'erreur:
1 2 3 4 5 6 7 8
|
01:56:30,346 ERROR [LazyInitializationException] failed to lazily initialize a collection of role: be.loopx.pixmania.ejb.entity.User.roles, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: be.loopx.pixmania.ejb.entity.User.roles, no session or session was closed
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:358)
at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:350)
at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:97)
at org.hibernate.collection.PersistentBag.size(PersistentBag.java:225)
at be.loopx.pixmania.ejb.facade.UserFacadeBean.addRoleToUser(UserFacadeBean.java:63) |
En fait, un module web (.war) est client des EJB (plus précisément, une servlet). Celle-ci communique via ce schéma:
Servlet => privateEJB(session stateful) => userEJB_facade(session stateless) => entity => base de donnée
Voici la méthode "addRoleToUser" de la facade "UserFacadeBean":
1 2 3 4 5 6
| public void addRoleToUser(Role role, User user) {
role.getUsers().add(user); //add 1 user to the role
user.getRoles().add(role); //add 1 role to the user
em.merge(user);
} |
Cette méthode est appelée par le bean stateful "privateEJB" dont voici un extrait:
1 2 3 4 5 6 7
|
//bind administrator to admin
usersList=userEJB_facade.listFiltered("where o.login='admin'");
rolesList=roleEJB_facade.listFiltered("where o.name='administrator'");
System.out.print(this.getClass().getName()+"> USER="+usersList.get(0).getLogin()+" / ROLE="+rolesList.get(0).getName());
userEJB_facade.addRoleToUser(rolesList.get(0), usersList.get(0)); |
Ce code est situé dans le constructeur du bean session stateful. Quand un client se connecte au module Web (via le navigateur), la servlet est instanciée. Dans chaque session HTTP, le bean privateEJB est injecté.
Pour les tests (actuellement), lors de l'instanciation du bean privateEJB, je crée le user "admin" et le role "administrator": aucun problème (dans la bd, j'ai tout ce qu'il faut, y compris une table "role_user" contenant (enfin, ne contenant encore rien) les liens entre user et role.
La méthode "addRoleToUser" est la pour pour lié ces 2 entités. Seulement voilà, j'ai l'erreur de "lazy" cité plus haut. J'ai trouvé pourquoi: dans la facade utilisateur (dans la méthode "addRoleToUser"), je récupère 2 beans entités. Ceux-ci sont considéré comme "non attaché" ce qui pose donc le problème de "lazy". Pour tester, j'ai ajouter cette ligne dans la méthode d'ajout d'un role:
user=findById(user.getId()); //re-attach user
AVANT le:
user.getRoles().add(role); //add 1 role to the user
de manière à re-attacher le bean entité ... L'erreur est passé ... enfin non, puisque j'ai le meme souci avec le bean entité "role" passé à la méthode:
1 2
| 02:07:30,991 WARN [arjLoggerI18N] [com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator_2] TwoPhaseCoordinator.beforeCompletion - failed for com.arjuna.ats.internal.jta.resources.arjunacore.SynchronizationImple@1a46497
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: be.loopx.pixmania.ejb.entity.Role |
Alors voilà, je me rend compte de ceci: je ne peux pas faire un "findById" sur chaque entité dans la facade utilisateur (userEJB_facade), logique (regardez le schéma ci-dessous
Il m'est impensable de faire l'injection de la facade "role" dans la facade "user"! Donc, le "truc" de l'ajout de la ligne permettant de ratacher le bean entité "user" dans la facade utilisateur est donc à annuler. Il faut que cela se passe AVANT l'appel à la facade!
J'essaie donc de modifier le bean privateEJB (son constructeur) de manière à ce qu'il soit ainsi:
1 2 3 4 5 6 7 8
| //bind administrator to admin
usersList=userEJB_facade.listFiltered("where o.login='admin'");
rolesList=roleEJB_facade.listFiltered("where o.name='administrator'");
System.out.print(this.getClass().getName()+"> USER="+usersList.get(0).getLogin()+" / ROLE="+rolesList.get(0).getName());
User user=userEJB_facade.findById(usersList.get(0).getId());
//userEJB_facade.addRoleToUser(rolesList.get(0), usersList.get(0));
userEJB_facade.addRoleToUser(rolesList.get(0), user); |
en conclusion, avant l'appel à "addRoleToUser", j'essaie de re-attacher le user. J'ai plusieurs question à ce sujet:
- pourquoi est-ce que la méthode de recherche (avec du code EJBQL) n'a t'elle pas attaché le bean entité USER et ROLE ??
- pourquoi est-ce que un "findById" juste avant la facade ne fonctionne pas ?
Parce que le problème est bien la: impossible d'attacher le bean USER (trouvé avec la requette login=admin) AVANT l'appel à la méthode "addRoleToUser". Je bloque, pour tout vous dire, ca fait 1 semaine que je me casse la tête, et rien n'avance. Je pense que j'ai un problème de logique dans le développement ou alors, un problème de "relation" entre bean.
Je précise que l'erreur (après avoir ajouté le findbyid du user dans le constructeur du bean privateEJB) est exactement la meme que au tout début (donc, le findbyid placé dans privateEJB ne fonctionne pas, alors que si je l'appel quand je suis dans la facade, cela fonctionne).
Voilà, je suis perdu, je pense que je n'y arriverais jamais sans une petite aide de votre part. Donc, je vous remercie d'avance de me lire.
EDIT: j'ai d'autre question:
- qu'elle est la méthode "correct" pour attaché un bean ? (findById ?)
- est-ce que la méthode utilisée pour "lier" 2 beans entité est correct ?
- un ptit tuto peut etre, avec exemple parce que, je bloque...
EDIT2: voici une mini partie des logs, pour ceux que ca intéresse ...
1 2 3
| 02:49:12,031 INFO [[/web_base]] be.loopx.pixmania.servlet.ControlServlet> All context have been initialized
02:49:12,062 INFO [STDOUT] be.loopx.pixmania.ejb.session.PrivateBean> All context have been initialized
02:49:12,246 INFO [STDOUT] be.loopx.pixmania.ejb.session.PrivateBean> USER=admin / ROLE=administrator |
actuellement, après ces logs, j'ai les erreurs cité plus haut
Partager