2009-03-10 7 views
11

Estoy intentando crear una aplicación web usando Spring MVC, con Hibernate como su capa ORM. Sin embargo, debido a mi inexperiencia con ambos frameworks estoy luchando.¿Por qué recibo una Hibernate LazyInitializationException en esta aplicación web Spring MVC cuando los datos se muestran correctamente?

El siguiente código mostrará correctamente todos los registros que estoy buscando pero aún arrojará un rastro de pila en mis registros. Tengo problemas para encontrar documentación exhaustiva sobre la integración de Hibernate y SpringMVC (he buscado en springsource.org y he leído varios artículos en el interweb). ¿Podría alguien señalar lo que podría estar haciendo mal aquí?

Tenga en cuenta que he pasado algunos tratando de rastrear las respuestas en Internet para esto, incluida la consulta de this SO. Lo cual lamentablemente no fue de ayuda.

También debería tener en cuenta que la parte ORM de esta aplicación se ha utilizado y probado en una aplicación Java independiente sin problemas. Así que creo que la integración de Spring MVC e Hibernate está causando el problema.

Aquí está el seguimiento de pila (truncado) con el famoso problema de inicialización diferida;

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

Aquí hay un código del controlador de mi página 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; 
} 

Que se emplea en la página web usando esta pantalla 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: Que he añadido report.searchRule.name para probar si podía conseguir en los objetos dentro del objeto de informe. Se muestra bien.

Y en mi 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> 

Aquí están las correlaciones de ORM, por si acaso;

El hibernate.cfg.xml (según lo solicitado)

<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 fija el mapeo de informes también –

+0

Se agregó hibernate.cfg.xml, que contiene mis asignaciones. –

+0

¿Puede mostrar el archivo Report.hbm.xml? –

Respuesta

5

Estoy adivinando, pero desde el seguimiento de la pila parece que se llama a toString en SearchRule. ¿SearchRule tiene algún objeto secundario que no se haya cargado? Si SearchRule.toString estaba intentando obtener el valor de un objeto secundario no inicializado que podría dar como resultado LazyInitializationException.

+1

Hmm, sí. Estoy usando el apache común ReflectionToString para construir mis cadenas, intentaré eliminarlas mañana. Vi que en el seguimiento de la pila también, no sé por qué no se me ocurrió eso hasta que lo mencionaste. Creo que acabo de ver este problema por demasiado tiempo. –

+0

No hay problema, me complace ayudarlo. – Mark

1

El (lista) llamada Hibernate.initialize no inicializa los objetos de entidad de destino que están referenciados en la colección. Debería iterar sobre los informes e inicializar cada objeto individual. La invocación para inicializar informes convierte una recopilación de proxy en una colección concreta de proxies de informe. Trate código de abajo:

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

El enfoque hacha embotada es apagar la carga diferida mediante la adición de lazy="false" a sus clases de HBM. Esto podría tener sentido si siempre itera sobre el objeto completo cada vez que se recupera (realice el paso de inicialización OBE).

+0

Sí, lo he intentado antes, no hay alegría. –

+0

¡No funcionó! – user613114

24

Acabo de pasar por este maratón LazyInitialization.

El problema principal es que está intentando acceder a una entidad gestionada por hibernación fuera del ciclo de vida del Session, es decir, en la vista web de Spring MVC. En mi caso, esta era una asociación List<>@OneToMany, que están cargadas de forma perezosa por defecto.

Hay algunos enfoques diferentes: Mark mencionó uno, donde se realiza una iteración "ficticia" sobre las asociaciones perezosas. También puede forzar la carga ansiosa, ya sea a través de la configuración (en toda la clase) (en JPA sería @Fetch(value = FetchType.EAGER)) o más específicamente a través del HQL. Pero esto demostrará más problematic if your lazy associations are Lists.

La solución más limpia que encontré fue utilizar la primavera de OpenEntityManagerInViewFilter (hay un OpenSessionInViewFilter de Hibernate) - un simple filtro de servlet se le cae a web.xml (por delante de sus otros filtros de servlet), y la primavera creará automáticamente un seguro para subprocesos , compatible con transacciones Sessionpor solicitud HTTP. ¡No más LazyInitializationException!

+0

Gracias, no he tocado este tema todavía (no estoy seguro de por qué), pero parece un consejo que podría ser muy útil en el futuro. –

0

Ok, soy un idiota. Mi problema es que eché un vistazo a la traza de la pila, pero realmente no la leí. Aquí están los rastros de pila completos (o uno de ellos, 3 versiones ligeramente diferentes aparecen en mis registros).

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 es aparentemente haciendo algún tipo de supervisión del lado del servidor, que llama al método toString, que se asusta porque algo que se llama a los métodos toString no se ha inicializado correctamente. Así que mi problema es doble, la reflexión basada en ToString parece una mala idea para los pojos de hibernación y Netbeans está cambiando mi comportamiento en el tiempo de ejecución al tratar de observarlo.

Gracias a todos por ayudar, creo que había estado mirando este problema muy de cerca por demasiado tiempo y necesitaba dar un paso atrás por un tiempo.

0

@PersistenceContext (tipo = PersistenceContextType.EXTENDED)

es trabajo :)

+1

Re: "@PersistenceContext (type = PersistenceContextType.EXTENDED) es WORK :)" Esa es una respuesta bastante simple. PersistenceContextType.EXTENDED significa que debe administrar sus propias transacciones – mhvelplund

1

En realidad, hay tres maneras de evitar la excepción de inicialización perezosa:

  • Conjunto la propiedad perezosa a falso en el archivo de mapeo. No recomiendo este enfoque porque aumentará la carga de la base de datos y, por lo tanto, producirá un aumento en el rendimiento.

  • Mantenga la sesión abierta. No cierre la sesión antes de haber procesado los datos. Si la sesión está abierta durante la solicitud, puede obtener el gráfico asociado, pero debe asegurarse de que la acción se realice dentro de la misma transacción.

  • Trae ansiosamente las asociaciones. En la consulta HQL use la palabra clave "fetch" para recuperar la asociación. Desde mi punto de vista, esta es la mejor solución para evitar el problema de la inicialización lenta. En HQL, solo necesita agregar la palabra clave fetch en la cláusula from para obtener una asociación con entusiasmo.

Aquí se muestra un ejemplo:

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

He escrito un post sobre este tema, con algunos ejemplos de códigos y vínculos con el proyecto github:

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

Cuestiones relacionadas