【问题标题】:Why am I getting a Hibernate LazyInitializationException in this Spring MVC web application when the data displays correctly?当数据正确显示时,为什么我在这个 Spring MVC Web 应用程序中得到一个 Hibernate LazyInitializationException?
【发布时间】:2010-10-12 12:20:52
【问题描述】:

我正在尝试使用 Spring MVC 创建一个 Web 应用程序,并将 Hibernate 作为其 ORM 层。但是,由于我对这两个框架都没有经验,所以我很苦恼。

以下代码将正确显示我正在查找的所有记录,但仍会在我的日志中抛出堆栈跟踪。我很难找到有关集成 Hibernate 和 SpringMVC 的完整文档(我查看了 springsource.org 并阅读了互联网上的各种文章)。谁能指出我在这里做错了什么?

请注意,我花了一些时间在互联网上寻找答案,包括查看this SO 问题。不幸的是,这没有帮助。

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

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

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

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

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

在网页上使用这个显示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>

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

在我的 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>

这里是 ORM 映射,以防万一;

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>

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

【问题讨论】:

  • 请也发布报告的映射
  • 添加了 hibernate.cfg.xml,其中包含我的映射。
  • 可以显示 Report.hbm.xml 文件吗?
  • 我发布了它,我还添加了关于在独立 java 应用程序中工作的 ORM 组件的注释。

标签: java hibernate spring web-applications netbeans


【解决方案1】:

我刚刚经历了这个 LazyInitialization 马拉松。

核心问题是您试图在Session 的生命周期之外访问休眠管理的实体,即在 Spring MVC 的 Web 视图中。就我而言,这是一个 List&lt;&gt; @OneToMany 关联,默认情况下会延迟加载。

有几种不同的方法——Mark 提到了一种方法,您可以在其中对惰性关联进行“虚拟”迭代。您还可以通过配置(类范围)(在 JPA 中为 @Fetch(value = FetchType.EAGER))或更具体地通过 HQL 强制预加载。但这将证明更多problematic if your lazy associations are Lists

我找到的最干净的解决方案是使用 Spring 的 OpenEntityManagerInViewFilter(有一个用于 Hibernate 的 OpenSessionInViewFilter)——一个简单的 servlet 过滤器,您可以放入 web.xml(在其他 servlet 过滤器之前),Spring 会自动创建一个线程安全、事务感知的Sessionper-HTTP-request。没有更多的LazyInitializationException

【讨论】:

  • 谢谢,我还没有遇到这个问题(不知道为什么),但这听起来像是一些建议,将来可能会非常有用。
【解决方案2】:

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

【讨论】:

  • 嗯,是的。我正在使用 apache common ReflectionToString 来构建我的 to 字符串,明天我将尝试删除它们。我也在堆栈跟踪中看到了这一点,直到你提到它,我才知道为什么我没有想到。我想我只是盯着这个问题太久了。
【解决方案3】:

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

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

直截了当的方法是通过将lazy="false" 添加到您的 hbm 类来关闭延迟加载。如果每次检索到整个对象时总是迭代整个对象(进行初始化步骤 OBE),这可能是有意义的。

【讨论】:

    【解决方案4】:

    好吧,我是个白痴。我的问题是我浏览了堆栈跟踪但并没有真正阅读它。这是完整的堆栈跟踪(或其中之一,我的日志中显示了 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 显然在做某种服务器端监控,它调用 toString,因为 toString 调用的东西没有正确初始化,所以这很糟糕。所以我的问题是双重的,基于反射的 ToString 对于休眠 pojos 来说似乎是一个坏主意,而 Netbeans 正在通过尝试观察它来改变我的运行时行为。

    感谢大家的帮助,我想我只是太仔细地研究这个问题太久了,需要退后一点。

    【讨论】:

      【解决方案5】:

      @PersistenceContext(type=PersistenceContextType.EXTENDED)

      是工作:)

      【讨论】:

      • Re: "@PersistenceContext(type=PersistenceContextType.EXTENDED) 是 WORK :)" 这是一个相当简单的答案。 PersistenceContextType.EXTENDED 意味着您必须管理自己的事务
      【解决方案6】:

      其实避免延迟初始化异常的方法有以下三种:

      • 在映射文件中将惰性属性设置为 false。我不推荐这种方法,因为它会增加数据库负载,因此会降低性能。

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

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

      这是一个例子:

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

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-02
        • 2013-11-10
        • 1970-01-01
        相关资源
        最近更新 更多