探讨Spring与Hibernate的整合所遇到的管理Session的问题

来源:互联网 发布:历史书 知乎 编辑:程序博客网 时间:2024/06/10 03:54

http://topic.csdn.net/u/20080704/14/bb2751bf-cd0a-4025-9e66-18f9031b1361.html

 

最近使用Struts1.3+Spring2.0+Hibernate3.2做了一个项目,最后测试时发现一个致命的错误,我的项目运行一段时间后,数据库直接down掉了。hibernate报了以下异常:

Java code
2008-06-30 19:00:02,250 WARN [com.mchange.v2.resourcepool.BasicResourcePool] - com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask@b27de5 -- Acquisition Attempt Failed!!! Clearing pending acquires. While trying to acquire a needed new resource, we failed to succeed more than the maximum number of allowed acquisition attempts (30).2008-06-30 19:00:02,312 WARN [org.hibernate.util.JDBCExceptionReporter] - SQL Error: 0, SQLState: null2008-06-30 19:00:02,312 ERROR [org.hibernate.util.JDBCExceptionReporter] - Connections could not be acquired from the underlying database!2008-06-30 19:00:02,328 WARN [org.hibernate.util.JDBCExceptionReporter] - SQL Error: 0, SQLState: null2008-06-30 19:00:02,328 ERROR [org.hibernate.util.JDBCExceptionReporter] - Connections could not be acquired from the underlying database!org.hibernate.exception.GenericJDBCException: Cannot open connection at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:103) at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:91) at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43) at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:29) at org.hibernate.jdbc.ConnectionManager.openConnection(ConnectionManager.java:420) at org.hibernate.jdbc.ConnectionManager.getConnection(ConnectionManager.java:144) at org.hibernate.jdbc.AbstractBatcher.prepareQueryStatement(AbstractBatcher.java:105) at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1561) at org.hibernate.loader.Loader.doQuery(Loader.java:661) at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:224) at org.hibernate.loader.Loader.doList(Loader.java:2145) at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2029) at org.hibernate.loader.Loader.list(Loader.java:2024) at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:375) at org.hibernate.hql.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:308) at org.hibernate.engine.query.HQLQueryPlan.performList(HQLQueryPlan.java:153) at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1106) at org.hibernate.impl.QueryImpl.list(QueryImpl.java:79) at cn.gcy.cyzd.impl.ItemDAOImpl.queryAll(ItemDAOImpl.java:15) at cn.gcy.cyzd.struts.action.IndexAction.list(IndexAction.java:54) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.apache.struts.actions.DispatchAction.dispatchMethod(DispatchAction.java:269) at org.apache.struts.actions.DispatchAction.execute(DispatchAction.java:170) at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:425) at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:228) at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1913) at org.apache.struts.action.ActionServlet.doGet(ActionServlet.java:449) at javax.servlet.http.HttpServlet.service(HttpServlet.java:690) at javax.servlet.http.HttpServlet.service(HttpServlet.java:803) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at cn.gcy.cyzd.filter.EncodingFilter.doFilter(EncodingFilter.java:22) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:198) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:286) at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:856) at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:565) at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1509) at java.lang.Thread.run(Unknown Source)Caused by: java.sql.SQLException: Connections could not be acquired from the underlying database! at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:104) at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:236) at com.mchange.v2.c3p0.PoolBackedDataSource.getConnection(PoolBackedDataSource.java:94) at com.mchange.v2.c3p0.ComboPooledDataSource.getConnection(ComboPooledDataSource.java:521) at org.springframework.orm.hibernate3.LocalDataSourceConnectionProvider.getConnection(LocalDataSourceConnectionProvider.java:81) at org.hibernate.jdbc.ConnectionManager.openConnection(ConnectionManager.java:417) ... 46 moreCaused by: com.mchange.v2.resourcepool.CannotAcquireResourceException: A ResourcePool could not acquire a resource from its primary factory or source. at com.mchange.v2.resourcepool.BasicResourcePool.awaitAcquire(BasicResourcePool.java:970) at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:208) at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:232) ... 50 more2008-06-30 19:00:05,093 WARN [com.mchange.v2.resourcepool.BasicResourcePool] - com.mchange.v2.resourcepool.BasicResourcePool$AcquireTask@4c03d4 -- Acquisition Attempt Failed!!! Clearing pending acquires. While trying to acquire a needed new resource, we failed to succeed more than the maximum number of allowed acquisition attempts (30).


