[hibernate 3] many-to-many, je tourne en rond
Bonjour,
Voilà mon soucis : je veux faire une association bidirectionnelle plusieurs à plusieurs et quoi que je fasse je tombe sur LazyInitializationException.
J'ai donc les tables USER, ROLE et REL_USER_ROLE(jointure).
J'ai compris que les collections sont chargées de façon "Lazy", c'est à dire à la demande donc il faut une session toujours ouverte quand on veut y accéder. OK.
J'ai également cru comprendre qu'il faut toujours implémenter les fonctions equals/hashCode dans le JavaBean. OK
Voilà des extraits des codes :
User.java
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
public class User
{
...
private Collection<Role> roles = new HashSet<Role>();
public int hashCode()
{
Iterator iRole = getRoles().iterator(); //ligne 65
while(iRole.hasNext()) {
result += 17 * (iRole.next() != null ? iRole.next().hashCode() : 1);
}
}
...
} |
Role.java
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
public class Role
{
...
private Collection<User> users = new HashSet<User>();
public int hashCode()
{
Iterator iUser = getUsers().iterator(); //ligne 41
while(iUser.hasNext()) {
result += 17 * (iUser.next() != null ? iUser.next().hashCode() : 1);
}
}
...
} |
User.hbm.xml
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
<hibernate-mapping>
<class table="USER" name="test.User">
<id access="property" name="id">
<generator class="native"/>
</id>
...
<set table="REL_USER_ROLE" cascade="all" name="roles">
<key column="user_id" not-null="true"/>
<many-to-many column="role_id" class="test.Role"/>
</set>
...
</class>
</hibernate-mapping> |
Role.hbm.xml
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13
|
<hibernate-mapping>
<class table="ROLE" name="test.Role">
<id access="property" name="id">
<generator class="native"/>
</id>
...
<set table="REL_USER_ROLE" inverse="true" cascade="all" name="users">
<key column="role_id" not-null="true"/>
<many-to-many column="user_id" class="test.User"/>
</set>
</class>
</hibernate-mapping> |
Corps de la fonction de test (obtenir la liste des roles) :
Code:
1 2 3 4 5
|
tx = session.beginTransaction();
Query q = session.createQuery( "from ROLE aRole left join fetch aRole.users" );
resultset = q.list();
tx.commit(); |
Et le message d'erreur :
Code:
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
|
May 5, 2010 9:38:18 AM org.hibernate.LazyInitializationException <init>
SEVERE: illegal access to loading collection
org.hibernate.LazyInitializationException: illegal access to loading collection
at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:341)
at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:86)
at org.hibernate.collection.PersistentSet.iterator(PersistentSet.java:163)
at test.Role.hashCode(Role.java:41)
at java.util.HashMap.put(HashMap.java:372)
at java.util.HashSet.add(HashSet.java:200)
at java.util.AbstractCollection.addAll(AbstractCollection.java:305)
at org.hibernate.collection.PersistentSet.endRead(PersistentSet.java:329)
at org.hibernate.engine.loading.CollectionLoadContext.endLoadingCollection(CollectionLoadContext.java:237)
at org.hibernate.engine.loading.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:222)
at org.hibernate.engine.loading.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:195)
at org.hibernate.loader.Loader.endCollectionLoad(Loader.java:877)
at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:865)
at org.hibernate.loader.Loader.doQuery(Loader.java:729)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
at org.hibernate.loader.Loader.loadCollection(Loader.java:1994)
at org.hibernate.loader.collection.CollectionLoader.initialize(CollectionLoader.java:36)
at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:565)
at org.hibernate.event.def.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:60)
at org.hibernate.impl.SessionImpl.initializeCollection(SessionImpl.java:1716)
at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:344)
at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:86)
at org.hibernate.collection.PersistentSet.iterator(PersistentSet.java:163)
at test.User.hashCode(User.java:65)
at java.util.HashMap.put(HashMap.java:372)
at java.util.HashSet.add(HashSet.java:200)
at java.util.AbstractCollection.addAll(AbstractCollection.java:305)
at org.hibernate.collection.PersistentSet.endRead(PersistentSet.java:329)
at org.hibernate.engine.loading.CollectionLoadContext.endLoadingCollection(CollectionLoadContext.java:237)
at org.hibernate.engine.loading.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:222)
at org.hibernate.engine.loading.CollectionLoadContext.endLoadingCollections(CollectionLoadContext.java:195)
at org.hibernate.loader.Loader.endCollectionLoad(Loader.java:877)
at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:865)
at org.hibernate.loader.Loader.doQuery(Loader.java:729)
at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
at org.hibernate.loader.Loader.doList(Loader.java:2220)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2104)
at org.hibernate.loader.Loader.list(Loader.java:2099)
at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:378)
at org.hibernate.hql.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:338)
at org.hibernate.engine.query.HQLQueryPlan.performList(HQLQueryPlan.java:172)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1121)
at org.hibernate.impl.QueryImpl.list(QueryImpl.java:79)
at test.USER_ManagementTest.getAllRoles(USER_ManagementTest.java:71)
at test.USER_ManagementTest.main(USER_ManagementTest.java:209) |
En fait on dirait que ça tourne en rond : quand je veux la liste des roles, hashCode va chercher les users de chaque role, et pour chaque user hibernate va chercher ces roles ...etc
Je n'ai trouvé sur le net aucun exemple complet de relation many-to-many.
Voyez-vous le problème ? Que me conseillez-vous?
Seule la clé primaire, il faut pas
Miday, d'après l'article ici:
http://community.jboss.org/wiki/EqualsandHashCode
ou là
Il ne faut pas utiliser uniquement la clé primaire.
Je peux cependant utiliser d'autres attributs (comme le login qui est unique aussi par contrainte BD, la "business key" ).
D'après la doc sun ici (item 8), je ne sais pas trop si pour une Collection il faut ou si il NE faut PAS traiter.
Pour info, dans une relation unidirectionnelle, la présence des Collection dans hashCode ne posaient pas de soucis...
Bon, bref, à défaut de comprendre, je vais virer les Collections de la fonction hashCode dans ce cas.
Pour les collections Sun , le hashCode est implémenté dans leurs classes abstraites
Oui c'est vrai que utiliser simplement l'id est un peu simpliste. Donc il faut utiliser une business key en combinaison linéaire avec des autres attributs primitifs et aussi des éléments du tableaux car d'après le document il faut traiter les tableaux :
vii. If the field is an array, treat it as if each element were a separate field.
That is, compute a hash code for each significant element by applying
these rules recursively, and combine these values as described in
step 2.b.
Pour les collections Sun , le hashCode est implémenté dans leurs classes abstraites et eux, il traite cela comme un tableau.
Si on utilise les HashSet par exemple:
Code:
1 2
| java.util.Set<String> set = new HashSet<String>();
set.hashCode(); // retourne un hashCode. |
java.util.HashSet hérite de AbstractSet qui elle implémente le hashCode().
En ce qui concerne les avantages d'utiliser HashCodeBuilder, je pense qu'il faut l'utiliser que quand on a énormément d'attributs et qu'on ne veut pas perdre de temps :D