2009-03-10 6 views
11

Je tente de créer une application web en utilisant Spring MVC, avec Hibernate comme couche ORM. Cependant, en raison de mon inexpérience avec les deux cadres, je me bats.Pourquoi une exception Hibernate LazyInitializationException s'affiche-t-elle dans cette application Web Spring MVC lorsque les données s'affichent correctement?

Le code suivant affichera correctement tous les enregistrements que je recherche mais jette toujours une trace de pile dans mes journaux. J'ai du mal à trouver une documentation complète concernant l'intégration d'Hibernate et de SpringMVC (j'ai regardé sur springsource.org et lu divers articles sur l'interweb). Quelqu'un pourrait-il souligner ce que je pourrais faire mal ici?

S'il vous plaît noter que j'ai passé un peu d'essayer de traquer les réponses sur Internet pour cela, y compris en regardant this SO question. Ce qui n'était malheureusement pas une aide.

Je devrais également noter que la partie ORM de cette application a été utilisée et testée dans une application Java autonome sans problèmes. Je crois donc que l'intégration de Spring MVC et d'Hibernate est à l'origine du problème.

Voici la trace de la pile (tronquée) avec le fameux problème d'initialisation paresseux;

2009-03-10 12:14:50,353 [http-8084-6] ERROR org.hibernate.LazyInitializationException.<init>(LazyInitializationException.java:19) - could not initialize proxy - no Session 
org.hibernate.LazyInitializationException: could not initialize proxy - no Session 
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:57) 
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:111) 
    at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:150) 
    at com.generic.orm.generated.SearchRule$$EnhancerByCGLIB$$92abaed6.toString(<generated>) 
    at java.lang.String.valueOf(String.java:2827) 
    at java.lang.StringBuffer.append(StringBuffer.java:219) 
    at org.apache.commons.lang.builder.ToStringStyle.appendDetail(ToStringStyle.java:578) 
    at org.apache.commons.lang.builder.ToStringStyle.appendInternal(ToStringStyle.java:542) 
    at org.apache.commons.lang.builder.ToStringStyle.append(ToStringStyle.java:428) 
    at org.apache.commons.lang.builder.ToStringBuilder.append(ToStringBuilder.java:840) 
    at org.apache.commons.lang.builder.ReflectionToStringBuilder.appendFieldsIn(ReflectionToStringBuilder.java:606) 
..... 

Voici un code de mon contrôleur de page Web;

private List<Report> getReports() { 
    Session session = HibernateUtil.getSessionFactory().getCurrentSession(); 
    session.beginTransaction(); 

    List<Report> reports = session.createCriteria(Report.class).list(); 
    Hibernate.initialize(reports); 

    session.getTransaction().commit(); 
    return reports; 
} 

Qui est employé sur la page Web en utilisant cet affichage html;

<table border="1"> 
    <c:forEach items="${model.reports}" var="report"> 
     <tr> 
      <td><c:out value="${report.id}"/></td> 
      <td><c:out value="${report.username}"/></td> 
      <td><c:out value="${report.thresholdMet}"/></td> 
      <td><c:out value="${report.results}"/></td> 
      <td><c:out value="${report.searchRule.name}"/></td> 
      <td><c:out value="${report.uuid}"/></td> 
     </tr> 
    </c:forEach> 
</table> 

Remarque: Que j'ai ajouté report.searchRule.name pour tester si je pouvais obtenir les objets dans l'objet de rapport. Il affiche bien.

Et dans mon applicationContext.xml;

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
    <property name="configLocation"> 
     <value>classpath:hibernate.cfg.xml</value> 
    </property> 
    <property name="hibernateProperties"> 
     <props> 
      <prop key="hibernate.dialect">${hibernate.dialect}</prop> 
     </props> 
    </property> 
</bean> 

Voici les mappages ORM, juste au cas où;

