当数据正确显示时,为什么我在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/