Java 当数据正确显示时,为什么在此 Spring MVC Web 应用程序中出现 Hibernate LazyInitializationException?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/631695/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why am I getting a Hibernate LazyInitializationException in this Spring MVC web application when the data displays correctly?
提问by James McMahon
I am attempting to create a web application using Spring MVC, with Hibernate as its ORM layer. However, due to my inexperience with both frameworks I'm struggling.
我正在尝试使用 Spring MVC 创建一个 Web 应用程序,并将 Hibernate 作为其 ORM 层。但是,由于我对这两个框架都缺乏经验,所以我很挣扎。
The following code will properly display all the records I am looking for but still throw a stack trace into my logs. I'm having trouble finding thorough documentation concerning integrating Hibernate and SpringMVC (I've looked on springsource.org and read various articles on the interweb). Could anyone point out what I could be doing wrong here?
以下代码将正确显示我正在查找的所有记录,但仍将堆栈跟踪放入我的日志中。我很难找到有关集成 Hibernate 和 SpringMVC 的详尽文档(我在 springsource.org 上查看并阅读了互联网上的各种文章)。谁能指出我在这里做错了什么?
Please note that I have spent a few trying to track down answers on the internet for this, including looking at thisSO question. Which was unfortunately no help.
请注意,我花了一些时间试图在互联网上找到答案,包括查看这个SO 问题。不幸的是,这没有帮助。
I should also note that the ORM part of this application has been used and tested in a stand alone Java application without problems. So I believe the integration of Spring MVC and Hibernate is causing the issue.
我还应该注意到,这个应用程序的 ORM 部分已经在一个独立的 Java 应用程序中使用和测试,没有问题。所以我相信 Spring MVC 和 Hibernate 的集成导致了这个问题。
Here is the stack trace (truncated) with the famous lazy initialization issue;
这是著名的延迟初始化问题的堆栈跟踪(截断);
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$abaed6.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)
.....
Here is a code from my web page controller;
这是来自我的网页控制器的代码;
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;
}
Which is employed on the web page using this display html;
使用此显示 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>
Note: That I added report.searchRule.name to test if I could get at the objects within the report object. It displays fine.
注意:我添加了 report.searchRule.name 来测试我是否可以获取报表对象中的对象。显示正常。
And in my applicationContext.xml;
在我的 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>
Here are the ORM mappings, just in case;
这是 ORM 映射,以防万一;
The hibernate.cfg.xml (as requested)
hibernate.cfg.xml(根据要求)
<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>
From report.hbm.xml
来自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>
采纳答案by Mark
I am just guessing but from the stack trace it seems that toString is being called on SearchRule. Does SearchRule have any child objects that may not have been loaded? If SearchRule.toString was trying to get the value for an uninitialised child object that could result in the LazyInitializationException.
我只是在猜测,但从堆栈跟踪来看,似乎在 SearchRule 上调用了 toString。SearchRule 是否有任何可能尚未加载的子对象?如果 SearchRule.toString 试图获取可能导致 LazyInitializationException 的未初始化子对象的值。
回答by Mark
The Hibernate.initialize(list) call doesn't initialize the target entity objects that are referenced in the collection. You would need to iterate over reports and initialize each individual object. The call to initialize reports turns a proxy collection into a concrete collection of Report proxies. Try code below:
Hibernate.initialize(list) 调用不会初始化集合中引用的目标实体对象。您需要遍历报告并初始化每个单独的对象。初始化报告的调用将代理集合转换为报告代理的具体集合。试试下面的代码:
for(Report r : reports)
Hibernate.initialize(r);
The blunt axe approach is to turn off lazy loading by adding lazy="false"
to your hbm classes. This might make sense if you will always iterate over the entire object every time its retrieved (make the initialization step OBE).
钝斧方法是通过添加lazy="false"
到您的 hbm 类来关闭延迟加载。如果每次检索对象时始终遍历整个对象(使初始化步骤 OBE),这可能是有意义的。
回答by jtgameover
I just went through this LazyInitialization marathon.
我刚刚经历了这个 LazyInitialization 马拉松。
The core problem is that you're trying to access an hibernate-managed entity outside of the lifecycle of the Session
, i.e. in the web view of Spring MVC. In my case, this was a List<>
@OneToMany
association, which are lazily loaded by default.
核心问题是您试图访问 生命周期之外的休眠管理实体Session
,即在 Spring MVC 的 Web 视图中。就我而言,这是一个List<>
@OneToMany
关联,默认情况下是延迟加载的。
There are a few different approaches -- Mark mentioned one, where you do a "dummy" iteration over the lazy associations. You can also force eager loading, either via the configuration (class-wide) (in JPA it'd be @Fetch(value = FetchType.EAGER)
) or more specifically through the HQL. But this will prove more problematic if your lazy associations are Lists
.
有几种不同的方法——Mark 提到了一种方法,您可以对惰性关联进行“虚拟”迭代。您还可以通过配置(类范围内)(在 JPA 中@Fetch(value = FetchType.EAGER)
)或更具体地通过 HQL来强制预加载。但是,如果您的懒惰关联是Lists
.
The cleanest solution I found was to use Spring's OpenEntityManagerInViewFilter
(there's an OpenSessionInViewFilter
for Hibernate) -- a simple servlet filter you drop in to web.xml
(ahead of your other servlet filters), and Spring will automatically create a thread-safe, transaction-aware Session
per-HTTP-request. No more LazyInitializationException
!
我发现的最干净的解决方案是使用 Spring 的OpenEntityManagerInViewFilter
(有一个OpenSessionInViewFilter
用于 Hibernate)——一个简单的 servlet 过滤器web.xml
(在其他 servlet 过滤器之前),Spring 将自动创建一个线程安全的、事务感知的Session
per-HTTP -请求。没有了LazyInitializationException
!
回答by James McMahon
Ok I am an idiot. My problem is I glanced over the stack trace but didn't really read it. Here is the full stack traces (or one of them, 3 slightly different versions show up in my logs).
好吧,我是个白痴。我的问题是我浏览了堆栈跟踪,但并没有真正阅读它。这是完整的堆栈跟踪(或其中之一,我的日志中显示了 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(<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 is apparently doing some kind of server side monitoring, which calls the toString, which freaks out because something called by the toString isn't initialized properly. So my problem is two fold, the reflection based ToString seems like a bad idea for hibernate pojos and Netbeans is changing my runtime behavior by trying to observe it.
Netbeans 显然是在做某种服务器端监控,它调用 toString,它吓坏了,因为 toString 调用的东西没有正确初始化。所以我的问题是双重的,基于反射的 ToString 对于休眠 pojos 来说似乎是一个坏主意,而 Netbeans 正在通过尝试观察它来改变我的运行时行为。
Thanks everyone for helping, I think I had just been looking at this problem too closely for too long and needed to step back for a little.
感谢大家的帮助,我想我只是太仔细地看这个问题太久了,需要退一步。
回答by shalunv
@PersistenceContext(type=PersistenceContextType.EXTENDED)
@PersistenceContext(type=PersistenceContextType.EXTENDED)
is WORK :)
是工作:)
回答by ignacio.suay
Actually,there are three ways to avoid the Lazy Initialization Exception:
实际上,有三种方法可以避免延迟初始化异常:
Set the lazy property to false in the mapping file.I don't recommend this approach because it will increment the database load and therefore, it will produce a decrease in performance.
Keep the session open. Don't close the session before you have processed the data. If the session is open during the request you could get the associated graph but you need to be sure that the action takes within the same transaction.
Eagerly fetch the associations. In the HQL query use the keyword "fetch" to retrieve the association. From my point of view this is the best solution to avoid the lazy initialization problem. In HQL, you just need to add the fetch keyword in the from clause to eagerly fetch an association.
在映射文件中将lazy 属性设置为false。我不推荐这种方法,因为它会增加数据库负载,因此会导致性能下降。
保持会话打开。在处理数据之前不要关闭会话。如果会话在请求期间打开,您可以获得关联图,但您需要确保操作在同一事务中执行。
急切地获取关联。在 HQL 查询中,使用关键字“fetch”来检索关联。从我的角度来看,这是避免延迟初始化问题的最佳解决方案。在 HQL 中,您只需要在 from 子句中添加 fetch 关键字即可急切地获取关联。
Here is an example:
下面是一个例子:
from Doctor doc
left join fetch doc.patients
where doc.name like ‘Doctor A%'
I have written a post about this issue, with some code examples and links to the github project:
我写了一篇关于这个问题的帖子,有一些代码示例和github项目的链接:
http://ignaciosuay.com/how-to-avoid-hibernate-lazy-initialization-exception/
http://ignaciosuay.com/how-to-avoid-hibernate-lazy-initialization-exception/