LazyInitializationException en JPA et Hibernate
Je sais que cette question a été poser de nombreuses fois ici et à travers l'internet, et j'ai lu à travers de nombreux de ces réponses mais je ne comprends toujours pas la bonne façon de résoudre ce problème. Je suis expérimenter avec Spring MVC et JPA, et à chaque fois que je accéder à un paresseusement chargé de propriété-je obtenir un LazyInitializationException.
Voici le code que j'expérimente avec:
@Repository
public class MyDAO {
private static final Logger logger = LoggerFactory.getLogger(MyDAO.class);
@PersistenceContext
private EntityManager em;
@Transactional
public void logDOI() {
DOI myDOI = em.find(DOI.class, Long.valueOf(1));
//This line gives the expected output
logger.info("Fetched DOI: " + myDOI.getDoiKey());
//This line throws the LazyInitalizationException
for(DOIMembership m : myDOI.getDoiMemberships()) {
logger.info("Got DOI Membership id: " + m.getId());
}
}
}
L'entité que je suis accédant:
@Entity
@Table(name="DOI")
public class DOI implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@SequenceGenerator(name="DOI_ID_GENERATOR", sequenceName="DOI_SEQ")
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="DOI_ID_GENERATOR")
private long id;
//Other properties omitted
//bi-directional many-to-one association to DOIMembership
@OneToMany(mappedBy="doi", fetch=FetchType.LAZY)
private Set<DOIMembership> doiMemberships;
public DOI() {
}
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
//Other Accessors Omitted
}
L'entité référencée à partir de DOI
@Entity
@Table(name="DOI_MEMBERSHIP")
public class DOIMembership implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@SequenceGenerator(name="DOI_MEMBERSHIP_ID_GENERATOR", sequenceName="DOI_MEMBERSHIP_SEQ")
@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="DOI_MEMBERSHIP_ID_GENERATOR")
private long id;
//bi-directional many-to-one association to DOI
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="DOI_ID")
private DOI doi;
@Column(name="GROUP_ID")
private BigDecimal groupId;
@Column(name="DATA_SET")
private BigDecimal dataSetId;
public DOIMembership() {
}
public BigDecimal getGroupId() {
return groupId;
}
public BigDecimal getDataSetId() {
return dataSetId;
}
public void setDataSetId(BigDecimal dataSetId) {
this.dataSetId = dataSetId;
}
public void setGroupId(BigDecimal groupId) {
this.groupId = groupId;
}
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
public DOI getDoi() {
return this.doi;
}
public void setDoi(DOI doi) {
this.doi = doi;
}
}
Le Contrôleur Spring MVC:
@Controller
@RequestMapping("/summary")
public class DOISummaryController {
@Autowired
MyDAO myDAO;
@RequestMapping()
public String DOISummary() {
myDAO.logDOI();
return "home";
}
}
Ma configuration Spring:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<context:property-placeholder location="WEB-INF/spring/root-context.properties, WEB-INF/spring/datasource-context.properties" />
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName">
<value>oracle.jdbc.driver.OracleDriver</value>
</property>
<property name="url">
<value>${Url}</value>
</property>
<property name="username">
<value>${Username}</value>
</property>
<property name="password">
<value>${Password}</value>
</property>
</bean>
<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
</property>
<property name="packagesToScan" value="org.myorg.doi.domain" />
<property name="jpaProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.Oracle10gDialect
</prop>
<prop key="hibernate.max_fetch_depth">3</prop>
<prop key="hibernate.jdbc.fetch_size">50</prop>
<prop key="hibernate.jdbc.batch_size">10</prop>
<prop key="hibernate.show_sql">true</prop>
</props>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="emf" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<context:annotation-config />
<task:annotation-driven />
<context:component-scan base-package="org.myorg.doi" />
</beans>
Et une trace de la pile, comme l'a demandé:
SEVERE: Servlet.service() for servlet [appServlet] in context with path [/DOI] threw exception [Request processing failed; nested exception is org.hibernate.LazyInitializationException: could not initialize proxy - no Session] with root cause
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:430)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:121)
at org.hibernate.collection.internal.PersistentSet.iterator(PersistentSet.java:180)
at org.myorg.doi.dao.MyDAO.logDOI(MyDAO.java:27)
at org.myorg.doi.web.DOISummaryController.DOISummary(DOISummaryController.java:29)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:213)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:126)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:778)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:225)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at com.springsource.insight.collection.tcserver.request.HttpRequestOperationCollectionValve.traceNextValve(HttpRequestOperationCollectionValve.java:116)
at com.springsource.insight.collection.tcserver.request.HttpRequestOperationCollectionValve.invoke(HttpRequestOperationCollectionValve.java:98)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1001)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:585)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:680)
Je vous le voyez, je suis en train de l'utiliser pur JPA, et seulement de l'utilisation d'Hibernate comme un fournisseur JPA.
Je comprends que l'exception est causée par la session de détachement de l'entité. Mais, je pensais que cela n'arriverait pas si nous sommes actuellement dans une transaction, qui devrait être le cas depuis le logDOI méthode annotée avec @Transactionnelle.
Bien sûr, tout fonctionne parfaitement si je change le FetchType à la HÂTE, mais il semble que je ne devrais pas avoir à le faire.
Je suis également conscient de OpenEntityManagerInViewFilter mais il semble que je ne devrais pas avoir à utiliser de soit que si je continue à tous l'accès à mon entités DAO annotée avec @Transactionnelle (ou par d'autres moyens que je ne suis pas au courant).
Je pense que j'ai peut-être aborder ce problème de manière incorrecte, mais je ne sais pas ce que la bonne approche est de. Comment est-on censé utiliser efficacement paresseusement chargé propriétés?
Il semble être liée à obtenir l'actuel Session Hibernate. Avez-vous vérifier que vous injecter correctement la Session Hibernate dans votre configuration? Vous ... avez l'annotation
@Repository
dans la nouvelle version du Framework Spring, je l'utilise pour certains de DAO.Je pense que c'est le même que
@Entity
que vous utilisez déjà. Ensuite, vous devez vérifier que le prof que vous avez défini dans JPA propriétés sont suffisamment profond pour charger les @Autowired
dépendances.Et avez-vous injecter le courant de la Session Hibernate à l'intérieur de votre APP manager? Enfin, je préfère de loin l'aide de OpenEntityManagerInViewInterceptor
Je suis désolé, je suis très nouveau à Spring et JPA, et je ne connais pas assez pour répondre à vos questions. Pourriez-vous être plus précis? Aussi, depuis que je suis en train d'apprendre JPA je préfère apprendre la bonne façon de l'utiliser avant-je utiliser des choses comme OpenEntityManagerInViewInterceptor.
OriginalL'auteur Eddie | 2012-09-14
Vous devez vous connecter pour publier un commentaire.
Grâce à Shailendra j'ai commencé à regarder de plus près à la transaction, et a remarqué que la transaction n'a jamais été de départ. Avec ces renseignements, j'ai fait quelques recherches et trouvé ceci: Printemps @Transaction ne pas commencer les opérations. J'ai mis
<tx:annotation-driven/>
dans mon servlet-context.xml fichier et tout à coup la transaction pour logDOI commencé, et tout a fonctionné correctement; je n'ai plus eu le LazyInitializationException. Je ne suis pas du tout clair pour lesquelles il a travaillé. Toutes les informations sur ce serait apprécié.Mise à jour:
J'ai tout compris. Un élément essentiel de mon problème était dans l'servlet-context.xml fichier. C'est à quoi il ressemblait
Le problème majeur est dans ce contexte:component-scan. Spring MVC crée deux contexte qui les haricots sont instanciés dans: la racine de contexte de l'application définie avec le contextConfigLocation paramètre dans l'web.xml fichier et le servlet contexte défini dans la DispatcherServlet dans le web.xml fichier. La servlet contexte pourrait voir le contexte de l'application, mais pas l'inverse. Maintenant, en tant que résultat de contexte:component-scan en cours de définition dans le contexte de servlet et de la numérisation de la ensemble application de l'espace de noms, mon DAO a été instancié dans le contexte de servlet. Toutefois, l'opération d'annotation de balayage a été fait dans le contexte de l'application et de l'AOP proxy trucs pour qu'elle ne pouvait pas être fait à partir de là. Simplement en modifiant le context:component-scan dans le contexte de servlet pour analyser uniquement les contrôleurs MVC (
<context:component-scan base-package="org.myapp.doi.web" />
fixe tout; le DAO a été créée dans le contexte de l'application et correctement configuré pour les transactions.Merci! Ceci est également résolu pour moi. Après la mise à jour de Printemps de 2,5 à 3,2 j'ai créé un fichier XML donc j'ai été coincé avec l'LazyInitializationException pour une journée. Après la création d'un distinct DispatcherServlet xml(Servlet) fichier le problème a été résolu. Merci Beaucoup!
Une meilleure pratique pour les deux contextes de spring mvc de l'application qui est suggéré ici: stackoverflow.com/a/11471568/4344443 . Vous avez vraiment n'avez pas besoin de déplacer le <tx:annotation-driven /> servlet-context.xml, il suffit de les garder dans application-context.xml. Et dans votre application.xml définir ceci: <context:component-scan de la base-package="org.myapp.doi"> <contexte:exclure-expression de filtre="org.springframework.stéréotype.Contrôleur" type="annotation"/> </context:component-scan>
OriginalL'auteur Eddie
La meilleure façon de résoudre ce problème serait d'abord de comprendre pourquoi et quand la session est fermée en dépit d'être en une seule transaction. Et le meilleur moyen pour cela serait de permettre la mise en veille prolongée (que vous êtes en utilisant hibernate comme fournisseur JPA) niveau de journalisation de DÉBOGAGE dans la configuration log4j et de la piste où les séances sont fermés. Ce serait vous donner une image claire. Bien que la trace de la pile suggère que le sous-jacent de la session a été fermée, mais il y a évidemment aucune raison pourquoi ? Vous pouvez poster pertinentes debug/messages d'infos consignées.
Vous pouvez également configurer la journalisation pour le printemps-cadre pour suivre l'opération de gestion de l'infrastructure
Les journaux donnent de bons messages sur lorsque la session a été fermée et la transaction validée.
Pour, par exemple,
DEBUG: 2012-09-17 09:51:55,976 org.springframework.transaction.annotation.AnnotationTransactionAttributeSource - Adding transactional method 'logDOI' with attribute: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
Il semble que la transaction n'est pas encore en cours de démarrage. Je n'ai aucune idée pourquoi. Ma configuration semble identique pour les exemples que j'ai vu.OriginalL'auteur Shailendra
J'ai aussi eu ce problème, mais inspiré par votre réponse, je le résoudre. Ici, est-il.
Mon application essayez d'utiliser Jpa Référentiel pour gérer les données dans un contrôleur rest, qui s'avère obtenu les pas d'erreur de session. Voici le code:
Selon cette post et ce post, nous savons que les haricots dans le contexte de servlet peut faire référence à des haricots dans le contexte de l'application. Ainsi, le TransactionManager ne pouvez pas accéder à ce repos contrôleur de haricot, résultant de cette erreur.
Solution, la création d'un milieu de la couche de haricots contexte de l'application de repos entre le contrôleur et le référentiel, l'encapsulation, ceux du code. Je l'ai essayer, il fonctionne très bien. Mettre à jour le code plus tard.
OriginalL'auteur Junbang Huang
Tout d'abord, pour toute dépendance à définir avec @Autocâblés, vous devez déclarer un Printemps Bean foreach DAO que vous allez utiliser, et il sera injecté au moment de l'exécution.
Peut-être que ces DAO besoin d'avoir une sessionFactory de référence comme ceci:
Deuxièmement, vous oubliez peut-être à ajouter à l'intérieur de votre Ressort de configuration XML:
Troisième, votre intercepteur trucs:
J'ai ajouté des choses utiles à la charge @Référentiel des trucs.
Aussi, quelle est la DAO non déclarées comme un ressort de haricot? Il est annoté avec
@Repository
et la@Autowired
annotation est de le ramasser correctement. Lelogger.info("Fetched DOI: " + myDOI.getDoiKey());
ligne dans logDOI est exécutée correctement avec le bon de sortie.Pourquoi êtes-vous de m'avoir injecter une session Hibernate dans mon DAO? Je veux travailler exclusivement avec JPA et de n'utiliser Hibernate comme une implémentation JPA. Et n'est-ce pas ce que vous avez de me faire la même chose qu'à l'aide de la
@PersistenceContext
annotation?Il est de savoir comment je le fais mais je n'ai pas d'exemple de mon propre avec jpa ici, vérifiez toutes tutoriel sur la JPA et Hibernate comme persitence fournisseur comme celle-ci
OriginalL'auteur ThierryB