这个错误郁闷了我很久,通过大家的帮助还有网络的大量资源我终于找到了其中的原因。首先说说我的daoimpl的写法
在daoimpl中我使用
Java code
Query q = super.getSession().createQuery(hql);

语句可以正常执行,但是在多次使用该语句后数据库有很多连接仍然没有断开!这是导致异常的关键所在,spring不是管理了session吗?这是为什么呢?
通过继承HibernateDaoSupport我们有两个选择:
 
Java code
getSession().createQuery("from Users");getHibernateTemplate().find( "FROM Users);


网上找了找资料都是推荐用getHibernateTemplate,原因说的不是很清楚。

于是我做了如下测试:

分别循环调用getSession().createQuery("from Users");getHibernateTemplate().find( "FROM Users);
1000次
结果getSession()很快就包无法建立连接了。而getHibernateTemplate屁事没有可以跑完。

通过后台观察,使用getSession会在数据库中留下很多SQL*Net message from client的连接,终止测试后连接自动释放。
而getHibernateTemplate则从头到尾都使用一个连接。

难道是getSession()不会自动释放连接?

于是我又分别循环调用getSession().createQuery("from Users");getHibernateTemplate().find( "FROM Users);
5次
发现当前端程序一结束,getSession的5个连接立刻就释放了。结合前面1000次时终止测试后连接自动释放,可以说明getSession()是会自动释放连接的。

结论:
1、getSession()和getHibernateTemplate都可以自动释放连接(当然你的配置要正确),但是在一个线程内getSession会get很多个session(就是开很多个会话、连接),很可能导致数据库连接超过上限。所以推荐使用getHibernateTemplate。

2、如果有些语句无法用getHibernateTemplate实现,可以使用getHibernateTemplate.execute使用HibernateCallback回调接口。

 

另:可以设定HibernateTemplate的AllowCreate为True,并在finally中关闭Session。也可以将true作为参数传递到super.getSession(..)方法中取得Session。这样也可以,就是麻烦点。
参见:
http://springframework.org/docs/api/org/springframework/orm/hibernate3/HibernateTemplate.html
http://www.mxjava.com/blog/article.asp?id=246
参考资料:http://blog.sina.com.cn/s/blog_50e4caf70100a1nx.html

于是,如果我们一定要书写hql语句可以参考如下形式

Java code
public PageUtil getLog(final Long userid,final Integer page) { String counthql="select count(mod) from Blog mod where mod.userinfo.userid=?"; Integer count=(Integer)getHibernateTemplate().find(counthql, userid).get(0); final PageUtil pu=new PageUtil();// 分页包装类 pu.setCount(count); pu.setPage(page); pu.setMaxPagesbyCount(count); List li= getHibernateTemplate().executeFind(new HibernateCallback(){ public Object doInHibernate(Session session) throws HibernateException, SQLException { String hql="from Blog mod left join fetch mod.userinfo where mod.userinfo.userid=? order by mod.logid desc"; Query query=session.createQuery(hql); query.setParameter(0, userid); query.setMaxResults(pu.getMaxResults()); query.setFirstResult(pu.getFirstResult()); return query.list(); } }); pu.setResults(li); return pu ; }



只有这样做了以后才能确保数据库的连接能够尽早被释放,项目不至于崩溃。

 

____________________________________________________

 

解决办法1 this.releaseSession(session);
解决办法2 hibernate.connection.release_mode=auto
解决办法3 getSession(false);

不妨可以再测试一下

____________________________________________________

原创粉丝点击