Le hibernate.cfg.xml (comme l'avait demandé)

<hibernate-configuration> 
    <session-factory> 
    <property name="hibernate.connection.driver_class">com.microsoft.sqlserver.jdbc.SQLServerDriver</property> 
    <property name="hibernate.connection.url">jdbc:sqlserver://<removed></property> 
    <property name="hibernate.connection.username"><removed></property> 
    <property name="hibernate.connection.password"><removed></property> 
    <property name="hibernate.current_session_context_class">thread</property> 
    <property name="hibernate.show_sql">false</property> 
    <mapping resource="com/generic/orm/generated/Report.hbm.xml"/> 
    <mapping resource="com/generic/orm/generated/FieldRule.hbm.xml"/> 
    <mapping resource="com/generic/orm/generated/Reconciliation.hbm.xml"/> 
    <mapping resource="com/generic/orm/generated/SearchRule.hbm.xml"/> 
    <mapping resource="com/generic/orm/generated/IndexTemplate.hbm.xml"/> 
    <mapping resource="com/generic/orm/generated/Field.hbm.xml"/> 
    <mapping resource="com/generic/orm/generated/ErrorCode.hbm.xml"/> 
    </session-factory> 
</hibernate-configuration> 

De report.hbm.xml

<hibernate-mapping> 
    <class name="com.generic.orm.generated.Report" table="Report" schema="dbo" catalog="CoolRecon"> 
     <id name="id" type="int"> 
      <column name="ID" /> 
      <generator class="native" /> 
     </id> 
     <timestamp name="timeStamp" column="TimeStamp" /> 
     <many-to-one name="searchRule" class="com.generic.orm.generated.SearchRule" fetch="select"> 
      <column name="SearchRuleName" length="50" not-null="true" /> 
     </many-to-one> 
     <many-to-one name="errorCode" class="com.generic.orm.generated.ErrorCode" fetch="select"> 
      <column name="ErrorCodeId" /> 
     </many-to-one> 
     <many-to-one name="reconciliation" class="com.generic.orm.generated.Reconciliation" fetch="select"> 
      <column name="ReconciliationName" length="100" /> 
     </many-to-one> 
     <property name="username" type="string"> 
      <column name="Username" length="50" /> 
     </property> 
     <property name="supersheetDate" type="timestamp"> 
      <column name="SupersheetDate" length="23" not-null="true" /> 
     </property> 
     <property name="milliSecondsTaken" type="long"> 
      <column name="MilliSecondsTaken" not-null="true" /> 
     </property> 
     <property name="thresholdMet" type="boolean"> 
      <column name="ThresholdMet" not-null="true" /> 
     </property> 
     <property name="results" type="int"> 
      <column name="Results" not-null="true" /> 
     </property> 
     <property name="exception" type="string"> 
      <column name="Exception" length="750" /> 
     </property> 
     <property name="uuid" type="string"> 
      <column name="UUID" length="36" not-null="true" /> 
     </property> 
    </class> 
</hibernate-mapping> 
+0

pls post la mappage pour les rapports aussi –

+0

Ajouté hibernate.cfg.xml, qui contient mes mappages. –

+0

Pouvez-vous afficher le fichier Report.hbm.xml? –

Répondre

5

Je ne fais que deviner, mais à partir de la trace de la pile, il semble que toString soit appelée sur SearchRule. Est-ce que SearchRule a des objets enfants qui n'ont peut-être pas été chargés? Si SearchRule.toString essayait d'obtenir la valeur pour un objet enfant non initialisé qui pourrait entraîner l'exception LazyInitializationException.

+1

Hmm, ouais. J'utilise le refrain commun ReflectionToString apache pour construire mon à cordes, je vais essayer d'enlever ceux de demain. J'ai vu que dans la trace de la pile aussi, je ne sais pas pourquoi cela ne m'est pas arrivé jusqu'à ce que vous le mentionniez. Je pense que je viens de regarder ce problème trop longtemps. –

+0

Pas de problème, heureux d'aider. – Mark

1

L'appel Hibernate.initialize (liste) n'initialise pas les objets d'entité cible qui sont référencés dans la collection. Vous devez parcourir les rapports et initialiser chaque objet individuel. L'appel à initialiser les rapports transforme une collection de proxy en une collection concrète de proxies de rapport. Essayez le code ci-dessous:

for(Report r : reports) 
    Hibernate.initialize(r); 

L'approche hache émoussée est de désactiver le chargement paresseux en ajoutant lazy="false" à vos classes de HBM. Cela peut avoir du sens si vous allez toujours parcourir l'objet entier chaque fois qu'il est récupéré (faites l'étape d'initialisation OBE).

+0

Oui, j'ai déjà essayé ça, pas de joie. –

+0

Cela n'a pas fonctionné! – user613114

24

Je viens de passer ce marathon LazyInitialization.

Le problème principal est que vous essayez d'accéder à une entité gérée par hibernate en dehors du cycle de vie du Session, c'est-à-dire dans la vue Web de Spring MVC. Dans mon cas, c'était une association List<>@OneToMany, qui sont paresseusement chargées par défaut.

Il existe plusieurs approches différentes - Mark en a mentionné une, où vous effectuez une itération "fictive" sur les associations paresseuses. Vous pouvez également forcer le chargement avec impatience, soit via la configuration (à l'échelle de la classe) (dans JPA ce serait @Fetch(value = FetchType.EAGER)) ou plus précisément via le HQL. Mais cela prouvera plus problematic if your lazy associations are Lists.

La solution la plus propre que j'ai trouvé utiliser OpenEntityManagerInViewFilter de printemps (il y a un OpenSessionInViewFilter pour Hibernate) - un simple filtre de servlet vous déposez pour web.xml (en avance sur vos autres filtres de servlet), et le printemps va créer automatiquement un thread-safe , prenant en charge la transaction Sessionpar requête HTTP. Plus besoin de LazyInitializationException!

+0

Merci, je n'ai pas encore rencontré ce problème (je ne sais pas pourquoi), mais cela ressemble à un conseil qui pourrait être très utile à l'avenir. –

0

Ok, je suis un idiot. Mon problème est que j'ai jeté un coup d'œil sur la trace de la pile mais je ne l'ai pas vraiment lue. Voici les traces complètes de la pile (ou l'une d'entre elles, 3 versions légèrement différentes apparaissent dans mes logs).

org.hibernate.LazyInitializationException: could not initialize proxy - no Session 
    at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:57) 
    at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:111) 
    at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.invoke(CGLIBLazyInitializer.java:150) 
    at com.generic.orm.generated.SearchRule$$EnhancerByCGLIB$$de674d10.toString(<generated>) 
    at java.lang.String.valueOf(String.java:2827) 
    at java.lang.StringBuffer.append(StringBuffer.java:219) 
    at org.apache.commons.lang.builder.ToStringStyle.appendDetail(ToStringStyle.java:578) 
    at org.apache.commons.lang.builder.ToStringStyle.appendInternal(ToStringStyle.java:542) 
    at org.apache.commons.lang.builder.ToStringStyle.append(ToStringStyle.java:428) 
    at org.apache.commons.lang.builder.ToStringBuilder.append(ToStringBuilder.java:840) 
    at org.apache.commons.lang.builder.ReflectionToStringBuilder.appendFieldsIn(ReflectionToStringBuilder.java:606) 
    at org.apache.commons.lang.builder.ReflectionToStringBuilder.toString(ReflectionToStringBuilder.java:759) 
    at org.apache.commons.lang.builder.ReflectionToStringBuilder.toString(ReflectionToStringBuilder.java:287) 
    at org.apache.commons.lang.builder.ReflectionToStringBuilder.toString(ReflectionToStringBuilder.java:121) 
    at com.generic.orm.generated.Report.toString(Report.java:141) 
    at java.lang.String.valueOf(String.java:2827) 
    at java.lang.StringBuilder.append(StringBuilder.java:115) 
    at java.util.AbstractCollection.toString(AbstractCollection.java:422) 
    at java.lang.String.valueOf(String.java:2827) 
    at java.lang.StringBuilder.append(StringBuilder.java:115) 
    at java.util.AbstractMap.toString(AbstractMap.java:490) 
    at org.netbeans.modules.web.monitor.server.MonitorFilter.recordRequestAttributes(MonitorFilter.java:1376) 
    at org.netbeans.modules.web.monitor.server.MonitorFilter.recordRequestData(MonitorFilter.java:1184) 
    at org.netbeans.modules.web.monitor.server.MonitorFilter.getDataBefore(MonitorFilter.java:803) 
    at org.netbeans.modules.web.monitor.server.MonitorFilter.doFilter(MonitorFilter.java:361) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:630) 
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:436) 
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:374) 
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:302) 
    at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:167) 
    at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:239) 
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1158) 
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:900) 
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:808) 
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:476) 
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:431) 
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:617) 
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
    at org.netbeans.modules.web.monitor.server.MonitorFilter.doFilter(MonitorFilter.java:390) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) 
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) 
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128) 
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) 
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) 
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:286) 
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:845) 
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583) 
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447) 
    at java.lang.Thread.run(Thread.java:619) 

