Pages

Tuesday, July 5, 2011

Struts2+Spring+Hibernate Declarative Transaction

Problem Faced:

The DAO classes were annotated with @Transactional and the applicationContext.xml is updated with
 "<tx:annotation-driven mode="proxy" proxy-target-class="true" transaction-manager="transactionManager" / >"
 to use spring managed transactions in hibernate.


The below DAO method (reading from db) is working as expected.

public List findJourney(){
Query q=getSession().createQuery("from Journey");
List list=q.list();
return list;
}
However, the other method in the same DAO (save operation) is failing with HibernateException.
public boolean createJourney(Journey journey){
System.out.println("## createJurney in DAO");
try{
getSessionFactory().getCurrentSession().save(journey);
}catch(Exception e){
e.printStackTrace();
}
return true;
}

Exception throws is:
23:35:26,957 DEBUG SessionImpl:248 - opened session at timestamp: 13098891269
org.hibernate.HibernateException: save is not valid without active transaction
at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:338)
at $Proxy63.save(Unknown Source)
at com.red.travels.dao.JourneyDAOImpl.createJourney(JourneyDAOImpl.java:19)
at com.red.travels.dao.JourneyDAOImpl$$FastClassByCGLIB$$976443ed.invoke()
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:191)

Solution:
In the applicationContext.xml file, for the sessionFactory bean configuration I have mentioned the property "hibernate.current_session_context_class" with "thread" as the value. This is required in hibernate, to use getCurrentSession()  i.e. to get the thread local session. However, this will not work with Spring transaction management. Spring 2.5+  override the default CurrentSessionContext (Spring's) with the standard Hibernate thread-local context. This is causing the problem. Refer to Spring JIRA

Once this property is removed from the sessionFactory configuration, everything works fine.

Struts2+Spring+Hibernate and OpenSessionInViewFilter


Problem Faced:

  • I have a struts2 action class (which is declared as a spring bean in application context) to retrieve the users list from the database and to show on the jsp.
  • Used Spring in the middle tier for transaction management (annotation based) and  HibernateDAOSupport form Spring to manage the DAO layer.
  • The action will call a service layer method which is transactional. The service method inturn called the UserDAO  to retrieve the User details. The User class has a relation with Account class.
  • By the time , the service layer method returns, only the User object will be initialized. Because , the Account entity is mapped as “lazy”.
  • In the JSP page, if I try to access Account property of the User, I’ll get LazyInitilizationException (as expected).

Now configured the “OpenSessionInViewFilter” in the web.xml. But still getting the same exception. The transaction is closing the underlying hibernate session once the transaction is completed.
In the logs I could see that the Filter is getting initialized, but no other statements related to it.
After some debugging found that, The OSIV filter is mapped in the web.xml after, struts2 filter. As a result, it is never applied to my urls. Changed the order of filters in web.xml. Now everything is working as expected.