Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does @Transactional(propagation = Propagation.SUPPORTS, readOnly = true) fix Hibernate Lazy Loading Exceptions?

I switched an app I am working on from using AspectJ load time weaving to using Spring CGlib proxies and right after I did that there were many parts of the code where I started to get hibernate lazy loading exceptions where in the past there were no exceptions being thrown.

I have been able to resolve these lazy loading exceptions by adding @Transactional(propagation = Propagation.SUPPORTS, readOnly = true) to bunch of previously public methods that did not have any transactional attributes on them but called spring repositories to read data from the database.

Anyone know why adding @Transactional(propagation = Propagation.SUPPORTS, readOnly = true) eliminates the hibernate lazy loading exceptions and why these annotations were not required with AspectJ load time weaving but are are required with out?

Update 2 I believe that removing AspectJ was NOT the issue, but the issue was was that I did not really understand the actual behavior of the SUPPORTS propagation. In particular how SUPPORTS interacted with the JPA EntityManager and so I removed a bunch a SUPPORTS propagation which caused the lazy loading exceptions. After reading through the source code for the Spring Transaction Manager It all became clear as to what to do. The key idea that the spring documentation does not really point out very well is that @Transactional annotations are used as synchronization points that tie life cycle of an EntityManager to the start and end of a transactional method. Also highly recommend this series of articles at http://www.ibm.com/developerworks/java/library/j-ts1/ and this blog post http://doanduyhai.wordpress.com/2011/11/21/spring-persistencecontext-explained/

Update 1

This is not a case of calls to private @Transactional methods not going through the AOP proxy. These issues are happening with public methods that are being called from other services.

Here is an example of the code structure, where I see the problem occurring.

@Service
public class FooService 
{
   @Autowired
   private BarService barService;

   public void someMethodThatOnlyReads() {
      SomeResult result = this.barService.anotherMethodThatOnlyReads()

      // the following line blows up with a HibernateLazyLoadingEcxeption 
     // unless there is a @Transactional supports annotation on this method
      result.getEntity().followSomeRelationship(); 
    }

}

@Service
public class BarService 
{
   @Autowired
   private BarRepository barRepo;

   public SomeResult anotherMethodThatOnlyReads()
   {
      SomeEntity entity =  this.barRepo.findSomeEntity(1123);
      SomeResult result = new SomeResult();
      result.setEntity(entity);
      return result; 
    }
}

@Repository
public class BarRepository 
{
   @PersistenceContext
   private EntityManager em;

   public SomeEntity findSomeEntity(id Integer)
   {
      em.find(SomeEntity.class,id);
   }
}
like image 479
ams Avatar asked Apr 17 '13 14:04

ams


1 Answers

I assume your code is not using OpenSessionInViewFilter or anything similar.

Without the @Transactional annotation, the Hibernate session is closed after leaving the BarRepository.findSomeEntity() method.

When a @Transactional method is called and the TransactionalInterceptor is properly bound to the method (through the cglib proxy or whatever other AOP configuration you have in the Spring context), then the Session is held open by Spring for the entirety of the annotated method, thus preventing any lazy loading exceptions.

If you turn up the logging to DEBUG on the org.springframework.transaction and org.springframework.orm.hibernate3 (or hibernate4 if you are on Hibernate 4) loggers, particularly the HibernateTransactionManager class and org.springframework.transaction.support.AbstractPlatformTransactionManager, you should see exactly at which points in the code flow Spring is deciding it needs to open and close the Hibernate Session. The logs should also show why a session or transaction is opened/closed at each point.

like image 86
matt b Avatar answered Oct 03 '22 05:10

matt b