当数据正确显示时,为什么我在Spring MVC Web应用程序中获得Hibernate LazyInitializationException?

我正在尝试使用Spring MVC创建一个Web应用程序,将Hibernate作为其ORM层。 但是,由于我对这两个框架缺乏经验,我正在努力。

以下代码将正确显示我正在查找的所有记录,但仍会将堆栈跟踪放入我的日志中。 我无法找到有关集成Hibernate和SpringMVC的详细文档(我查看了springsource.org并阅读了关于interweb的各种文章)。 谁能指出我在这里做错了什么?

请注意,我花了一些时间试图在互联网上找到答案,包括查看这个问题。 遗憾的是没有帮助。

我还应该注意,此应用程序的ORM部分已在独立的Java应用程序中使用和测试,没有任何问题。 所以我认为Spring MVC和Hibernate的集成导致了这个问题。

这是具有着名的延迟初始化问题的堆栈跟踪(截断);

2009-03-10 12:14:50,353 [http-8084-6] ERROR org.hibernate.LazyInitializationException.(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() 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) ..... 

这是我的网页控制器的代码;

 private List getReports() { Session session = HibernateUtil.getSessionFactory().getCurrentSession(); session.beginTransaction(); List reports = session.createCriteria(Report.class).list(); Hibernate.initialize(reports); session.getTransaction().commit(); return reports; } 

使用此显示html在网页上使用哪个;

 

注意:我添加了report.searchRule.name来测试我是否可以获取报表对象中的对象。 它显示正常。

在我的applicationContext.xml中;

   classpath:hibernate.cfg.xml    ${hibernate.dialect}    

以下是ORM映射,以防万一;

hibernate.cfg.xml(根据要求)

   com.microsoft.sqlserver.jdbc.SQLServerDriver jdbc:sqlserver://   thread false          

来自report.hbm.xml

                                        

我只是在猜测,但是从堆栈跟踪看来,似乎在SearchRule上调用了toString。 SearchRule是否有任何可能尚未加载的子对象? 如果SearchRule.toString尝试获取未初始化的子对象的值,可能导致LazyInitializationException。

我刚刚完成了这项LazyInitialization马拉松比赛。

核心问题是你试图在Session的生命周期之外访问一个hibernate管理的实体,即在Spring MVC的Web视图中。 在我的例子中,这是一个List<> @OneToMany关联,默认情况下会延迟加载。

有几种不同的方法 – 马克提到过一种方法,你可以在懒惰的关联上进行“虚拟”迭代。 您还可以通过配置(类范围)(在JPA中它是@Fetch(value = FetchType.EAGER) )或更具体地通过HQL来@Fetch(value = FetchType.EAGER)进行@Fetch(value = FetchType.EAGER) 。 但如果您的懒惰关联是Lists这将certificate更有问题 。

我发现最干净的解决方案是使用Spring的OpenEntityManagerInViewFilter (对于Hibernate有一个OpenSessionInViewFilter ) – 一个简单的servletfilter,你可以放入web.xml (在你的其他servletfilter之前),Spring将自动创建一个线程安全的事务- 每个HTTP请求的感知Session 。 没有更多的LazyInitializationException

Hibernate.initialize(list)调用不会初始化集合中引用的目标实体对象。 您需要迭代报告并初始化每个单独的对象。 对初始化报告的调用将代理集合转换为报告代理的具体集合。 请尝试以下代码:

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

钝斧方法是通过向你的hbm类添加lazy="false"来关闭延迟加载。 如果每次检索整个对象时都会迭代整个对象(进行初始化步骤OBE),这可能是有意义的。

好的,我是个白痴。 我的问题是我浏览了堆栈跟踪,但没有真正读过它。 这是完整的堆栈跟踪(或其中一个,3个略有不同的版本显示在我的日志中)。

 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() 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显然正在进行某种服务器端监视,它调用toString,因为toString调用的东西没有正确初始化,所以它变得怪异。 所以我的问题是双重的,基于reflection的ToString对于hibernate pojos来说似乎是一个坏主意,而Netbeans通过尝试观察它来改变我的运行时行为。

谢谢大家的帮助,我想我一直在密切关注这个问题太久了,需要退一步。

@PersistenceContext(类型= PersistenceContextType.EXTENDED)

工作:)

实际上,有三种方法可以避免延迟初始化exception:

  • 在映射文件中将lazy属性设置为false。我不建议使用此方法,因为它会增加数据库负载,因此会降低性能。

  • 保持会话开放。 在处理数据之前,请勿关闭会话。 如果会话在请求期间处于打开状态,您可以获取关联的图表,但您需要确保该操作在同一事务中进行。

  • 急切地获取关联。 在HQL查询中,使用关键字“fetch”来检索关联。 从我的角度来看,这是避免延迟初始化问题的最佳解决方案。 在HQL中,您只需要在from子句中添加fetch关键字以急切地获取关联。

这是一个例子:

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

我写了一篇关于这个问题的post,有一些代码示例和github项目的链接:

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