web-dev-qa-db-fra.com

hibernate: LazyInitializationException: impossible d'initialiser le proxy

En voici un qui m'a laissé perplexe. J'essaie d'implémenter une structure de base DAO Hibernate, mais je rencontre un problème.

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 le troisième assertTrue où il tente de comparer une valeur dans sf à la valeur correspondante dans 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)
70
Piko

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

session.update(object);

En utilisant lazy=false n'est pas une bonne solution car vous jetez la fonctionnalité d'initialisation différée de Veille prolongée. Quand lazy=false, la collection est chargée en mémoire au moment où l'objet est demandé. Cela signifie que si nous avons une collection de 1 000 éléments, ils seront tous chargés en mémoire, que nous y accédions ou non. Et ce n'est pas bon.

S'il vous plaît lisez ceci article où il explique le problème, les solutions possibles et pourquoi est implémenté de cette façon. En outre, pour comprendre les sessions et les transactions, vous devez lire cet autre article .

68
pakore

Cela signifie généralement que la session propriétaire d'Hibernate est déjà fermée. Vous pouvez effectuer l'une des opérations suivantes pour résoudre ce problème:

  1. quel que soit l'objet créant ce problème, utilisez HibernateTemplate.initialize(object name)
  2. Utilisation lazy=false dans vos fichiers hbm.
14
digitalsanctum

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 paresseux = faux n'est pas la solution - il peut tout charger en même temps, et ce n'est pas forcément bon. Exemple:
1 table d'enregistrement A références:
5 enregistrements de références au tableau B:
25 enregistrements de références au tableau C:
125 enregistrements table D
...
etc. Ce n’est qu’un exemple de ce qui peut mal se passer.
- Tim Sabin

11
Tim Sabin

Si vous utilisez la mise en veille prolongée avec des annotations JPA, cela sera utile. Dans votre classe de service, il devrait exister un configurateur pour le gestionnaire d'entités avec @PersistenceContext. remplacez-le par @PersistenceContext (type = PersistenceContextType.EXTENDED). Ensuite, vous pouvez accéder à la propriété paresseux dans n'importe quel endroit.

7
konda

si vous utilisez le chargement paresseux, votre méthode doit être annotée avec

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) pour un EJB de session sans état

4
Rolando Quezada

Nous avons également rencontré cette erreur. Pour résoudre ce problème, nous avons ajouté un lazy = false dans le fichier de mappage Hibernate.

Il semble que nous ayons une classe A située 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 séparée de la session.

Pour pouvoir accéder à cette classe B, nous avons dû spécifier dans le fichier de mappage Hibernate de la 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>  
3
chris

Si vous connaissez l'impact de lazy=false Et souhaitez tout de même le rendre par défaut (par exemple, à des fins de prototypage), vous pouvez utiliser l'un des éléments suivants:

  • si vous utilisez la configuration XML: ajoutez default-lazy="false" à votre élément <hibernate-mapping>
  • si vous utilisez la configuration des annotations: ajoutez @Proxy(lazy=false) à votre ou vos classes d'entités.
2
Shayanlinux

Il semble que seul votre DAO utilise la session. Ainsi, une nouvelle session est ouverte puis fermée pour chaque appel d'une méthode DAO. Ainsi, l'exécution du programme peut être reprise en tant que:

// 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 paresseuses: elles sont chargées à partir de la base de données à la demande. Ainsi:

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

lance une exception car il demande un nouveau chargement de la base de données et que la session associée au chargement de l'entité a déjà été fermée.

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

  • créer une session pour inclure tout notre code. Il faudrait donc changer votre contenu DAO pour éviter d'ouvrir une deuxième session.

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

    session.update (objet);

2
Kartoch

Ok, a finalement compris où je me sentais négligent. J'étais sous la notion erronée que je devrais envelopper 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 configuré les transactions strictement au niveau de la couche application/gestionnaire. Cela a totalement résolu tous mes problèmes. Les données sont correctement chargées paresseusement car j'en ai besoin, elles sont bouclées et fermées une fois que je fais le commit.

La vie est belle ... :)

2
Piko

utiliser Hibernate.initialize pour le champ paresseux

1
myururdurmaz

Si vous gérez manuellement la session Hibernate, vous voudrez peut-être consulter sessionFactory.getCurrentSession () et les documents associés ici:

http://www.hibernate.org/hib_docs/v3/reference/en/html/architecture-current-session.html

1
cliff.meyers

Je pense que Piko veut dire dans sa réponse qu’il existe un fichier hbm. J'ai un fichier appelé Tax.Java. Les informations de mappage sont enregistrées dans le fichier hbm (= hibernate mapping). Dans la balise de classe, il existe une propriété appelée lazy . Définissez cette propriété sur true. L'exemple hbm suivant montre comment définir la propriété lazy sur false .

"id ..."

Si vous utilisez des annotations, consultez plutôt la documentation en veille prolongée. http://docs.jboss.org/hibernate/stable/annotations/reference/en/html_single/

J'espère que cela a aidé.

1
Alexander Fenske

Si vous utilisez des annotations Spring et JPA, la méthode la plus simple pour éviter les problèmes de session en initialisation différée consiste à répéter la lecture:

@PersistenceContext   

à

@PersistenceContext(type = PersistenceContextType.EXTENDED)
1
DmRomantsov

Par défaut, tous les one-to-many et many-to-many _ les associations sont récupérées paresseusement lors de leur premier accès.

Dans votre cas d'utilisation, vous pouvez résoudre ce problème en regroupant 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 consiste à récupérer toutes les associations LAZY lors du chargement de votre entité, de sorte que:

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

devrait aller 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 avec impatience et vous pourrez y accéder après la fermeture de la session.

Vous pouvez aller chercher autant de [one|many]-to-one associations et une association "[un | plusieurs] -à-plusieurs" (en raison de l'exécution d'un produit cartésien).

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

0
Vlad Mihalcea