Netbeans fait apparemment une sorte de contrôle côté serveur, qui appelle la toString, qui flippe parce que quelque chose appelé par le toString n'est pas initialisé correctement. Donc, mon problème est double, la réflexion basée sur ToString semble être une mauvaise idée pour les pojos hibernate et Netbeans change mon comportement d'exécution en essayant de l'observer.

Merci à tous pour votre aide, je pense que je regardais trop attentivement ce problème depuis trop longtemps et que je devais prendre un peu de recul.

0

@PersistenceContext (type = PersistenceContextType.EXTENDED)

est un travail :)

+1

Re: "@PersistenceContext (type = PersistenceContextType.EXTENDED) est WORK :)" C'est une réponse plutôt simple. PersistenceContextType.EXTENDED signifie que vous devez gérer vos propres transactions – mhvelplund

1

En fait, il y a trois façons d'éviter l'initialisation Lazy Exception:

  • Set la propriété paresseuse à false dans le fichier de mappage. Je ne recommande pas cette approche car elle va incrémenter la charge de la base de données et, par conséquent, elle produira un plissement de la performance.

  • Gardez la session ouverte. Ne fermez pas la session avant d'avoir traité les données. Si la session est ouverte pendant la requête, vous pouvez obtenir le graphique associé, mais vous devez être sûr que l'action se déroule dans la même transaction.

  • Récupérez rapidement les associations. Dans la requête HQL, utilisez le mot-clé "fetch" pour récupérer l'association. De mon point de vue, c'est la meilleure solution pour éviter le problème de l'initialisation paresseuse. En HQL, il suffit d'ajouter le mot clé fetch dans la clause from pour récupérer une association avec impatience.

Voici un exemple:

from Doctor doc 
left join fetch doc.patients 
where doc.name like ‘Doctor A%’ 

J'ai écrit un post sur cette question, avec quelques exemples de code et des liens vers le projet github:

http://ignaciosuay.com/how-to-avoid-hibernate-lazy-initialization-exception/

Questions connexes