Spring – org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role (Hibernate + Spring)

hibernatehibernate-mappingspringspring-mvc

I have a SpringMVC+Hibernate web application with the following files:

applicationContext.xml

    <bean id="dataSource" destroy-method="close" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="org.postgresql.Driver"/>
    <property name="url" value="jdbc:postgresql://localhost:5432/db"/>
    <property name="username" value="kjhkjkj"/>
    <property name="password" value="dsfa@efe45"/>
</bean>

<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="hibernateSessionFactory"/>
</bean>

<bean id="transactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager" ref="txManager"/>
    <property name="transactionAttributes">
        <props>
            <prop key="*">PROPAGATION_REQUIRED</prop>
        </props>
    </property>
    <property name="target">
        <bean class="dao.DocumentViewDao">
            <property name="sessionFactory" ref="hibernateSessionFactory"/>
        </bean>
    </property>
    <property name="proxyTargetClass" value="true"/>
</bean>

<bean id="hibernateSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="mappingResources">
        <list>
    <value>orm/Document.hbm.xml</value>
    <value>orm/UploadedDocument.hbm.xml</value>
    <!-- More resource files go here -->
        </list>
    </property>
    <property name="hibernateProperties">
        <value>
            hibernate.dialect = org.hibernate.dialect.PostgreSQLDialect
            hibernate.show_sql = true
            hibernate.format_sql = true
            hibernate.generate_statistics = true
            hibernate.cache.use_query_cache = true
            hibernate.cache.use_second_level_cache = true
            hibernate.cache.provider_class = org.hibernate.cache.EhCacheProvider
            hibernate.query.substitutions = true 't', false 'f'
        </value>
    </property>
</bean>

<bean id="documentViewDao" class="dao.DocumentViewDao">
    <property name="sessionFactory" ref="hibernateSessionFactory"/>
</bean>

dao.DocumentViewDao

@Transactional
public class DocumentViewDao extends HibernateDaoSupport
{
public List all ( )
        throws HibernateException
{
    Session session = this.getSessionFactory ( ).getCurrentSession ( );

    return session.createQuery ( new StringBuilder ( 35 )
                    .append ( "SELECT d.id AS id, d.identifier AS identifier, d.title AS title, u.version AS version, u.effectFrom AS effectFrom " )
                    .append ( "FROM Document AS d " )
                    .append ( "LEFT OUTER JOIN d.uploadedDocumentsById AS u " )
                    .append ( "WITH u.isCurrent = true" )
                    .toString ( ) )
            .setCacheable ( true )
            .setResultTransformer( Transformers.ALIAS_TO_ENTITY_MAP )
            .list ( );
}

public Document getDocument ( int documentId )
        throws HibernateException
{
    Session session = this.getSessionFactory ( ).getCurrentSession ( );

    Document document = null;

    Query q = session.createQuery ( "FROM IsoDocument AS d " +
                                    "LEFT OUTER JOIN FETCH d.uploadedDocumentsById " +
                                    "WHERE d.id = :documentId"
    );
    q.setParameter ( "documentId", documentId );
    q.setCacheable ( true );
    document = ( Document ) q.uniqueResult ( );

    return document;
}
}

controller.DocumentController

public class DocumentController implements Controller
{   
    public ModelAndView handleRequest ( HttpServletRequest request, HttpServletResponse response )
            throws Exception
    {
        ModelAndView modelAndView = new ModelAndView ( "document" );

        WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext ( );
        DocumentViewDao documentViewDao = ( DocumentViewDao ) context.getBean ( "transactionProxy" );

        Document document = DocumentViewDao.getDocument ( Integer.parseInt ( request.getParameter ( "id" ) ) );
        modelAndView.addObject ( "document", document );

        return modelAndView;
    }
}

The ORM classes and mapping should be OK, I won't write them (lost of lines :))
So the problem is I get

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: orm.Document.uploadedDocumentsById, no session or session was closed

when the page generated by controller.DocumentController is refreshed. Obviously there is a problem with the caching and transactions which I cannot solve. Moreover, using all() from dao.DocumentViewDao doen't throw such an exception (but there isn't FETCH in the query). So do you have any ideas? I also will be happy to any comments and/or suggestions on this code in orger to improve it (am new to Spring and Hibernate).

Best Answer

When a query returning entity instance(s) is cacheable and you have a cache hit, the cache returns only the ID(s) of the entity(ies), and then the session is used to load the entity(ies). This means that the join fecth is not applied anymore when you have a cache hit. This also means that all the entity instances returned by the query should also be in the second-level cache, else the performance might in fact be worse than without the query cache.

You should thus use Hibernate.initialize(document.getUploadedDocumentsById() in the DAO to make sure it's initialized.