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)
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 .
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:
HibernateTemplate.initialize(object name)
lazy=false
dans vos fichiers hbm.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
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.
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
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>
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:
default-lazy="false"
à votre élément <hibernate-mapping>
@Proxy(lazy=false)
à votre ou vos classes d'entités.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);
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 ... :)
utiliser Hibernate.initialize pour le champ paresseux
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
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é.
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)
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.