问题:
系统采用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.processRequest(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.processRollback(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.processRequest(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
- jdbcjdbc.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>