myBatis連接MySQL報異常:No operations allowed after connection closed.Connection was implicitly closed
異常信息
mybatis錯誤如下:
HTTP Status 500 - Request processing failed; nested exception is org.springframework.transaction.CannotCreateTransactionException:Could not open JDBC Connection for transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.type Exception report message Request processing failed; nested exception is org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed. description The server encountered an internal error that prevented it from fulfilling this request. exception org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:973)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
root cause org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.
org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:243)
org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373)
org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:420)
org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:257)
org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:95)
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
com.sun.proxy.$Proxy24.news(Unknown Source)
com.cn.article.controller.ArticleInfoController.news2(ArticleInfoController.java:691)
sun.reflect.GeneratedMethodAccessor171.invoke(Unknown Source)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:601)
org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:749)
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:690)
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:945)
org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
hibernate錯誤如下: org.hibernate.exception.JDBCConnectionException:
could not execute query at
org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java: 74 ) at
org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java: 43 ) ....... Caused
by: com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException: No operations allowed after connection closed.Connection was implicitly closed due to underlying exception/error: **
BEGIN NESTED EXCEPTION ** com.mysql.jdbc.CommunicationsException MESSAGE:
Communications link failure due to underlying exception: **
BEGIN NESTED EXCEPTION ** java.net.SocketException MESSAGE:
Broken pipe STACKTRACE: java.net.SocketException:
Broken pipe at
java.net.SocketOutputStream.socketWrite0(Native Method) ...... **
END NESTED EXCEPTION ** |
原因分析
查看了Mysql的文檔,以及Connector/J的文檔以及在線說明發現,出現這種異常的原因是:
Mysql服務器默認的“wait_timeout”是8小時,也就是說一個connection空閒超過8個小時,Mysql將自動斷開該connection。這就是問題的所在,在C3P0 pools中的connections如果空閒超過8小時,Mysql將其斷開,而C3P0並不知道該connection已經失效,如果這時有Client請求connection,C3P0將該失效的Connection提供給Client,將會造成上面的異常。
解決方案
解決的方法有3種:
- 增加 wait_timeout 的時間。
- 減少 Connection pools 中 connection 的 lifetime。
- 測試 Connection pools 中 connection 的有效性。
- 自己最終解決:將原來的DBCP連接池改爲C3p0連接池,並增c3p0對於maxIdleTime時間的控制,一定要小於8小時。
當然最好的辦法是同時綜合使用上述方法,下面就 DBCP、C3P0 和 simple jdbc dataSource 分別做一說明,假設 wait_timeout 爲
默認的8小時
DBCP 增加以下配置信息:
validationQuery = "select 1"
testWhileIdle = "true"
//some positive integer
timeBetweenEvictionRunsMillis = 3600000
//set to something smaller than 'wait_timeout'
minEvictableIdleTimeMillis = 18000000
//if you don't mind a hit for every getConnection(), set to "true"
testOnBorrow = "true"
C3P0 增加以下配置信息:
//獲取connnection時測試是否有效
testConnectionOnCheckin = true
//自動測試的table名稱
automaticTestTable=C3P0TestTable
//set to something much less than wait_timeout, prevents connections from going stale
idleConnectionTestPeriod = 18000
//set to something slightly less than wait_timeout, preventing 'stale' connections from being handed out
maxIdleTime = 25000
//if you can take the performance 'hit', set to "true"
testConnectionOnCheckout = true
c3p0配置(推薦)
:
使用第三方數據庫連接池: 現在第三方數據庫連接池使用較多的爲c3p0,proxool等,在性能上c3p0稍好一些,原因c3p0數據庫連接池,
底層有一個定時查看數據庫連接是否有效的參數。而且Hibernate的api中也推薦使用第三方數據庫連接池,
因爲Hibernate本身的數據庫連接池過於簡單、本身存在bug。c3p0參數配置如下:
<!-- lang: xml -->
<!-- 數據源 -->
<bean id="dataSourceTarget" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
<property name="user" value="root" />
<property name="password" value="root" />
<property name="jdbcUrl" value="jdbc:mysql://192.168.61.208:3306/sinoomv1_0_0" />
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<!-- 系統初始化連接數 -->
<property name="initialPoolSize" value="10" />
<!-- 最大連接數 -->
<property name="maxPoolSize" value="30" />
<!-- 最小連接數 -->
<property name="minPoolSize" value="10" />
<!--最大空閒時間,600秒(10分鐘)內未使用則連接被丟棄。若爲0則永不丟棄。Default: 0 -->
<property name="maxIdleTime" value="600" />
<!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default: 3 -->
<property name="acquireIncrement" value="3" />
<!--每60秒檢查所有連接池中的空閒連接。Default: 0 -->
<property name="idleConnectionTestPeriod" value="60" />
<!-- 每次從pool內checkout連接時測試有效性(同步操作)
程序每次數據庫調用都連接有效性,若無效關閉此連接並剔除出pool,
從pool內取其他連接,慎用,會造成至少多一倍的數據庫調用。Default:false -->
<property name="testConnectionOnCheckout" value="false" />
<!--定義在從數據庫獲取新連接失敗後重復嘗試的次數。Default: 30 -->
<property name="acquireRetryAttempts" value="30"/>
<!--兩次連接中間隔時間,單位毫秒。Default: 1000 -->
<property name="acquireRetryDelay" value="1000"/>
</bean>
查看mysql數據庫連接池情況,命令如下:
<!-- lang: sql -->
Mysql> show processlist;
ps:最近在在停起程序的時候我發現了一個問題,就算是我停止了服務,但是程序和mysql數據庫之間的連接數依然沒有斷開,
後來經查資料和同事請教,我發現在上邊的c3p0配置存在一個小bug。因爲我將c3p0交給spring進行管理,
但是在上邊注入的bean中沒有加上destroy-method="close"這個屬性。 也就是說Spring是不能正常銷燬連接的,由此出現了我說的問題。
寫在這裏以供大家警示,真是馬虎啊,竟然忘記了銷燬,實在不應該。
<!-- lang: xml -->
<!-- 數據源 -->
<bean id="dataSourceTarget" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
... ...
</bean>
4.自己實際解決方案:
4.1Spring整合c3p0 配置文件Demo如下:
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:jee="http://www.springframework.org/schema/jee"
- xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
- http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">
- <bean id="dataSource"
- class="com.mchange.v2.c3p0.ComboPooledDataSource"
- destroy-method="close">
- <property name="driverClass">
- <value>com.mysql.jdbc.Driver</value>
- </property>
- <property name="jdbcUrl">
- <value>jdbc:mysql://192.168.3.110:3306/DBName?useUnicode=true&characterEncoding=GBK</value>
- </property>
- <property name="user">
- <value>root</value>
- </property>
- <property name="password">
- <value>root</value>
- </property>
-
- <!--連接池中保留的最小連接數。-->
- <property name="minPoolSize">
- <value>5</value>
- </property>
-
- <!--連接池中保留的最大連接數。Default: 15 -->
- <property name="maxPoolSize">
- <value>30</value>
- </property>
-
- <!--初始化時獲取的連接數,取值應在minPoolSize與maxPoolSize之間。Default: 3 -->
- <property name="initialPoolSize">
- <value>10</value>
- </property>
-
- <!--最大空閒時間,60秒內未使用則連接被丟棄。若爲0則永不丟棄。Default: 0 -->
- <property name="maxIdleTime">
- <value>60</value>
- </property>
-
- <!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default: 3 -->
- <property name="acquireIncrement">
- <value>5</value>
- </property>
-
- <!--JDBC的標準參數,用以控制數據源內加載的PreparedStatements數量。但由於預緩存的statements
- 屬於單個connection而不是整個連接池。所以設置這個參數需要考慮到多方面的因素。
- 如果maxStatements與maxStatementsPerConnection均爲0,則緩存被關閉。Default: 0-->
- <property name="maxStatements">
- <value>0</value>
- </property>
-
- <!--每60秒檢查所有連接池中的空閒連接。Default: 0 -->
- <property name="idleConnectionTestPeriod">
- <value>60</value>
- </property>
-
- <!--定義在從數據庫獲取新連接失敗後重復嘗試的次數。Default: 30 -->
- <property name="acquireRetryAttempts">
- <value>30</value>
- </property>
-
- <!--獲取連接失敗將會引起所有等待連接池來獲取連接的線程拋出異常。但是數據源仍有效
- 保留,並在下次調用getConnection()的時候繼續嘗試獲取連接。如果設爲true,那麼在嘗試
- 獲取連接失敗後該數據源將申明已斷開並永久關閉。Default: false-->
- <property name="breakAfterAcquireFailure">
- <value>true</value>
- </property>
-
- <!--因性能消耗大請只在需要的時候使用它。如果設爲true那麼在每個connection提交的
- 時候都將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable
- 等方法來提升連接測試的性能。Default: false -->
- <property name="testConnectionOnCheckout">
- <value>false</value>
- </property>
- </bean>
- </beans>
4.2Maven的pom.xml文件中引入c3p0依賴:<!-- 數據庫連接池包-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5-pre8</version>
</dependency>
注意:可以配置最大空閒時間小一些如下60秒
- <!--最大空閒時間,60秒內未使用則連接被丟棄。若爲0則永不丟棄。Default: 0 -->
- <property name="maxIdleTime">
- <value>60</value>
- </property>
4.3 在項目日誌文件中配置debug模式,其中DEBUG模式下可能會報java.io.FileNotFoundException錯誤信息(說c3p0找不到c3p0.properties;mchange-log.properties;mchange-commons.properties等文件),這是因爲在DEBUG模式下c3p0會打印出具體的警告信息(這不是報錯,這是警告信息,不影響運行),其中c3p0官方推薦INFO級別日誌打印級別。在eclipse控制檯中便可以查看c3p0數據庫連接池中連接的獲取(默認最少有5個空閒,name一分鐘之內有五個connection被廢除)和廢除情況,這樣就可以保持Spring維護的數據庫連接池中的連接都是最新的。輸出如下:
[com.mchange.v2.c3p0.impl.NewPooledConnection] - com.mchange.v2.c3p0.impl.NewPooledConnection@369d34 closed by a client.
java.lang.Exception: DEBUG -- CLOSE BY CLIENT STACK TRACE
at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:659)
at com.mchange.v2.c3p0.impl.NewPooledConnection.closeMaybeCheckedOut(NewPooledConnection.java:255)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.destroyResource(C3P0PooledConnectionPool.java:621)
at com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask.run(BasicResourcePool.java:1024)
at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:696)
[com.mchange.v2.resourcepool.BasicResourcePool] - incremented pending_acquires: 2
[com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool] - Preparing to destroy PooledConnection: com.mchange.v2.c3p0.impl.NewPooledConnection@1908897
[com.mchange.v2.resourcepool.BasicResourcePool] - Starting acquisition series. Incremented pending_acquires [2], attempts_remaining: 30
[com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool] - Successfully destroyed PooledConnection: com.mchange.v2.c3p0.impl.NewPooledConnection@369d34
[com.mchange.v2.c3p0.impl.NewPooledConnection] - com.mchange.v2.c3p0.impl.NewPooledConnection@3b21e3 closed by a client.
java.lang.Exception: DEBUG -- CLOSE BY CLIENT STACK TRACE
at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:659)
at com.mchange.v2.c3p0.impl.NewPooledConnection.closeMaybeCheckedOut(NewPooledConnection.java:255)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.destroyResource(C3P0PooledConnectionPool.java:621)
at com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask.run(BasicResourcePool.java:1024)
at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:696)
[com.mchange.v2.async.ThreadPoolAsynchronousRunner] - com.mchange.v2.async.ThreadPoolAsynchronousRunner@1641371: Adding task to queue -- com.mchange.v2.resourcepool.BasicResourcePool$ScatteredAcquireTask@c6a83d
[com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool] - Successfully destroyed PooledConnection: com.mchange.v2.c3p0.impl.NewPooledConnection@3b21e3
[com.mchange.v2.resourcepool.BasicResourcePool] - Successfully destroyed resource: com.mchange.v2.c3p0.impl.NewPooledConnection@369d34
[com.mchange.v2.resourcepool.BasicResourcePool] - Successfully destroyed resource: com.mchange.v2.c3p0.impl.NewPooledConnection@3b21e3
[com.mchange.v2.resourcepool.BasicResourcePool] - incremented pending_acquires: 3
[com.mchange.v2.c3p0.impl.NewPooledConnection] - com.mchange.v2.c3p0.impl.NewPooledConnection@1908897 closed by a client.
java.lang.Exception: DEBUG -- CLOSE BY CLIENT STACK TRACE
at com.mchange.v2.c3p0.impl.NewPooledConnection.close(NewPooledConnection.java:659)
at com.mchange.v2.c3p0.impl.NewPooledConnection.closeMaybeCheckedOut(NewPooledConnection.java:255)
at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.destroyResource(C3P0PooledConnectionPool.java:621)
at com.mchange.v2.resourcepool.BasicResourcePool$1DestroyResourceTask.run(BasicResourcePool.java:1024)
at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:696)
simple jdbc dataSource 增加以下配置信息:
Pool.PingQuery = select 1
Pool.PingEnabled = true
Pool.PingConnectionsOlderThan = 0
//對於空閒的連接一個小時檢查一次
Pool.PingConnectionsNotUsedFor = 3600000
其他方案(不推薦)
對於 MySQL5 之前的版本,如 Mysql4.x,只需要修改連接池配置中的 URL,添加一個參數:autoReconnect=true(如jdbc:mysql://hostaddress:3306/schemaname?autoReconnect=true),如果是 MySQL5 及以後的版本,則需要修改 my.cnf(或者my.ini) 文件,在 [mysqld] 後面添加上:
wait_timeout
= n interactive-timeout
= n |
其中 n 爲服務器關閉交互式連接前等待活動的秒數。可是就部署而言每次修改 my.ini 比較麻煩,而且 n 等於多少纔是合適的值呢? 所以並不推薦這個解決辦法。)
-------------------------相關-------------------------
1. 即使在創建Mysql時url中加入了autoReconnect=true參數,一但這個連接兩次訪問數據庫的時間超出了服務器端wait_timeout的時間限制,還是會CommunicationsException: The last packet successfully received from the
server was xxx milliseconds ago.
2. 服務器端的參數可以用
show global variables like 'wait_timeout';
set global wait_timeout=10;
來進行設置,但是wait_timeout值不應該設的太高.
3. 較好的策略是對處於idle狀態的connection定時發送一個sql,來刷新服務器上的時間戳.這可以使用c3p0r的連接池.http://bzhang.iteye.com/blog/321832
4. 對於tomcat的server.xml中使用的連接池,http://tomcat.apache.org/tomcat-6.0-doc/jndi-datasource-examples-howto.html,http://commons.apache.org/dbcp/configuration.html使用DBCP的連接池可以採用
<Resource name="jdbc/test" auth="Container"
type="javax.sql.DataSource" driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost:3306/test?characterEncoding=utf-8"
username="root" password="test" maxActive="500" maxIdle="10"
maxWait="-1" timeBetweenEvictionRunsMillis="10000" minEvictableIdleTimeMillis="10000" />
4.1 設置validationQuery,這樣每次borrow(默認爲開啓)時會通過這個sql校驗連接的有效性,但是增加了時間.
4.2 設置timeBetweenEvictionRunsMillis="10000" minEvictableIdleTimeMillis="10000" 依賴evictor thread線程來把超時的連接關閉.
4.3 設置testWhileIdle="true" timeBetweenEvictionRunsMillis="10000" validationQuery="select 1" 使得定時去用query檢測處於idle狀態的連接,也就刷新了服務器端的時間.
5.每次提交的最大packet大小
show global variables like 'max_allowed_packet';
set global max_allowed_packet=1024*1024;
6. SQLyog 中連接參數的設置
6.1 在SQLyog中的設置 set autocommit=0,這樣當前連接的自動提交爲false,可以控制事務了.
6.2 begin; 事務開始
6.3 select * from test where 1=1 and id =1 for update;這樣就把選到的記錄行鎖上了,再開一個SQLyog,也執行以上相同的操作,就會一直wait在那裏.
6.4 commit; 提交
6.5 rollback; 回滾
6.6 set autocommit=0;後應該加上
set transaction isolation level read committed;
這樣其它客戶端就能看到commit的數據,
疑問:
如果不設置set transaction isolation level read committed;如果兩個客戶端都select 相同的數據,一個客戶端修改然後提交,另一個客戶端不提交當前事務的前提下,去執行select ,取不到另一客戶端提交的數據,不知道SQLyog默認的事務級別是什麼樣的.
7. SQLyog中查看mysql的狀態,show global variables like '%lock%'; 是個好方法.對於事務鎖(例如for update)報Lock wait timeout exceeded ,只能通過修改my.ini文件innodb_lock_wait_timeout = 100;才能生效.
8. linux下修改用戶密碼 mysqladmin -u root password "new_pass"
三大連接池說明:
1. Apache-DBCP
BasicDataSource 相關的參數說明
dataSource: 要連接的 datasource (通常我們不會定義在 server.xml)
defaultAutoCommit: 對於事務是否 autoCommit, 默認值爲 true
defaultReadOnly: 對於數據庫是否只能讀取, 默認值爲 false
driverClassName:連接數據庫所用的 JDBC Driver Class,
maxActive: 可以從對象池中取出的對象最大個數,爲0則表示沒有限制,默認爲8
maxIdle: 最大等待連接中的數量,設 0 爲沒有限制 (對象池中對象最大個數)
minIdle:對象池中對象最小個數
maxWait: 最大等待秒數, 單位爲 ms, 超過時間會丟出錯誤信息
password: 登陸數據庫所用的密碼
url: 連接數據庫的 URL
username: 登陸數據庫所用的帳號
validationQuery: 驗證連接是否成功, SQL SELECT 指令至少要返回一行
removeAbandoned: 是否自我中斷, 默認是 false
removeAbandonedTimeout: 幾秒後會自我中斷, removeAbandoned 必須爲 true
logAbandoned: 是否記錄中斷事件, 默認爲 false
minEvictableIdleTimeMillis:大於0 ,進行連接空閒時間判斷,或爲0,對空閒的連接不進行驗證;默認30分鐘
timeBetweenEvictionRunsMillis:失效檢查線程運行時間間隔,如果小於等於0,不會啓動檢查線程,默認-1
testOnBorrow:取得對象時是否進行驗證,檢查對象是否有效,默認爲false
testOnReturn:返回對象時是否進行驗證,檢查對象是否有效,默認爲false
testWhileIdle:空閒時是否進行驗證,檢查對象是否有效,默認爲false
在使用DBCP的時候,如果使用默認值,則數據庫連接因爲某種原因斷掉後,再從連接池中取得連接又不進行驗證,這時取得的連接實際上就會是無效的數據庫連接。因此爲了防止獲得的數據庫連接失效,在使用的時候最好保證:
username: 登陸數據庫所用的帳號
validationQuery:SELECT COUNT(*) FROM DUAL
testOnBorrow、testOnReturn、testWhileIdle:最好都設爲true
minEvictableIdleTimeMillis:大於0 ,進行連接空閒時間判斷,或爲0,對空閒的連接不進行驗證
timeBetweenEvictionRunsMillis:失效檢查線程運行時間間隔,如果小於等於0,不會啓動檢查線程
PS:在構造GenericObjectPool [BasicDataSource在其createDataSource () 方法中也會使用GenericObjectPool] 時,會生成一個內嵌類Evictor,實現自Runnable接口。如果timeBetweenEvictionRunsMillis大於0,每過timeBetweenEvictionRunsMillis毫秒Evictor會調用evict()方法,檢查對象的閒置時間是否大於 minEvictableIdleTimeMillis毫秒(_minEvictableIdleTimeMillis小於等於0時則忽略,默認爲30分鐘),是則銷燬此對象,否則就激活並校驗對象,然後調用ensureMinIdle方法檢查確保池中對象個數不小於_minIdle。在調用returnObject方法把對象放回對象池,首先檢查該對象是否有效,然後調用PoolableObjectFactory
的passivateObject方法使對象處於非活動狀態。再檢查對象池中對象個數是否小於maxIdle,是則可以把此對象放回對象池,否則銷燬此對象
上述特性的可設置性已在代碼中驗證,具體性能是否能實現有待實際驗證
2. C3P0
C3P0的官方example中使用的數據源爲ComboPooledDataSource,網上一篇文章詳細介紹了C3P0連接池配置中各項含義[這些配置項的含義在下載解壓c3p0的壓縮包之後目錄的doc\index.html中的Configuration部分也有詳細的介紹,這裏偷下懶:P],現摘錄如下:
<c3p0-config>
<default-config>
<!--當連接池中的連接耗盡的時候c3p0一次同時獲取的連接數。Default: 3 -->
<property name="acquireIncrement">3</property>
<!--定義在從數據庫獲取新連接失敗後重復嘗試的次數。Default: 30 -->
<property name="acquireRetryAttempts">30</property>
<!--兩次連接中間隔時間,單位毫秒。Default: 1000 -->
<property name="acquireRetryDelay">1000</property>
<!--連接關閉時默認將所有未提交的操作回滾。Default: false -->
<property name="autoCommitOnClose">false</property>
<!--c3p0將建一張名爲Test的空表,並使用其自帶的查詢語句進行測試。如果定義了這個參數那麼
屬性preferredTestQuery將被忽略。你不能在這張Test表上進行任何操作,它將只供c3p0測試
使用。Default: null-->
<property name="automaticTestTable">Test</property>
<!--獲取連接失敗將會引起所有等待連接池來獲取連接的線程拋出異常。但是數據源仍有效
保留,並在下次調用getConnection()的時候繼續嘗試獲取連接。如果設爲true,那麼在嘗試
獲取連接失敗後該數據源將申明已斷開並永久關閉。Default: false-->
<property name="breakAfterAcquireFailure">false</property>
<!--當連接池用完時客戶端調用getConnection()後等待獲取新連接的時間,超時後將拋出
SQLException,如設爲0則無限期等待。單位毫秒。Default: 0 -->
<property name="checkoutTimeout">100</property>
<!--通過實現ConnectionTester或QueryConnectionTester的類來測試連接。類名需制定全路徑。
Default: com.mchange.v2.c3p0.impl.DefaultConnectionTester-->
<property name="connectionTesterClassName"></property>
<!--指定c3p0 libraries的路徑,如果(通常都是這樣)在本地即可獲得那麼無需設置,默認null即可
Default: null-->
<property name="factoryClassLocation">null</property>
<!--Strongly disrecommended. Setting this to true may lead to subtle and bizarre bugs.
(文檔原文)作者強烈建議不使用的一個屬性-->
<property name="forceIgnoreUnresolvedTransactions">false</property>
<!--每60秒檢查所有連接池中的空閒連接。Default: 0 -->
<property name="idleConnectionTestPeriod">60</property>
<!--初始化時獲取三個連接,取值應在minPoolSize與maxPoolSize之間。Default: 3 -->
<property name="initialPoolSize">3</property>
<!--最大空閒時間,60秒內未使用則連接被丟棄。若爲0則永不丟棄。Default: 0 -->
<property name="maxIdleTime">60</property>
<!--連接池中保留的最大連接數。Default: 15 -->
<property name="maxPoolSize">15</property>
<!--JDBC的標準參數,用以控制數據源內加載的PreparedStatements數量。但由於預緩存的statements
屬於單個connection而不是整個連接池。所以設置這個參數需要考慮到多方面的因素。
如果maxStatements與maxStatementsPerConnection均爲0,則緩存被關閉。Default: 0-->
<property name="maxStatements">100</property>
<!--maxStatementsPerConnection定義了連接池內單個連接所擁有的最大緩存statements數。Default: 0 -->
<property name="maxStatementsPerConnection"></property>
<!--c3p0是異步操作的,緩慢的JDBC操作通過幫助進程完成。擴展這些操作可以有效的提升性能
通過多線程實現多個操作同時被執行。Default: 3-->
<property name="numHelperThreads">3</property>
<!--當用戶調用getConnection()時使root用戶成爲去獲取連接的用戶。主要用於連接池連接非c3p0
的數據源時。Default: null-->
<property name="overrideDefaultUser">root</property>
<!--與overrideDefaultUser參數對應使用的一個參數。Default: null-->
<property name="overrideDefaultPassword">password</property>
<!--密碼。Default: null-->
<property name="password"></property>
<!--定義所有連接測試都執行的測試語句。在使用連接測試的情況下這個一顯著提高測試速度。注意:
測試的表必須在初始數據源的時候就存在。Default: null-->
<property name="preferredTestQuery">select id from test where id=1</property>
<!--用戶修改系統配置參數執行前最多等待300秒。Default: 300 -->
<property name="propertyCycle">300</property>
<!--因性能消耗大請只在需要的時候使用它。如果設爲true那麼在每個connection提交的
時候都將校驗其有效性。建議使用idleConnectionTestPeriod或automaticTestTable
等方法來提升連接測試的性能。Default: false -->
<property name="testConnectionOnCheckout">false</property>
<!--如果設爲true那麼在取得連接的同時將校驗連接的有效性。Default: false -->
<property name="testConnectionOnCheckin">true</property>
<!--用戶名。Default: null-->
<property name="user">root</property>
<!--早期的c3p0版本對JDBC接口採用動態反射代理。在早期版本用途廣泛的情況下這個參數
允許用戶恢復到動態反射代理以解決不穩定的故障。最新的非反射代理更快並且已經開始
廣泛的被使用,所以這個參數未必有用。現在原先的動態反射與新的非反射代理同時受到
支持,但今後可能的版本可能不支持動態反射代理。Default: false-->
<property name="usesTraditionalReflectiveProxies">false</property>
<property name="automaticTestTable">con_test</property>
<property name="checkoutTimeout">30000</property>
<property name="idleConnectionTestPeriod">30</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">25</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">0</property>
<user-overrides user="swaldman">
</user-overrides>
</default-config>
<named-config name="dumbTestConfig">
<property name="maxStatements">200</property>
<user-overrides user="poop">
<property name="maxStatements">300</property>
</user-overrides>
</named-config>
</c3p0-config>
上述特性的可設置性已在代碼中驗證,具體性能是否能實現有待實際驗證
從配置項的內容來看,C3P0和DBCP都有比較詳細的有關連接檢測保證的配置,我們可以看到C3P0可以控制數據源內加載的PreparedStatements數量,並且可以設置幫助線程的數量來提升JDBC操作的速度,這些是DBCP未提供的;另外從網絡上的評價來看,DBCP出現Bug的頻率要大於C3P0,不過這一點有待於我們自己實際的檢測。
3. Proxool
Proxool的使用和dbcp以及c3p0稍有不同,我們需要並且只需要在使用基本的java.sql.DriverManager之前加載org.logicalcobwebs.proxool.ProxoolDriver驅動類,並且按照proxool定義的url格式 ["proxool." + alias + ":" + driverClass + ":" + driverUrl ,其中alias是爲連接池自定義的別名] 來獲得connection;具體的可以參看proxool doc下的UserGuide,或本文所附的示例代碼。下面對連接池的特性配置作詳細說明
[這個是自己翻譯的,不一定準確,有問題時請參看doc下的Properties ~]。
fatal-sql-exception
以逗號隔開的異常列表,當設置了此項之後,每當出現SQLException時都將與列表中異常項作比較,如果匹配則認爲出現fatal異常,這將導致connection被丟棄,並且不論出現任何情況該異常將被重拋一次以通知用戶發生的情況。默認值爲null
fatal-sql-exception-wrapper-class
如果配置了fatal-sql-exception,則默認的操作是丟 棄引起SQLException的原因而只是拋出原始異常。使用fatal-sql-exception-wrapper-class這個特性可以將 SQLException包裝到繼承SQLException或RunTimeException的任何異常類裏。Proxool提供了兩個類供使用 FatalSQLException和FatalRunTimeException;使用這兩個類的話就將該選項設置爲 'org.logicalcobwebs.proxool.FatalSQLException'或者
'org.logicalcobwebs.proxool.FatalRuntimeException'。默認值爲null
house-keeping-sleep-time
proxool自動偵察各個連接狀態的時間間隔(毫秒),偵察到空閒的連接就馬上回收,超時的銷燬,默認值爲30秒
house-keeping-test-sql
如果偵察線程發現閒置連接,則會使用這個SQL語句來對這些連接進行檢查;這項設置的語句應該能夠被很快的執行,例如查詢當前時間 [info.setProperty("proxool.house-keeping-test-sql", "select CURRENT_DATE");] 。如果不設置則該選項被忽略
injectable-connection-interface、injectable-statement-interface、injectable-prepared-statement-interface、injectable-callable-statement-interface
jmx
如果此項設爲true,則連接池將被以名稱"Proxool:type=Pool, name=<alias>"註冊爲JMS Server的MBean。默認值爲false
jmx-agent-id
當且僅當jmx選項設爲true時使用,爲以逗號分隔的連接持註冊到的JMS代理名稱列表;如果不設置則所有註冊的JMX Server都將被使用
maximum-active-time
線程最大存活時間,超過此時間的線程將被守護線程kill掉,默認值爲5分鐘
maximum-connection-count
到數據庫的最大連接數,超過了這個連接,再有請求時,就排在隊列中等候,最大的等待請求數由simultaneous-build-throttle決定;默認值爲15
maximum-connection-lifetime
連接最大存活時間,毫秒爲單位,默認值爲4小時
minimum-connection-count
不管是否被使用都保持開放的最小連接數,默認值爲5
overload-without-refusal-lifetime
用來判斷連接池狀態,如果在此選項設置時間內(毫秒爲單位)拒絕了連接,則認爲過負載。默認值爲60秒
prototype-count
最少保持的空閒連接數,注意與minimum-connection-count區分。默認值爲0
simultaneous-build-throttle
最大的等待請求數,默認值爲10
test-before-use
如果設爲true則connection在使用前將以house-keeping-test-sql設置的語句測試,如果測試不通過則該connection被丟棄並會重新分配一個connection。默認爲false
test-after-use
如果設爲true則connection在關閉(放回連接池)前將以house-keeping-test-sql設置的語句測試,如果測試不通過connection將被丟棄。默認值爲false
與其它連接池特性的設置方法不同,Proxool不提供相應的set方法,所有特性都要以諸如info.setProperty("proxool.jmx", "false");方式設定
上述特性的可設置性已在代碼中驗證,具體性能是否能實現有待實際驗證
引自原網址:http://fengbin2005.iteye.com/blog/1906488