2010-10-12 5 views
1

J'ai un problème avec Hibernate et l'exception LazyInitializationException. J'ai cherché et trouvé beaucoup de réponses, mais je ne peux pas les utiliser pour résoudre mon problème, parce que je dois dire, je suis nouveau à Hibernate.Hibernate -> LazyInitializationException avec une relation n: m

I exécuter des tests JUnit, en cas de l'erreur celui-ci:

@Test 
public void testAddPerson() { 
    Set<Person> persons = service.getAllPersons(); 

    // create new person 
    Person person = new Person(); 
    person.setEmail("[email protected]"); 
    Project testProject = serviceProj.findProjectById(1); 

    HashSet<Project> lister = new HashSet<Project>(); 
    lister.add(testProject); 
    person.setProjects(lister); 
    service.addPerson(person); 

    testProject.getPersons().add(person); 
    ... 
} 

La dernière ligne montre:

testProject.getPersons().add(person); 

jette cette erreur:

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.doe.john.domain.Project.persons, no session or session was closed 

personne et projet bidirectionnel n: m:

Person.java:

@ManyToMany(mappedBy="persons") 
private Set<Project> projects = new HashSet<Project>(); 

Project.java:

@ManyToMany 
@JoinTable(name = "Project_Person", 
    joinColumns = {@JoinColumn(name="project_id", referencedColumnName="id")}, 
    inverseJoinColumns = {@JoinColumn(name="person_id", referencedColumnName="id")} 
) 
private Set<Person> persons = new HashSet<Person>(); 

Alors, quel est le problème?

Répondre

1

Le problème est que, par défaut, la collection est chargée paresseusement. Cela signifie qu'il ne sera pas chargé à partir de la base de données tant que l'accès n'aura pas été effectué. Pour le charger, vous aurez besoin d'une session/transaction active.

La solution la plus simple est de le changer en FethType.EAGER, ce qui garantit que la collection est immédiatement remplie.

--update--

J'ai récemment eu le problème même et je fini par modifier mon service réel pour faire face à ce genre de chose. Déclarez une méthode addPerson dans votre classe ProjectService.

public void addPersonTo(Project project, Person person) 
{ 
    project = em.merge(project); //EntityManager, not sure what you are using but you get the idea hopefully 
    project.addPerson(person); 
} 
+0

Oui, mais si je voulais fais ici FetchType.EAGER, j'ai dans ma vraie application des choses chargées qui ne sont pas nécessaires. – Tim

+0

Ai-je besoin de la ligne "testProject.getPersons(). Add (person);" ? J'ajoute un projet à une personne. Donc ça devrait être dans la tbale n: m join, n'est-ce pas? – Tim

+0

Cela signifie que vous devez aller pour ma première suggestion ... – willcodejavaforfood

0

Pouvez-vous essayer

person.getProjects().Add(testProject) 

au lieu de

HashSet<Project> lister = new HashSet<Project>(); 
lister.add(testProject); 
person.setProjects(lister); 

Vous devriez faire cela puisque vous auriez autrement souffler loin une collection gérée de mise en veille prolongée.

+0

Je l'ai fait, mais la même erreur dans la ligne testProject.getPersons(). Add (person); – Tim

0

De référence Mise en veille prolongée:

Par défaut, Hibernate3 utilise select pour les collections fetching paresseux et proxy pour les associations allant chercher paresseux univoques. Ces valeurs par défaut ont un sens pour la plupart des associations dans la majorité des applications.

Si vous définissez hibernate.default_batch_fetch_size, Hibernate utilisera l'optimisation par lots pour la récupération paresseuse. Cette optimisation peut également être activée à un niveau plus granulaire.

Veuillez noter que l'accès à une association paresseuse en dehors du contexte d'une session Hibernate ouverte entraînera une exception.Par exemple:

s = sessions.openSession(); 
Transaction tx = s.beginTransaction(); 

User u = (User) s.createQuery("from User u where u.name=:userName") 
.setString("userName", userName).uniqueResult(); 
Map permissions = u.getPermissions(); 

tx.commit(); 
s.close(); 

Integer accessLevel = (Integer) permissions.get("accounts"); // Error! 

Depuis la collecte des autorisations n'a pas été initialisé lors de la session a été fermée, la collection ne sera pas en mesure de charger son état. Hibernate ne supporte pas l'initialisation paresseuse pour les objets détachés. Cela peut être résolu en déplaçant le code qui lit de la collection juste avant que la transaction soit validée.

Vous pouvez également utiliser une collection ou une association non fainéante en spécifiant lazy = "false" pour le mappage d'association. Cependant, il est prévu que l'initialisation paresseuse soit utilisée pour presque toutes les collections et associations. Si vous définissez trop d'associations non-paresseuses dans votre modèle d'objet, Hibernate récupérera la base de données entière dans la mémoire dans chaque transaction. D'autre part, vous pouvez utiliser l'extraction de jointure, qui est par nature non-paresseux, au lieu de sélectionner la récupération dans une transaction particulière. Nous allons maintenant expliquer comment personnaliser la stratégie de récupération. Dans Hibernate3, les mécanismes de choix d'une stratégie de récupération sont identiques pour les associations et collections à valeur unique.

Vous devez donc fermer la session après avoir accédé à la collection!

Au lieu de:

service.addPerson(person); 

testProject.getPersons().add(person); 

Je pense que vous devriez avoir:

testProject.getPersons().add(person); 
service.addPerson(person); 
+0

Je pense que la méthode service.addPerson ferme la session alors appelez-la à la fin. – virgium03

+0

J'ai changé l'ordre à testProject.getPersons(). Add (person); service.addPerson (personne); Malheureusement même erreur. – Tim

+0

Veuillez noter que l'accès à une association paresseuse en dehors du contexte d'une session Hibernate ouverte entraînera une exception. Vous ouvrez et fermez des sessions dans les méthodes de service (utilisez-vous spring btw) et lorsque vous essayez d'ajouter la personne au projet, vous êtes en dehors de la portée de la session de projet. – virgium03

1

Mettez votre code à l'intérieur d'une transaction - cela résoudra le problème pour vous

+0

comment puis-je faire cela? – Tim

+0

Mon DaoImpl ressemble à: public void updateProject (Projet p) { SessionFactory sessionFactory = HibernateUtil.getSessionFactory(); Session sess = sessionFactory.getCurrentSession(); Transaction tx = sess.beginTransaction(); sess.update (p); tx.commit(); } J'ai donc une transaction ?! – Tim

+0

@Tim: Non, vous ne le faites pas. Après avoir quitté votre 'updateProject', votre' Project p' est détaché. Et si vous essayez d'accéder à n'importe quelle collection de 'p' vous obtenez LazyInit. Pour éviter cela, vous devriez mettre votre appel 'getPersons()' entre BETWEEN 'beginTransaction()' et 'commit()' appels –

Questions connexes