Announcement Announcement Module
Collapse
No announcement yet.
Hibernate LazyInitializationException and ThrowsAdvice Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Hibernate LazyInitializationException and ThrowsAdvice

    Hi,

    I have a working app based on Spring 1.2.7, hibernate-3.2.0.cr1 and hibernate-annotations-3.1beta9. So actually I have my DAOs extend HibernateDaoSupport as usual.

    Unfortunatelly, all the associations are declared to be fetched eagerly (fetch=FetchType.EAGER), and of course I now run into huge performance issues

    The point is that I would like not to fall into LazyInitializationException, and I would like not to change a lot how I interact with my persistent objects.

    The OpenInSessionViewFilter or OpenInSessionViewInterceptor seem not to fit my needs (correct me if I am wrong!), because I have a big suite of unit tests which are independent from the view, the container and even the (spring mvc) controllers: I just test the business logic on the model infact, and how the model is persisted by the subclass of HibernateDaoSupport. So I would like really not to add session code in them.

    Having little notion of AOP in Spring and AOP in general, I was thinking that a dumb approach could be: whenever a model entity object getter method invokation throws a LazyInitializationException, I could intercept the event, open the hibernate session on the fly, call again the method to actually resolve the lazy association, and then close the session.

    In this way my code would not change at all and I could set all the EAGER fetching strategies to LAZY

    Is this approach wrong? If yes, why? if not, what are the things that I have to consider for not getting into big troubles? Is there anything already made that shows how to realize this?

    Thanks in advance for any help,

  • #2
    It's good that your tests are decoupled from the view, however, starting a transaction during the test (and this having the session opened) can be easily achived by using one of Spring's test from the .test package. Yo can also start the transaction by yourself - if you look at the tests you'll see that they are quite simple.
    There have been some discussions on the forum (which I advice you to search) but in short, HB sessions should not be reused after an exception is thrown which is your case.

    Comment


    • #3
      Hi,
      thanks for the answer.

      So, what I did in order to *not* change a lot my JUnit tests code was:

      1) use a single DAO interface (with generic methods) that has the @Transational annotation and proxying its classic HibernateTemplate based implementation by means of the TransactionProxyFactoryBean

      Code:
      @Transactional
      public interface PersistenceManager {
         
          public <T> List<T> findAll(Class<T> entityClass);
          public <T> T findById(Class<T> entityClass, Long id);
          public <T> void saveOrUpdate(T entity);	
          public <T> T merge(T entity);
          public <T> void remove(T entity);
          public <T> void removeAll(Class<T> entityClass);
          // ... others
      }
      Code:
      <bean id="txManager"
      		class="org.springframework.orm.hibernate3.HibernateTransactionManager">
      		<property name="sessionFactory" ref="sessionFactory" />
      	</bean>
      
      	<bean id="persistenceManagerTarget" class="org.mypackage.persistence.hibernate.PersistenceManagerHibernate">
      		<property name="sessionFactory">
      			<ref bean="sessionFactory"/>
      		</property>
      	</bean>
      
      	<bean id="persistenceManager"
      		class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
      		<property name="transactionManager" ref="txManager" />
      		<property name="target" ref="persistenceManagerTarget" />
      		<property name="transactionAttributeSource">
      			<bean
      				class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource" />
      		</property>
      	</bean>
      2) adding some boiler plate code in the setUp() method of each TestCase class that is meant to use inside testXxx() methods lazy associations:

      Code:
      protected void setUp() throws Exception {
          super.setUp();
      
          SessionFactory sessionFactory = (SessionFactory) this.ac.getBean("sessionFactory");
          session = SessionFactoryUtils.getSession(sessionFactory, true);
          TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
      	
          // continue initializing stuff and persist them with the PersistenceManager
          // ...
      }
      3) adding some other boiler plate code to force flushing the commit of the operations before a lazy association is meant to be resolved:
      Code:
      public void testLazyAssociationDoNotThrowException() {
      
          SessionFactory sessionFactory = (SessionFactory)   this.ac.getBean("sessionFactory");
          SessionHolder holder = (SessionHolder)   TransactionSynchronizationManager.getResource(sessionFactory);
          Session s = holder.getSession(); 
          s.flush();
          TransactionSynchronizationManager.unbindResource(sessionFactory);
           // SessionFactoryUtils.closeSessionIfNecessary(s, sessionFactory);
      		
           Set<TextAnnotation> set = null;
           try {
               TextDocument textDocumentFromDb =   this.persistenceManager.findAll(TextDocument.class).get(0);
      	
               // the Set is declared as Lazy		
               set = textDocumentFromDb.getAnnotations();
          }catch(LazyInitializationException e) {
              fail(e.getClass() + " should not have been thrown!");
          
           assertEquals("Assert numbers of associations", 1, set.size());
      }
      Ok, now I wonder if (as in point (3) ) the session has to be explicitly flushed, and the resource unbinded, before I can access the Lazy set (if I don't do that and put the TransactionSynchronizationManager.unbindResource(s essionFactory); only in the tearDown method, then the last assertion on the number of elements in the Set fails: it says that the set is made by 0 elements, wrong!)

      Hope I have explained my scenario and I look for some suggestions.

      Comment


      • #4
        After a couple of days of hitting my head to the wall I found what I think is the right solution:
        http://www.jroller.com/page/kbaum?en...ation_with_dao

        I adapted that blog entry and now I have something like this:

        Code:
        <bean id="hibernateInterceptor"
        			class="org.springframework.orm.hibernate3.HibernateInterceptor">
        			<property name="sessionFactory">
        				<ref bean="sessionFactory" />
        			</property>
        		</bean>
        
        		<bean id="txManager"
        			class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        			<property name="sessionFactory" ref="sessionFactory" />
        		</bean>
        
        		<bean id="persistenceManagerTarget"
        			class="my.package.persistence.hibernate.PersistenceManagerHibernate">
        			<property name="sessionFactory">
        				<ref bean="sessionFactory" />
        			</property>
        		</bean>
        
        		<bean id="persistenceManager"
        			class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        			<property name="transactionManager" ref="txManager" />
        			<property name="target" ref="persistenceManagerTarget" />
        			<property name="transactionAttributeSource">
        				<bean
        					class="org.springframework.transaction.annotation.AnnotationTransactionAttributeSource" />
        			</property>
        		</bean>
        A component used inside a Spring MVC controller (below: SearchResultsPrinter is the interface, SearchResultsPrinterImpl the implementation, to please dynamic proxies) needs to navigate lazy associations and collections from the domain, so I proxyed it and use HibernateInterceptor for the lazy associations to work:

        Code:
        <bean id="searchResultsPrinterTarget" class="my.package.controller.SearchResultsPrinterImpl"> 
        	</bean>
        	
        	<bean id="searchResultsPrinter" class="org.springframework.aop.framework.ProxyFactoryBean">
                 <property name="target"><ref bean="searchResultsPrinterTarget"/></property>
                 <property name="proxyInterfaces">
                   <value>my.package.controller.SearchResultsPrinter</value>
                 </property>
                 <property name="interceptorNames">
                   <list>
                      <value>hibernateInterceptor</value>
                   </list>
                 </property>
             </bean>
        Then I found the beautiful (apart from the name..) AbstractTransactionalSpringContextTests for testing my model with lazy association inside a JUnit test case

        Note: I didn't use the OpenSessionInViewInterceptor cause I found that being "independent from the view" using the HibernateInterceptor (on the components which need to navigate lazy associations) makes a little easier to test those components with unit tests, and anyway make those usable outside of a request/response context. What do you think?

        Comment


        • #5
          AbstractTransactionalDataSourceSpringContextTests

          Great post, just what I needed. I had exactly the same problem.

          You should also check AppFuse regularly as Matt Raible seems to be up to date with the latest APIs -- that's where I found out about AbstractTransactionalDataSourceSpringContextTests

          -Yves-

          Comment


          • #6
            Hello, i am new in this topic with spring/hibernate, but no with hibernate
            i am working with spring 2.0

            i saw the important tutorial in http://www.jroller.com/page/kbaum?en...ation_with_dao

            exactly what packages import for this class ???

            MyLazyTestCase <------
            i have problems of incompatibility among some packages

            ie:
            Code:
            import net.sf.hibernate.Session; 
            import net.sf.hibernate.SessionFactory;
            
            //import org.springframework.orm.hibernate.SessionFactoryUtils;
            import org.springframework.orm.hibernate.SessionHolder;
            //import org.springframework.orm.hibernate3.LocalSessionFactoryBean;
            import org.springframework.transaction.support.TransactionSynchronizationManager;
            i guess that net.sf.hibernate and org.springframework.orm.hibernate work to hibernate 2

            but i want to work with hibernate 3, and i have probelms , just like this
            java.lang.ClassCastException: $Proxy0, coz my applicationContext.xml
            was configured to work with Hibernate 3

            thanks for advanced

            Comment


            • #7
              Hibernate 2 (net.sf.hibernate) is supported in spring by org.springframework.orm.hibernate classes while Hibernate 3 (org.hibernate) by org.springframework.orm.hibernate3.

              Comment

              Working...
              X