2009-03-10 15 views
11

Sto tentando di creare un'applicazione Web utilizzando Spring MVC, con Hibernate come layer ORM. Tuttavia, a causa della mia inesperienza con entrambe le strutture, sto lottando.Perché ricevo Hibernate LazyInitializationException in questa applicazione Web MVC di Spring quando i dati vengono visualizzati correttamente?

Il seguente codice visualizzerà correttamente tutti i record che sto cercando, ma getterò comunque una traccia di stack nei miei registri. Ho difficoltà a trovare una documentazione approfondita sull'integrazione di Hibernate e SpringMVC (ho cercato su springsource.org e ho letto vari articoli sull'interweb). Qualcuno potrebbe far notare cosa potrei fare di sbagliato qui?

Si prega di notare che ho speso alcuni cercando di rintracciare le risposte su Internet per questo, tra cui guardando domanda this SO. Che purtroppo non è stato d'aiuto.

Devo anche notare che la parte ORM di questa applicazione è stata utilizzata e testata in un'applicazione Java autonoma senza problemi. Quindi credo che l'integrazione di Spring MVC e Hibernate stia causando il problema.

Ecco la traccia dello stack (troncata) con il famoso problema di inizializzazione pigro;

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) 
..... 

Ecco un codice dal mio controller di pagina 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; 
} 

Quale viene utilizzato sulla pagina Web utilizzando questo display 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> 

Nota: che ho aggiunto report.searchRule.name per verificare se ho potuto ottenere presso gli oggetti all'interno dell'oggetto report. Visualizza bene.

E nel mio 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> 

Ecco i mapping ORM, per ogni evenienza;

Il hibernate.cfg.xml (come richiesto)

<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> 

Da 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 inviare la mappatura anche per report –

+0

Aggiunto hibernate.cfg.xml, che contiene i miei mapping. –

+0

Puoi mostrare il file Report.hbm.xml? –

risposta

5

Sto solo supponendo, ma dalla traccia dello stack sembra che toString venga chiamato su SearchRule. SearchRule ha oggetti figlio che potrebbero non essere stati caricati? Se SearchRule.toString stava tentando di ottenere il valore per un oggetto figlio non inizializzato che potrebbe provocare LazyInitializationException.

+1

Hmm, si. Sto usando il comune Apache ReflectionToString per costruire le mie stringhe, proverò a rimuoverle domani. Ho visto anche quello nella traccia dello stack, non so perché non mi è venuto in mente finché non l'hai menzionato. Penso di aver osservato questo problema troppo a lungo. –

+0

Nessun problema, felice di aiutare. – Mark

1

L'(lista) chiamata Hibernate.initialize non inizializza gli oggetti entità bersaglio a cui si fa riferimento nella raccolta. Dovresti eseguire iterazioni sui report e inizializzare ogni singolo oggetto. La chiamata per inizializzare i report trasforma una raccolta di proxy in una raccolta concreta di proxy di report. Prova codice qui sotto:

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

L'approccio ascia smussata è di spegnere lazy loading aggiungendo lazy="false" alle classi HBM. Questo potrebbe avere senso se si ripeterà sempre l'intero oggetto ogni volta che viene recuperato (eseguire il passaggio di inizializzazione OBE).

+0

Sì, ci ho provato prima, nessuna gioia. –

+0

Non ha funzionato! – user613114

24

Ho appena passato questa maratona LazyInitialization.

Il problema principale è che si sta tentando di accedere a un'entità gestita in modalità ibernazione al di fuori del ciclo di vita di Session, ad esempio nella vista Web di Spring MVC. Nel mio caso, questa era un'associazione List<>@OneToMany, caricata pigramente di default.

Ci sono alcuni approcci diversi: Mark ne ha menzionato uno, dove si esegue un'iterazione "fittizia" sulle associazioni pigre. Puoi anche forzare il carico di caricamento, tramite la configurazione (a livello di classe) (in JPA sarebbe @Fetch(value = FetchType.EAGER)) o più specificamente attraverso l'HQL. Ma questo si rivelerà più problematic if your lazy associations are Lists.

la soluzione più pulita che ho trovato è stato quello di utilizzare Primavera OpenEntityManagerInViewFilter (c'è un OpenSessionInViewFilter per Hibernate) - un semplice filtro servlet si lascia cadere a web.xml (avanti rispetto ai vostri altri filtri servlet), e Spring creerà automaticamente un thread-safe , compatibile con la transazione Sessionper-HTTP-request. Non più LazyInitializationException!

+0

Grazie, non ho ancora riscontrato questo problema (non so perché), ma sembra un consiglio che potrebbe essere davvero utile in futuro. –

0

Ok, sono un idiota. Il mio problema è che ho dato un'occhiata alla traccia dello stack ma non l'ho letto veramente. Ecco le tracce dello stack completo (o una di queste, 3 versioni leggermente diverse vengono visualizzate nei miei registri).

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 è apparentemente facendo un qualche tipo di controllo lato server, che chiama il toString, che va fuori di testa perché qualcosa chiamato dal toString non è inizializzato correttamente. Quindi il mio problema è duplice, la riflessione basata su ToString sembra una cattiva idea per i pojos in ibernazione e Netbeans sta cambiando il mio comportamento di runtime cercando di osservarlo.

Grazie a tutti per l'aiuto, credo di aver guardato troppo da vicino questo problema troppo a lungo e di dover fare un passo indietro per un po '.

0

@PersistenceContext (type = PersistenceContextType.EXTENDED)

è il lavoro :)

+1

Ri: "@PersistenceContext (type = PersistenceContextType.EXTENDED) è WORK :)" Questa è una risposta piuttosto semplice. PersistenceContextType.EXTENDED significa che devi gestire le tue transazioni – mhvelplund

1

realtà, ci sono tre modi per evitare l'eccezione pigro inizializzazione:

  • Set la proprietà lazy su false nel file di mapping. Non consiglio questo approccio perché aumenterà il carico del database e quindi produrrà un piega in termini di prestazioni.

  • Mantieni aperta la sessione. Non chiudere la sessione prima di aver elaborato i dati. Se la sessione è aperta durante la richiesta, è possibile ottenere il grafico associato ma è necessario essere sicuri che l'azione avvenga all'interno della stessa transazione.

  • Prendere le associazioni con entusiasmo. Nella query HQL utilizzare la parola chiave "fetch" per recuperare l'associazione. Dal mio punto di vista questa è la soluzione migliore per evitare il problema di inizializzazione pigro. In HQL, devi solo aggiungere la parola chiave fetch nella clausola from per recuperare con entusiasmo un'associazione.

Ecco un esempio:

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

Ho scritto un post su questo problema, con alcuni esempi di codice e link al progetto github:

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