Hibernate, Spring and Lazy Initialization

Tomislav
2004-02-27
2004-05-25
  • Tomislav

    Tomislav - 2004-02-27

    So I've read the various posts addressing problems with using lazy="true" in the hibernate xml, but I'm still not clear on what the proper approach/workaround for this is.  Is there a pattern/example out there that addresses this issue?

     
    • Seth Ladd

      Seth Ladd - 2004-02-27

      What problems are you refering to?  I've found that lazy loading is nice, as long as I keep the Session open.  To date, I've been using a Servlet filter to close the session for me.  This way, it's closed when all processing is done, including any JSP rendering.

      Of course, this is web specific.

      The hibernate site has some good examples of the ThreadLocal usage of storing and retreiving the Session.  Combined w/ the SessionFilter, you should be safe.  Spring uses the ThreadLocal in its tracking of the Session, too.

       
    • Matthew Payne

      Matthew Payne - 2004-03-04

      So whats the answer that is not web specific? 

      What happens when I just want to run junit tests on my objects, but I don't want to setup a web enviroment for them?   
      I have used the SessinInView/Filter, and it works for a web.  However, I would like to use some of the same objects outside a web container. 

      Is there a simple way to implement this?

       
      • William Jaynes

        William Jaynes - 2004-03-04

        To run junit tests you'll need to duplicate what the Spring OpenSessionInViewFilter does. Just use methods like the following in your setUp() and tearDown() methods.

        private void openHibernateSession(ApplicationContext ac)
            {
                sessionFactory = (SessionFactory) ac.getBean("sessionFactory");
                session = SessionFactoryUtils.getSession(sessionFactory, true);
                session.setFlushMode(FlushMode.NEVER);
                TransactionSynchronizationManager.bindResource(
                    sessionFactory,
                    new SessionHolder(session));
            }

            private void closeHibernateSession()
            {
                TransactionSynchronizationManager.unbindResource(sessionFactory);
                SessionFactoryUtils.closeSessionIfNecessary(session, sessionFactory);
            }

         
      • Andre Paradis

        Andre Paradis - 2004-03-04

        Hi,

        There is a post from Juergen on this:
        https://sourceforge.net/forum/message.php?msg_id=2416981

        From those infos, I've made a base test case extending JUnit TestCase:

        class SpringTestCase extends TestCase

        In this class, i redefined setUp() and tearDown() to open and close hibernate session.

        All of our test cases then sublclass SpringTestCase.  When you need to redefine setUp() and tearDown() in these classes, just call super.setUp() and super.tearDown() to insure proper session management.

        From SpringTestCase :

        protected void setUp() throws Exception {
              // open and bind the session for this test thread.
              Session s = sf.openSession();
              TransactionSynchronizationManager.bindResource(sf, new SessionHolder(s));
        }

        protected void tearDown() throws Exception
            {
              // unbind and close the session.
              SessionHolder holder =
                  (SessionHolder) TransactionSynchronizationManager.getResource(sf);
              Session s = holder.getSession();   
              s.flush();
                TransactionSynchronizationManager.unbindResource(sf);
              SessionFactoryUtils.closeSessionIfNecessary(s, sf);
        }

        To initialize Spring, i just use a static block using FileSystemXmlApplicationContext.

        Andre

         
        • Steven Caswell

          Steven Caswell - 2004-05-25

          Im curious as to why you included a session flush in your tearDown() method. The OpenSessionInViewFilter explicitly does not perform a flush.

           
    • Tomislav

      Tomislav - 2004-03-11

      So given the following application context (directly from PetClinic) how would I wire the openSessionInViewInterceptor bean

        <bean id="openSessionInViewInterceptor" class="org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor">
              <property name="sessionFactory">
                  <ref bean="sessionFactory"/>
              </property>
          </bean>

      into the rest of it in order to enable lazy initialization?  My set up is pretty much exactly the same, and I've gotten that OpenSessionInViewInterceptor is intended to allow lazy loading in a Hibernate context, but I'm not clear on how to put it all together...thanks!

          <!-- JNDI DataSource for J2EE environments -->
         
          <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
              <property name="jndiName"><value>java:comp/env/jdbc/petclinic</value></property>
          </bean>

          <!-- Hibernate SessionFactory -->
          <bean id="sessionFactory" class="org.springframework.orm.hibernate.LocalSessionFactoryBean">
              <property name="dataSource"><ref local="dataSource"/></property>
              <property name="mappingResources">
                  <value>org/springframework/samples/petclinic/hibernate/petclinic.hbm.xml</value>
              </property>
              <property name="hibernateProperties">
                  <props>
                      <prop key="hibernate.dialect">${hibernate.dialect}</prop>
                  </props>
              </property>
          </bean>

          <!-- Transaction manager for a single Hibernate SessionFactory (alternative to JTA) -->
          <bean id="transactionManager" class="org.springframework.orm.hibernate.HibernateTransactionManager">
              <property name="sessionFactory"><ref local="sessionFactory"/></property>
          </bean>

          <!-- ========================= BUSINESS OBJECT DEFINITIONS ========================= -->

          <!-- Transactional proxy for the Petclinic primary business object -->
          <bean id="clinic" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
              <property name="transactionManager"><ref local="transactionManager"/></property>
              <property name="target"><ref local="clinicTarget"/></property>
              <property name="transactionAttributes">
                  <props>
                      <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
                      <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
                      <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
                      <prop key="store*">PROPAGATION_REQUIRED</prop>
                  </props>
              </property>
          </bean>

          <!-- Petclinic primary business object: Hibernate implementation -->
          <bean id="clinicTarget" class="org.springframework.samples.petclinic.hibernate.HibernateClinic">
              <property name="sessionFactory"><ref local="sessionFactory"/></property>
          </bean>

       
      • Tomislav

        Tomislav - 2004-03-11

        Forgot to mention that I'm using Spring MVC, so I gather this is the approach rather than using the Filter

         
      • Juergen Hoeller

        Juergen Hoeller - 2004-03-11

        OpenSessionInViewInterceptor is meant to be used with Spring's web MVC framework, to be registered with a HandlerMapping. In the case of Petclinic, you could modify petclinic-servlet.xml as follows:

        <bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
          <property name="interceptors">
            <list>
              <ref local="openSessionInViewInterceptor"/>
            </list>
          </property>
          <property name="mappings">
            <props>
              <prop key="/welcome.htm">clinicController</prop>
              ...
            </props>
          </property>
        </bean>

        <bean id="openSessionInViewInterceptor" class="org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor">
        <property name="sessionFactory">
        <ref bean="sessionFactory"/>
        </property>
        </bean>

        For usage with any other sort of web components, stick with OpenSessionInViewFilter.

        Juergen

         
        • Whatever

          Whatever - 2004-03-12

          Maybe I'm stupid (or slept too little or...), but I still get:

          net.sf.hibernate.LazyInitializationException: Failed to lazily initialize a collection - no session or session was closed
          net.sf.hibernate.collection.PersistentCollection.initialize(PersistentCollection.java:214)
          net.sf.hibernate.collection.PersistentCollection.read(PersistentCollection.java:71)
              net.sf.hibernate.collection.Set.contains(Set.java:122)
          com.triligon.entities.user.SimpleUserImpl.isMemberOfRole(Unknown Source)
          com.triligon.entities.accounting.CustomerImpl.mayCreateNewInstance(Unknown Source)

          even though I have
          <!-- enable lazy loading in web MVC -->
          <bean id="openSessionInViewInterceptor" class="org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor">
              <property name="sessionFactory"> <ref bean="SessionFactory"/> </property>
          </bean>

          <!-- This will map /foo to the bean with name="/foo" -->
          <bean id="urlMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
              <property name="interceptors">
                  <list><ref bean="openSessionInViewInterceptor"/></list>
              </property>
          </bean>

          in my webcontext.xml.

           
          • Whatever

            Whatever - 2004-03-13

            I must have indeed been half a sleep already, as the exception occurs before the view is even called, i.e. deep in the controller/business object interaction which strikes me as weirder, even. The business object is a subclass of HibernateDaoSupport and is injected a SessionFactory upon creation, so why can't it load it's collections?

             
        • Tomislav

          Tomislav - 2004-03-15

          Thank you so much Juergen that did the trick!  I definetely think this should be included as part of the Pet Client example in future releases...great!

           
        • Tomislav

          Tomislav - 2004-03-19

          So the above worked great from my Spring MVC web app.  However, I also expose certain mathods via JAX-RPC.  I am now getting the LazyInitialization error when hiting the app via Web Services.  Are the OpenSessionInView* classes even relevant here?  How can I make the Hibernate session available during a JAXRPC call?

           
          • Whatever

            Whatever - 2004-03-22

            As long as your RPC call uses a transaction for the whole time of it, it should work. Now of course you can't go about using lazy initiliazation in the client (that would mean that the client needed direct access to the DB which is hardly what you'd want).

             
        • Dan Hayes

          Dan Hayes - 2004-04-10

          I apologize for resurfacing this issue however I am still receiving the "Failed to lazily initialize a collection - no session or session was closed" despite implementing the OpenSessionInViewInterceptor bean as described in your earlier post.  I've confirmed that the interceptor is configured properly and being called at the appropriate times by simply extending the Spring class, putting a debug statement in the preHandle method then calling the superclass method.  Is there something else I need to do in these methods?  My application follows nearly the identical patterns as the PetClinic example, except for the fact that I am also using Tiles.  The transaction attributes on dao methods are declared as PROPAGATION REQUIRED, read only except for the store methods which are PROPAGATION REQUIRED.  Any ideas as to where to focus my attention in trying to solve this?  Do I need to do something in my jsp's to grab the session?

           
          • Daniel Miller

            Daniel Miller - 2004-04-10

            I would guess that you loaded the object your are trying to access in a different session than the one you are trying to access it with. If this is the case, you need to "DAO.update(your_object)" in the new session. Actually there are several approaches to this problem. For more info read the Hibernate FAQ entitled "Do I have to keep the Session open if I want to use lazy initalization?" under "FAQs from the Forum" (or just go here: http://www.hibernate.org/74.html#A4\).

            Daniel Miller

             
            • Dan Hayes

              Dan Hayes - 2004-04-10

              Yes. I am loading the object in a dao that is using the spring framework (via the hibernate template). Transactions in this dao are managed declaratively via AOP and managed by Spring (see Petclinic sample application for the blueprint).  However, I was under the impression that by defining the OpenSessionInViewInterceptor (or filter in my case), I could re-connect to a hibernate session allowing me to navigate lazily loaded associations freely from within my view (jsp).  It seemed to me that this filter took care of all the plumbing.  Obviously, there is more to it.  Thanks for the reply.

               
              • Dan Hayes

                Dan Hayes - 2004-04-11

                Well. It's been one of those days!  I've figured out that the problem with my OpenSessionInView filter wasn't the filter configuration at all.  I was trying to lazy load a collection from an object reference in session scope, not one that was retrieved in the current request.  Duh!  Of course it couldn't reconnect to a session.  Oh well, at least I learned alot about Spring and Hibernate session management in the process of uncovering my careless mistake.  Sorry....

                 
    • Dan Hayes

      Dan Hayes - 2004-04-10

      BTW, I also tried configuring a filter instead of the interceptor due to the struts incompatibility as noted above (I am using the Spring Tiles adapter, quite successfully).  However, I am still receiving the lazy initialize exception.  Interestingly enough, my JUnit test cases that test the lazy initialization run fine (thanks to the setup and tear down code posted earlier!). However, no such luck on the web side even though the same hibernate mapping file is being used.  I know the filter is setup properly since the logging statements reveal that the OpenSessionInViewFilter is indeed being found and the "sessionFactory" bean is found just prior to throwing the lazy initialization exception. 

      This is really frustrating!

       
    • bard

      bard - 2004-04-14

      i wrote a little spring interceptor to do this outside of the web paradigm:

      public class HibernateSessionInterceptor implements MethodInterceptor, InitializingBean
      {
          private SessionFactory sessionFactory;

          protected final Log logger = LogFactory.getLog(getClass());

          /**
           * @param arg0
           * @return
           * @throws Throwable
           */
          public Object invoke(MethodInvocation invocation) throws Throwable
          {
              Object retVal = null;
              Session s = null;
              try
              {
                  s = sessionFactory.openSession();
                  TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(s));
                  retVal = invocation.proceed();
                  return retVal;
              }
              catch (Throwable ex)
              {
                  throw ex;
              }
              finally
              {
                  SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
                  TransactionSynchronizationManager.unbindResource(sessionFactory);
                  SessionFactoryUtils.closeSessionIfNecessary(s, sessionFactory);
              }
          }

          /**
           * @throws Exception
           */
          public void afterPropertiesSet() throws Exception
          {
              if (this.sessionFactory == null)
              {
                  throw new IllegalArgumentException("sessionFactory is required");
              }
          }

          /**
           * @param sessionFactory The sessionFactory to set.
           */
          public void setSessionFactory(SessionFactory sessionFactory)
          {
              this.sessionFactory = sessionFactory;
          }
      }

       

Log in to post a comment.