2008-12-06 11 views
67

En voici un qui me rend perplexe. J'essaie d'implémenter une structure basique d'Hibernate DAO, mais j'ai un problème.hibernate: LazyInitializationException: impossible d'initialiser le proxy

Voici le code essentiel:

int startingCount = sfdao.count(); 
sfdao.create(sf); 
SecurityFiling sf2 = sfdao.read(sf.getId()); 
sfdao.delete(sf); 
int endingCount = sfdao.count(); 

assertTrue(startingCount == endingCount); 
assertTrue(sf.getId().longValue() == sf2.getId().longValue()); 
assertTrue(sf.getSfSubmissionType().equals(sf2.getSfSubmissionType())); 
assertTrue(sf.getSfTransactionNumber().equals(sf2.getSfTransactionNumber())); 

Il échoue sur la troisième assertTrue où il essaie de comparer une valeur de sf à la valeur correspondante de SF2. Voici l'exception:

org.hibernate.LazyInitializationException: could not initialize proxy - no Session 
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:86) 
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:140) 
    at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190) 
    at com.freightgate.domain.SecurityFiling_$$_javassist_7.getSfSubmissionType(SecurityFiling_$$_javassist_7.java) 
    at com.freightgate.dao.SecurityFilingTest.test(SecurityFilingTest.java:73) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:40) 

Répondre

15

Cela signifie généralement que la session Hibernate propriétaire est déjà fermée. Vous pouvez faire une des actions suivantes pour y remédier:

  1. selon objet la création de ce problème, utilisez HibernateTemplate.initialize(object name)
  2. Utilisez lazy=false dans vos fichiers HBM.
+0

eu le même problème et paresseux = false le fixe . Merci – autonomatt

+1

maintenant dans mon cas j'utilise 'lazy = false' pour tous les niveaux de dao, mais il s'avère que les performances de l'application est lent à cause de cela, a essayé de mettre' lazy = true', mais maintenant lazyException sont levées, des suggestions comment cela peut être fixé. – Rachel

+0

pakore, pourriez-vous indiquer pourquoi n'est pas la solution et comment le comprendre? – Victor

2

D'accord, enfin compris où je me trouvais négligents. J'étais sous la notion erronée que je devrais emballer chaque méthode DAO dans une transaction. Terriblement faux! J'ai appris ma leçon. J'ai transporté tout le code de transaction de toutes les méthodes DAO et ai mis en place des transactions strictement au niveau de la couche application/gestionnaire. Cela a totalement résolu tous mes problèmes. Les données sont chargées paresseusement comme j'en ai besoin, enveloppées et fermées une fois que je fais le commit.

La vie est ... :) beau

+0

