Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why am I getting a Hibernate LazyInitializationException in this Spring MVC web application when the data displays correctly?

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.

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?

Please note that I have spent a few trying to track down answers on the internet for this, including looking at this SO question. Which was unfortunately no help.

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.

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

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;

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

And in my 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;

The hibernate.cfg.xml (as requested)

<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

<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>
like image 833
James McMahon Avatar asked Mar 10 '09 18:03

James McMahon


People also ask

How do I fix hibernate LazyInitializationException?

The right way to fix a LazyInitializationException is to fetch all required associations within your service layer. The best option for that is to load the entity with all required associations in one query.

What is org hibernate LazyInitializationException?

Indicates an attempt to access not-yet-fetched data outside of a session context. For example, when an uninitialized proxy or collection is accessed after the session was closed.

Could not initialize proxy the owning session was closed hibernate?

We have seen that this error mainly comes when you have closed the connection and trying to access the proxy object which is no fully initialized. Since Proxy object needs a connection, you can either reattach object to the session or carefully avoid writing such code, which access uninitialized Proxy object.


2 Answers

I just went through this LazyInitialization marathon.

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.

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.

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!

like image 56
jtgameover Avatar answered Oct 12 '22 18:10

jtgameover


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.

like image 34
Mark Avatar answered Oct 12 '22 20:10

Mark