|
|
Spring-Hibernate database calls stops responding after a few calls
Hi all,
I'm experiencing a very strange problem with the Spring-Hibernate combination, which allowed a generic DAO class that extends HibernateDaoSupport. Specifically, after making 7 successful Hibernate database access calls,(whether they be criteria.list(), save(), get(), or delete()) on the 8th call, the application stops responding right when Hibernate makes the db access call. I would then have to manually stop the application by doing a Ctrl-C on the command line.
My database max connections is set to 100, DB process always at a steady 4% CPU usage, not a problem at all. Tried both MySQL andHSQLDB with no luck.
I'm using the following:
- Spring 2.5.6
- Hibernate 3.2.6
Below is the GenericHibernateDao class that I use and the simple test app i created to repro the problem:
public class GenericHibernateDaolt;T, ID extends Serializablegt; extends HibernateDaoSupport {
private Classlt;Tgt; persistentClass;
public void delete(T entity) { if (entity == null) { throw new IllegalArgumentException(quot;Entity cannot be nullquot;); } Session session = getSession(); session.delete(entity); session.flush(); session.evict(entity); }
public Listlt;Tgt; findAll() { System.out.println(quot;In DAO~~~~~~~~~~~~~quot;); Criteria criteria = getSession().createCriteria(getPersistentClass()); System.out.println(quot;after getSession-----quot;); return criteria.list(); }
public T findById(ID id) { return findById(id, false); }
public T findById(ID id, boolean lock) { System.out.println(quot;In DAO~~~~~~~~~~~~~quot;); if (id == null) { throw new IllegalArgumentException(quot;Id cannot be nullquot;); } if (lock) { return (T) getSession().get(getPersistentClass(), id, LockMode.UPGRADE); } return (T) getSession().get(getPersistentClass(), id); }
protected Classlt;Tgt; getPersistentClass() { if (persistentClass == null) { this.persistentClass = (Classlt;Tgt;) ((ParameterizedType) getClass().getGenericSuperclass()) .getActualTypeArguments()[0]; } return persistentClass; }
public T save(T entity) { if (entity == null) { throw new IllegalArgumentException(quot;Entity cannot be nullquot;); } Session session = getSession(); session.saveOrUpdate(entity); session.flush(); session.evict(entity);
return entity; }
}
------------------------------------------------------------------------
public static void main(String[] args) { ApplicationContext cxt = new ClassPathXmlApplicationContext(new String[]{quot;setup-beans.xmlquot;, quot;test-beans.xmlquot;}); ParentDao parentDao = (ParentDao)cxt.getBean(quot;parentDaoquot;); Parent parent = new Parent(); parent.setName(quot arent1quot;); parentDao.save(parent); Parent parent2 = new Parent(); parent.setName(quot arent2quot;); parentDao.save(parent2); for(int i=0; ilt;10; i++) { try { //Listlt arentgt; parents = parentDao.findAll(); Parent returnedParent = parentDao.findById(1L); junit.framework.Assert.assertTrue(returnedParent != null); Thread.sleep(3000); } catch(InterruptedException e) {} } }
-------------------------------------------------------------------------I'm clueless at this point and am desperately in need of help. Any feedback on how to resolve this issue would be appreciated. Let me know if you want me to post more code.Thanks in advance.
Max
I am able to reproduce the problem on Spring 2.5.5 and 2.5.6. But, discovering that the problem was NOT able to be reproduced on 2.5.1 was a surprising relief. So I found a bug! Does anyone know where I can submit the bug?
Originally Posted by maximosI am able to reproduce the problem on Spring 2.5.5 and 2.5.6. But, discovering that the problem was NOT able to be reproduced on 2.5.1 was a surprising relief. So I found a bug! Does anyone know where I can submit the bug?
Spring JIRA.
What is the bug?
Hi,
Sorry for the delayed response. My first post is pretty much the description of the repro process. I actually have no time to dig into the source code to look into it to find out exactly where the bug is.
In a nutshell, the combination of Spring 2.5.5/6 and Hibernate 3.2.6ga with the above generic DAO class would stop responding and hang upon a database query after about a number of queries/updates calls. The number of queries varies between systems. On my Windows XP, it took merely 7 getById()'s, whereas on my Ubuntu 2.6.18.5, it took 15 mixed save and find operations. But same outcome at the end, the application will hang until a hard interrupt to end it.
I repeated the same test for Spring 2.5.1, keeping Hibernate constant, and the problem was NOT present.
Sounds like a Session management issue to me, but I haven't had the chance to do trace. Let me know if you need anymore info from my end.
Originally Posted by maximosHi,
Sorry for the delayed response. My first post is pretty much the description of the repro process.
You said that you found a spring bug, i.e. identified the wrong place at the spring code.
About the test-case - just checked it with spring 2.5.6 and hibernate 3.2.6 - everything works fine. The test was to persist entitiy and remove it then in a loop.
I assume that you have incorrect spring/database configuration from the above. If you really find a spring bug it's nice to check spring code and say exactly what the problem is.
Imo, this can't work. You are using HibernateDaoSupport.getSession(), without ever returning them using releaseSession() (as described in the javadocs).
a) use HibernateDaoSupport.getHibernateTemplate() to cleanly create/destroy sessions
b) use getSession()/releaseSession() in a finally block
c) forget about HibernateDaoSupport, define transactions and use sessionFactory.getCurrentSession()
Hi Dejanp,
Thanks for the pointers! You gave me some good one there. However, I would like to understand the differences among the three.
1) getHibernateTemplate() - creates/destroys sessions automatically? In your opinion, is that efficient?
2) quot;define transactions and use sessionFactory.getCurrentSession()quot; - Currently I'm using AOP tx-advice to handle transaction demarcations. See below:
lt;bean id=quot;txManagerquot; class=quot;org..orm.hibernate3.Hibernat eTransactionManagerquot;gt; lt;property name=quot;sessionFactoryquot; ref=quot;sessionFactoryquot;/gt; lt;/beangt; lt;tx:advice id=quot;txAdvicequot; transaction-manager=quot;txManagerquot;gt; lt;tx:attributesgt; lt;tx:method name=quot;find*quot; read-only=quot;truequot; propagation=quot;REQUIREDquot; /gt; lt;tx:method name=quot;get*quot; read-only=quot;truequot; propagation=quot;REQUIREDquot;/gt; lt;tx:method name=quot;save*quot; propagation=quot;REQUIREDquot;/gt; lt;tx:method name=quot;delete*quot; propagation=quot;REQUIREDquot;/gt; lt;tx:method name=quot;remove*quot; propagation=quot;REQUIREDquot;/gt; lt;/tx:attributesgt; lt;/tx:advicegt; lt;aop:configgt; lt;aopointcut id=quot;daoOperationquot; expression=quot;execution(* netgravity.admanager.server.dao.IGenericDao.*(..)) quot;/gt; lt;aop:advisor advice-ref=quot;txAdvicequot; pointcut-ref=quot;daoOperationquot;/gt; lt;/aop:configgt;
I tried to go with the getCurrentSession() way with a simpler GenericDAO without HibernateDaoSupport. But it gave me the following error:
No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one here
Now, I know that this is a result of not having a transaction opened prior to make db calls. But as you can see, my transaction manager has been configured to kick in automatically via a pointcut. So why am i still getting this exception? Is the transaction manager not kicking in?
Please give me some advice on using the getCurrentSession() option with Spring configured transaction handling via AOP.
Thanks.
Hi,
Futher testing shows the following strange finding:
I have successfully gotten the above-mentioned setup running.
The setup is below:
=================== Hibernate Generic DAO ====================
public class AbstractHibernateDao14 implements IGenericDao {
private SessionFactory sessionFactory; private Class persistentClass;
public AbstractHibernateDao14() {} public AbstractHibernateDao14(Class persistentClass) { this.persistentClass = persistentClass; } public void setSessionFactory(SessionFactory sessionFactory) { this.sessionFactory = sessionFactory; }
public AbstractHibernateDao14(Class persistentClass, SessionFactory sessionFactory) {
this.persistentClass = persistentClass;
this.sessionFactory = sessionFactory; }
protected Session getCurrentSession() { return this.sessionFactory.getCurrentSession(); } /* (non-Javadoc) * @see netgravity.admanager.server.dao.IGenericDao#findCo unt() */ public int findCount() {
Criteria criteria = getCurrentSession().createCriteria(persistentClass );
criteria.setProjection( Projections.rowCount() );
return ((Integer)criteria.list().get(0)).intValue(); }
/* (non-Javadoc) * @see netgravity.admanager.server.dao.IGenericDao#delete (java.lang.Object) */ public void delete(Object entity) { if ( entity == null ) { throw new IllegalArgumentException( quot;Entity cannot be nullquot; ); } Session s = getCurrentSession(); s.delete(entity); s.flush(); s.evict( entity ); } ...
}
======================== appContext.xml ===================
lt;bean id=quot;txManagerquot; class=quot;org..orm.hibernate3.Hibernat eTransactionManagerquot;gt; lt;property name=quot;sessionFactoryquot; ref=quot;sessionFactoryquot;/gt; lt;/beangt; lt;tx:advice id=quot;txAdvicequot; transaction-manager=quot;txManagerquot;gt; lt;tx:attributesgt; lt;tx:method name=quot;find*quot; read-only=quot;truequot; propagation=quot;REQUIREDquot; /gt; lt;tx:method name=quot;get*quot; read-only=quot;truequot; propagation=quot;REQUIREDquot;/gt; lt;tx:method name=quot;save*quot; propagation=quot;REQUIREDquot;/gt; lt;tx:method name=quot;delete*quot; propagation=quot;REQUIREDquot;/gt; lt;tx:method name=quot;remove*quot; propagation=quot;REQUIREDquot;/gt; lt;tx:method name=quot;*quot;/gt; lt;/tx:attributesgt; lt;/tx:advicegt; lt;!-- lt;aop:configgt; lt;aopointcut id=quot;genericDaoOperationquot; expression=quot;execution(* netgravity.admanager.server.dao.IGenericDao.*(..)) quot;/gt; lt;aop:advisor advice-ref=quot;txAdvicequot; pointcut-ref=quot;genericDaoOperationquot;/gt; lt;/aop:configgt; --gt; lt;aop:configgt; lt;aopointcut id=quot;allDaoOperationquot; expression=quot;execution(* netgravity.admanager.server.dao.*.*(..))quot;/gt; lt;aop:advisor advice-ref=quot;txAdvicequot; pointcut-ref=quot;allDaoOperationquot;/gt; lt;/aop:configgt;
As you can see above, I commented out the lt;aopointcut id=quot;genericDaoOperationquot;gt; section. That was the strange problem that I found. When I used it instead of the quot;allDaoOperationquot; aop:config, it did not work for methods that are invoked by another method in a DAO subclass. E.g.:
================ DAO subclass =========================
public class SumDao extends AbstractHibernateDao14 implements ISumDao {
public SumDao(SessionFactory sessionFactory) { super(Sum.class, sessionFactory); } public List getByDateRange(Date startDate, Date endDate) { String start = DateUtil.getDatePart(startDate); String end = DateUtil.getDatePart(endDate); Criterion dateIntervalRestriction = null; if (start.equals(end)) { dateIntervalRestriction = Restrictions.eq(quot;eventDatequot;, java.sql.Date.valueOf(start)); } else { dateIntervalRestriction = Restrictions.between(quot;eventDatequot;, startDate, endDate); } Criterion mobileRestriction = Restrictions.eq(quot;mobilequot;, new Boolean(true)); return findByCriteria(new Criterion[]{dateIntervalRestriction, mobileRestriction}); }
}
As you can see, getByDateRange calls the findByCriteria method in the class that it extends, AbstractHibernateDao14. findByCriteria, by definition, should be proxied and transaction-managed automagically by spring AOP. But it returns an error: quot;No Hibernate Session bound to thread, and configuration does not allow creation of non-transactional one herequot;.
If I were to call the findByCriteria method directly from the SumDao instance, I would have no problem. Strange, isn't it? So that is why I had to use the other lt;aop:configgt; (id=quot;allDaoOperationquot;) to make everything work. Well, I guess I don't really have a good conclusion to this, except either I might have a defective configuration (once again), or perhaps that the Spring-managed transaction doesn't propagate through the hierarchy of methods?
Feedback anyone?
Spring AOP is, per default, interface-proxy based, meaning that methods that are not a part of an interface are not proxied and thus will not be made transactional. If you only define pointcuts for you generic dao interface only those methods will be proxied.
No matter how you define your transactions, they get bound to the current thread, so there is no way that they do not propagate.
Personally, I find @Transactional style easier to read and to handle then pointcut expressions.
Originally Posted by dejanpSpring AOP is, per default, interface-proxy based, meaning that methods that are not a part of an interface are not proxied and thus will not be made transactional. If you only define pointcuts for you generic dao interface only those methods will be proxied.
No matter how you define your transactions, they get bound to the current thread, so there is no way that they do not propagate.
Personally, I find @Transactional style easier to read and to handle then pointcut expressions.
Thanks for the quick response!
quot;If you only define pointcuts for you generic dao interface only those methods will be proxied.quot;
The empirical tests above seem to show otherwise. Take a look at the IGenericDao interface that I left out above:
public interface IGenericDao {
public abstract int findCount();
public abstract void delete(Object entity);
public abstract List findAll();
public abstract Object findById(Long id);
public abstract Object findById(Long id, boolean lock);
public abstract Object save(Object entity);
public abstract List findByName(String name); public abstract List findByCriteria(Criterion[] criterions);
}
As you can see, the findByCriteria method is supposed to be transactional for the reason you mentioned. But this is not happening when findByCriteria is invoked in another method, which is defined in another interface that extends the IGenericDao interface e.g. ISumDao. So, it seems like the interface that is used to invoke the method, whether it extends IGenericDao or not, needs to be within the definition of the AOP pointcut. That is why everything worked when I used
lt;aop: configgt;
lt;aop: pointcut id=quot;allDaoOperationquot;
expression=quot;execution(* netgravity.admanager.server.dao.*.*(..))quot;/gt;
lt;aop: advisor advice-ref=quot;txAdvicequot; pointcut-ref=quot;allDaoOperationquot;/gt;
lt;/aop: configgt;
because ISumDao is in the package netgravity.admanager.server.dao.*.
However, this would not be good because it looks like in order for me to use a transactional method, my entire stack of methods needs to be marked transactional?! It sure doesn't make any sense at this point.
As for using @Transactional ...
It's a great idea. And I'd always chosen that path when I'm on a project that uses java 1.5+. But this is legacy stuff that runs on java 1.4 so ... yeah ..
If you can repro this problem/feature, i'd appreciate it. |
|