Je ne suis pas sûr de bien comprendre, car je ne me souviens pas avoir vu cela dans d'autres projets. Mais vous avez raison: l'ajout de '@ org.springframework.transaction.annotation.Transactional (readOnly = true)' aux méthodes de la couche de service a corrigé le problème. (Dans cette couche, nous récupérons une entité et la transmettons à un autre appel au DAO. – Arjan

1

Je pense que Piko signifie dans sa réponse qu'il ya le fichier HBM. J'ai un fichier appelé Tax.java. Les informations de mappage sont enregistrées dans le fichier hbm (= mappage d'hibernation). Dans la balise de classe, il existe une propriété appelée paresseuse. Définissez cette propriété sur true. L'exemple hbm suivant montre un moyen de définir la propriété paresseuse sur false.

` id ... »

Si vous utilisez des annotations plutôt regarder dans le Documenation de veille prolongée. http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/

J'espère que cela a aidé.

4

si vous utilisez Lazy le chargement de votre méthode doit être annotée avec

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) pour EJB Session Stateless

3

Nous avons rencontré cette erreur aussi bien. Ce que nous avons fait pour résoudre le problème, c'est que nous avons ajouté un paresseux = faux dans le fichier de mappage Hibernate. Il semble que nous ayons une classe A dans une session qui charge une autre classe B. Nous essayons d'accéder aux données de la classe B, mais cette classe B est détachée de la session.Pour que nous accédions à cette classe B, nous devions spécifier dans le fichier de mappage Hibernate de classe A l'attribut lazy = false. Par exemple,

 <many-to-one name="classA" 
       class="classB" 
       lazy="false"> 
     <column name="classb_id" 
       sql-type="bigint(10)" 
       not-null="true"/> 
    </many-to-one> 
68

Le problème est que vous essayez d'accéder à une collection dans un objet qui est detached. Vous devez rattacher l'objet avant d'accéder à la collection à la session en cours. Vous pouvez le faire par

session.update(object); 

L'utilisation lazy=false n'est pas une bonne solution parce que vous jetez la fonction Lazy Initialisation de mise en veille prolongée. Lorsque lazy=false, la collection est chargée en mémoire en même temps que l'objet est demandé. Cela signifie que si nous avons une collection avec 1000 éléments, ils seront tous chargés en mémoire, même si nous allons y accéder ou non. Et ce n'est pas bon.

Veuillez lire ce article où il explique le problème, les solutions possibles et pourquoi est mis en œuvre de cette façon. En outre, pour comprendre les sessions et les transactions, vous devez lire this other article.

7

Si vous utilisez Hibernate avec des annotations JPA, cela sera utile. Dans votre classe de service, il devrait y avoir un setter pour le gestionnaire d'entités avec @PersistenceContext. changez ceci en @PersistenceContext (type = PersistenceContextType.EXTENDED). Ensuite, vous pouvez accéder à la propriété paresseuse dans n'importe où.

+1

Cela n'est pas correct si vous ne gérez pas manuellement vos transactions. Le type de contexte de persistance Spring EXTENDED est utilisé pour un modèle de conversation long, pas pour le modèle session par requête demandé par l'OP. – HDave

+0

Je suppose que @HDave a raison; voir aussi [Quelle est la différence entre le contexte de persistance à l'échelle de la transaction et le contexte de persistance étendue?] (http://stackoverflow.com/questions/2547817/what-is-the-difference-between-transaction-scoped-persistence-context- Et-étendre) – Arjan

2

Si vous connaissez l'impact de lazy=false et que vous voulez toujours fait comme défaut (par exemple, à des fins de prototypage), vous pouvez utiliser une des opérations suivantes:

  • si vous utilisez la configuration XML: ajouter default-lazy="false" à votre élément <hibernate-mapping>
  • si vous utilisez la configuration d'annotation: ajouter @Proxy(lazy=false) à votre classe d'entité (s)
11

Voir mon article. J'ai eu le même problème - LazyInitializationException - et voici la réponse que j'ai finalement trouvée:
http://community.jboss.org/wiki/LazyInitializationExceptionovercome
Définir lazy = false n'est pas la réponse - il peut charger tout en même temps, et ce n'est pas nécessairement bon. Exemple:
1 table d'enregistrement A Références:
5 fiches de références Tableau B:
25 fiches de références Tableau C:
125 enregistrements tableau D
...
etc. C'est un exemple de ce qui peut faux.
--Tim Sabin

+0

Vous devez expliquer la solution ici, pas de lien vers un site Web tiers .. –

1

utilisation Hibernate.initialize pour le champ paresseux

2

Il semble que votre DAO utilisent session. Ainsi, une nouvelle session est ouverte puis fermée pour chaque appel à une méthode DAO. Ainsi, l'exécution du programme peut être repris comme:

// open a session, get the number of entity and close the session 
int startingCount = sfdao.count(); 

// open a session, create a new entity and close the session 
sfdao.create(sf); 

// open a session, read an entity and close the session 
SecurityFiling sf2 = sfdao.read(sf.getId()); 

// open a session, delete an entity and close the session 
sfdao.delete(sf); 

etc... 

Par défaut, la collecte et l'association dans une entité sont paresseux: ils sont chargés de la base de données sur demande.Ainsi:

sf.getSfSubmissionType().equals(sf2.getSfSubmissionType())

jette une exception parce qu'il demande un nouveau chargement de la base de données, et la session associée à la charge de l'entité a déjà été fermée.

Il y a deux approches pour résoudre ce problème:

  • créer une session à tous notre code ci-joint. Ainsi, cela signifierait de modifier votre contenu DAO pour éviter d'ouvrir une seconde session.

  • créez une session, puis mettez à jour (c'est-à-dire, reconnectez) votre entité à cette session avant les assertions.

    session.update (objet);

0

Par défaut, tous les one-to-many et many-to-many associations sont paresseusement après avoir été tiré par les cheveux accès pour la première fois.

Dans votre cas d'utilisation, vous pouvez remédier à ce problème en enveloppant toutes les opérations DAO dans une transaction logique:

transactionTemplate.execute(new TransactionCallback<Void>() { 
    @Override 
    public Void doInTransaction(TransactionStatus transactionStatus) { 

     int startingCount = sfdao.count(); 

     sfdao.create(sf); 

     SecurityFiling sf2 = sfdao.read(sf.getId()); 

     sfdao.delete(sf); 

     int endingCount = sfdao.count(); 

     assertTrue(startingCount == endingCount); 
     assertTrue(sf.getId().longValue() == sf2.getId().longValue()); 
     assertTrue(sf.getSfSubmissionType().equals(sf2.getSfSubmissionType())); 
     assertTrue(sf.getSfTransactionNumber().equals(sf2.getSfTransactionNumber())); 

     return null; 
    } 
}); 

Une autre option est de chercher toutes les associations LAZY lors du chargement de votre entité, de sorte que:

SecurityFiling sf2 = sfdao.read(sf.getId()); 

devrait chercher le LAZY submissionType aussi:

select sf 
from SecurityFiling sf 
left join fetch.sf.submissionType 

De cette façon, vous récupérez toutes les propriétés paresseuses et vous pouvez y accéder après la fermeture de la session.

Vous pouvez récupérer autant d'associations [one|many]-to-one et une "[un | plusieurs]] à plusieurs associations de listes (à cause de l'exécution d'un produit cartésien).

Pour initialiser plusieurs "[un | plusieurs]] à plusieurs", vous devez utiliser Hibernate.initialize(collection), juste après le chargement de votre entité racine.

1

Si vous avec Spring et annotations JPA, la plus simple façon d'éviter un problème session initialize paresseux est replaysing:

@PersistenceContext 

à

@PersistenceContext(type = PersistenceContextType.EXTENDED) 
+1

Cela ne fonctionne que si vous gérez manuellement vos transactions – Gemasoft