问题:
     系统采用Spring MVC 2.5 + Spring 2.5 + Hibernate 3.2架构,其中数据源连接池采用的是Apache commons DBCP。问题是这样的,系统运行一段时间后(大致每隔8小时),访问系统会出现如下错误,再次访问恢复正常。

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.hibernate.exception.GenericJDBCException: Cannot release connection
at org.springframework.web.servlet.FrameworkServlet.proce***equest(FrameworkServlet.java:
583 )
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:
501 )
at javax.servlet.http.HttpServlet.service(HttpServlet.java:
617 )
at javax.servlet.http.HttpServlet.service(HttpServlet.java:
717 )
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:
290 )
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:
206 )

Caused by: org.hibernate.exception.GenericJDBCException: Cannot release 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.closeConnection(ConnectionManager.java:
449 )
at org.hibernate.jdbc.ConnectionManager.cleanup(ConnectionManager.java:
379 )
at org.hibernate.jdbc.ConnectionManager.manualDisconnect(ConnectionManager.java:
333 )
at org.hibernate.impl.SessionImpl.disconnect(SessionImpl.java:
375 )
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCleanupAfterCompletion(HibernateTransactionManager.java:
744 )
at org.springframework.transaction.support.AbstractPlatformTransactionManager.cleanupAfterCompletion(AbstractPlatformTransactionManager.java:
989 )
at org.springframework.transaction.support.AbstractPlatformTransactionManager.proce***ollback(AbstractPlatformTransactionManager.java:
855 )
at org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager.java:
800 )
at org.springframework.transaction.interceptor.TransactionAspectSupport.completeTransactionAfterThrowing(TransactionAspectSupport.java:
339 )
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:
110 )
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:
171 )
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:
89 )
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:
171 )
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:
635)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:
39 )
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
25 )
at java.lang.reflect.Method.invoke(Method.java:
585 )
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.doInvokeMethod(HandlerMethodInvoker.java:
421 )
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:
136 )
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:
326 )
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter.handle(AnnotationMethodHandlerAdapter.java:
313 )
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:
875 )
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:
807 )
at org.springframework.web.servlet.FrameworkServlet.proce***equest(FrameworkServlet.java:
571 )
36 more
Caused by: java.sql.SQLException: Already closed.
at org.apache.commons.dbcp.PoolableConnection.close(PoolableConnection.java:
77 )
at org.apache.commons.dbcp.PoolingDataSource$PoolGuardConnectionWrapper.close(PoolingDataSource.java:
180 )
at org.springframework.orm.hibernate3.LocalDataSourceConnectionProvider.closeConnection(LocalDataSourceConnectionProvider.java:
96 )
at org.hibernate.jdbc.ConnectionManager.closeConnection(ConnectionManager.java:
445 )
62 more



解决:
     造成Cannot release connection的原因有很多,要具体问题具体分析。从异常分析,造成这个异常 org.hibernate.exception.GenericJDBCException: Cannot release connection 归根结底是Caused by: java.sql.SQLException: Already closed. 即连接已关闭。所以解决的办法就要从DBCP的参数配置入手,见下面的参数配置properties文件。

#### :: Apache DBCP :: ####
jdbc.driverClassName
= oracle.jdbc.driver.OracleDriver
jdbc.url
=jdbc:oracle:thin:@10.165.153.9:1521 :PRDC
jdbc.username= guser
jdbc.password
=guser
#初始化连接
jdbc.initialSize
=0
#连接池的最大活动个数
jdbc.maxActive
=20
#没有人用连接的时候,最大闲置的连接个数。
jdbc.maxIdle
=100
#没有人用连接的时候,最小闲置的连接个数。
jdbc.minIdle
=0
#超时等待时间以毫秒为单位
jdbc.maxWait
=10000
#是否自动回收超时连接
jdbc.removeAbandoned
=true
#设置被遗弃的连接的超时的时间(以秒数为单位),即当一个连接被遗弃的时间超过设置的时间,则它会自动转换成可利用的连接。默认的超时时间是300秒。
jdbc.removeAbandonedTimeout
=60
#是否在自动回收超时连接的时候打印连接的超时错误
jdbc.logAbandoned
= true
#给出一条简单的sql语句进行验证
jdbc.validationQuery=select 1 from dual
#在取出连接时进行有效验证
jdbc.testOnBorrow=true

其中标红的两个参数的作用是对池化连接进行验证,This will ensure that DBCP only hands out good connections to Hibernate. 故加上这两个参数后,这个异常就不会再出现了。在Spring中指定数据源如下:

<!-- 數據源定義 使用Apache DBCP連接池 -->
    
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        
<property name="driverClassName" value="${jdbc.driverClassName}" />
        
<property name="url" value="${jdbc.url}" />
        
<property name="username" value="${jdbc.username}" />
        
<property name="password" value="${jdbc.password}" />
        
<property name="maxActive" value="${jdbc.maxActive}" />
        
<property name="maxIdle" value="${jdbc.maxIdle}" />
        
<property name="maxWait" value="${jdbc.maxWait}" />
        
<property name="removeAbandoned" value="${jdbc.removeAbandoned}" />
        
<property name="removeAbandonedTimeout" value="${jdbc.removeAbandonedTimeout}" />
        
<property name="logAbandoned" value="${jdbc.logAbandoned}" />
        
<property name="validationQuery" value="${jdbc.validationQuery}" />
        
<property name="testOnBorrow" value="${jdbc.testOnBorrow}" />
    
</bean>

     问题解决了。

    另附

    DBCP参数详细说明(部分参考了 http://justin-ray.javaeye.com/blog/413591 ):

     在配置时,大部分参数一目了然,不再赘述,有不清楚的话,可以参考下面的详细说明。主要难以理解的参数主要有:removeAbandoned 、logAbandoned、removeAbandonedTimeout、maxWait这四个参数,设置了rmoveAbandoned=true 那么在getNumActive()快要到getMaxActive()的时候,系统会进行无效的Connection的回收,回收的 Connection为removeAbandonedTimeout(默认300秒)中设置的秒数后没有使用的Connection,激活回收机制大致 为getNumActive()=getMaxActive()-3。logAbandoned=true的话,将会在回收事件后,在log中打印出回收 Connection的错误信息,包括在哪个地方用了Connection却忘记关闭了,在调试的时候很有用。在这里个人建议maxWait的时间不要设 得太长,maxWait如果设置太长那么客户端会等待很久才激发回收事件。

 源自:http://hi.baidu.com/iloverobot/blog/item/22148e032dac63e008fa9391.html