?? connectionpool.java
字號:
package com.pansonlu.common.database;
import java.sql.*;
import java.util.*;
/**
* <p>
* 數據庫連接池。
* 池里每個連接的最大活躍期是24小時,過期則自動重新連接數據庫(以保證性能穩定)。
* 如果連接池中的某個連接因為某個原因被數據庫斷開連接,則系統把該連接從連接池移出。
* 客戶端請求連接時,如果當前沒有空閑連接,而連接總數小于連接池最大容量,則新建
* 連接加入到連接池
* </p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2008</p>
* <p>Company: </p>
* @author hmy
* @version 2004-06-11
*/
import com.pansonlu.common.util.*;
public class ConnectionPool {
public static final long SECOND = 1000; //秒
public static final long MINUTE = SECOND*60; //分
public static final long HOUR = MINUTE*60; //小時
//一個數據庫連接的有效期。超過有效其的連接應該被釋放,連接池重新建立一個連
// 接來代替它,默認為24(單位:小時)
//一個連接建立后,使用一定時間后,應該釋放,重新建立一個連接代替它,否則
//一個連接時間長了會出現性能和可靠性不穩定
private final static int CONNECTION_ALIVE_TIME = 24;
private int poolSize; //連接池大小
private long timeout; //連接時的time out設置(單位:分鐘)。必須大于0,默認為30
private String driver = null; //驅動程序類
private String dbURL = null; //數據庫服務器URL地址
private String username = null; //訪問數據庫的用戶名
private String password = null; //訪問數據庫的密碼
/**
* 用來測試的數據庫連接的有效性的數據表的表名。要求數據
* 庫里存在這個表,而且這個表屬于這個用戶;至于這個表有
* 些什么字段,有多少記錄,都不重要(記錄條數最好不要超過10條,可以沒有記錄)。
* 這個屬性的值可以為null,如果為null,則對某些數據庫,這個連接池可能失去自動重連
* 的功能(即在數據庫服務器重新啟動或網絡連接短暫斷開后,
* 該連接池無法自動重新連接數據庫)。
*/
private String testTableName = null;
/**
* 物理連接池(包含所有物理數據庫連接,不論是否被用戶占用)。
* 本集合的大小即代表與數據庫的總連接數。
* 集合中每個元素都是一個java.sql.Connection對象。
*/
private Vector allConns;
// /**
// * 所有連接(包裝)的池子
// */
// private Vector allWrappers;
/**
* 空閑連接(包裝)池(只包含未被用戶占用的連接)。
* 本集合的大小即代表當前空閑數據庫連接(未被用戶占用)的數目。
* 里面的每個元素都是一個ConnectionWrapper對象。用戶請求連接時,
* 系統從本集合中取得ConnectionWrapper對象,同時把該對象從本集合中移除。
*/
private Vector freeWrappers;
private static ConnectionPool pool = null;
/**
*
* @param driver 驅動程序類名
* @param dbURL 數據庫服務器的URL地址
* @param username 數據庫用戶名
* @param password 數據庫用戶的密碼
* @param test_table_name 參見屬性testTableName的說明
* @param poolSize 連接池大小
* @param timeout timeout設置,單位:分
*/
private ConnectionPool(
String driver,
String dbURL,
String username,
String password,
int poolSize,
int timeout) throws Exception{
this.driver = driver.trim();
this.dbURL = dbURL.trim();
this.username = username.trim();
this.password = password.trim();
this.timeout = timeout;
this.poolSize = poolSize;
// if(testTableName!=null && testTableName.trim().length()==0){
// testTableName = null;
// }
allConns = new Vector(poolSize);
freeWrappers = new Vector(poolSize);
try{
Class.forName(driver);
for(int i=0;i<poolSize;i++){//根據池的大小,建立若干個連接,放入池中
addConnection();
}
}catch(Exception e){
for(int i=0;i<allConns.size();i++){
try{
//關閉所有已經取得的物理連接
((Connection)allConns.get(i)).close();
}catch(Exception eee){}
}//end for i
allConns.clear();
freeWrappers.clear();
throw e;
}//end try
//啟動檢查線程,定期對過期連接進行釋放、重新連接的工作
(new ReCreateThread()).start();
(new ReConnectThread()).start();
}//end of method
private void addConnection() {
try{
Connection conn = DriverManager.getConnection(dbURL, username, password); //建立連接
ConnectionWrapper wrapper = new ConnectionWrapper(conn, freeWrappers);
allConns.addElement(conn); //把物理連接放入數據庫連接池中
//把物理連接放入包裝器后,放入空閑連接池中
freeWrappers.addElement(wrapper);
}catch (SQLException e) {
System.out.println("ConnectionPool,addConnection(),新建數據庫鏈接失敗,原因:"+e);
}
}
/**
* 初始化數據庫連接池
* @param driver
* @param url
* @param user
* @param password
* @param testTableName 用來測試的數據庫連接的有效性的數據表的表名。要求數據
* 庫里存在這個表,而且這個表屬于這個用戶;至于這個表有
* 些什么字段,有多少記錄,都不重要(記錄條數最好不要超過10條,
* 可以沒有記錄)。這個參數可以為null,
* 如果為null,則對某些數據庫,這個連接池可能失去自動重連
* 的功能(即在數據庫服務器重新啟動或網絡連接短暫斷開后,
* 該連接池無法自動重新連接數據庫)。
* 連接池將用這個表
* @param size 連接池大小
* @param timeout 連接超時設置(單位:秒)
*/
public static void initConnectionPool(String driver,String url,String user,
String password, int size,int timeout) throws SQLException {
if(pool!=null){ //連接池已經建立
return;
}
try{
pool = new ConnectionPool(driver,url,user,password,size,timeout);
}catch(Exception e){
throw new SQLException(e.toString());
}
}
/**
* 從空閑連接池取得連接。
* @return 取得的連接
* @throws 遇到SQL異常則拋出,連接超時也拋出異常。
*/
public static Connection getConnection() throws SQLException {
if(pool==null){
throw new SQLException("異常,ConnectionPool,getConnection(),連接池尚未初始化");
}
ConnectionWrapper wrapper;
Connection conn = null;
long startTime = System.currentTimeMillis(); //發起請求的時間
while(conn==null && System.currentTimeMillis()-startTime<SECOND*pool.timeout){
synchronized(pool.freeWrappers){
if (pool.freeWrappers.size() > 0) {
//從空閑連接池取出連接。
//從連接池取出連接時從隊列的最前面取,把連接放回連接池時放到隊列的最后
wrapper = (ConnectionWrapper) pool.freeWrappers.remove(0);
return wrapper;
}else{ //如果沒有空閑連接
if(pool.allConns.size()<pool.poolSize){ //有效物理連接不夠
pool.addConnection(); //增加連接
}
}
}//end synchronized
try{
Thread.sleep(SECOND); //等待1秒
}catch(Exception e){
//
}
}//end while
throw new SQLException("ConnectionPool,getConnection,請求連接超時");
}//end of method
/**
* 定期檢查物理連接池里的連接,如果某個連接被數據庫斷開了,則重新建立連接替
* 代它,同時要對空閑連接(包裝)池做處理
*/
private class ReConnectThread extends Thread{
public void run(){
while(true){
try {
Thread.sleep(10 * SECOND); //間隔10秒
}
catch (Exception e) {
//
}
deleteInvalidConns(); //刪除物理連接池里的無效連接
deleteInvalidWrappers(); //刪除無效的連接包裝器
}//end while
}
private void deleteInvalidWrappers(){
if(allConns.size()==poolSize){
return;
}
synchronized(freeWrappers){
ConnectionWrapper wrapper = null;
Connection conn = null;
for(int i=freeWrappers.size()-1;i>=0;i--){
wrapper = (ConnectionWrapper)freeWrappers.get(i);
conn = wrapper.getPhysicalConnection();
if(allConns.contains(conn)==false){ //說明該包裝器已經無效
freeWrappers.remove(wrapper); //刪除無效包裝器
}//end if
}//end for i
}//end synchronized
}//end method
private void deleteInvalidConns(){
synchronized(allConns){
Connection conn = null;
for(int i=allConns.size()-1;i>=0;i--){
conn = (Connection)allConns.get(i);
if(testConnection(conn)==false){
try{
conn.close(); //關閉物理連接
}catch(Exception e){}
allConns.remove(conn); //刪除無效物理連接
}
}//end for i
}//end synchronized
// System.out.println("連接池大小:"+allConns.size());
}
/**
* 檢查一個數據庫連接是否有效
* @param conn
* @return 有效則返回true
*/
private boolean testConnection(Connection conn){
Statement stmt = null;
ResultSet rs = null;
boolean flag = false;
try{
stmt = conn.createStatement(); //測試是否有效
//if(testTableName!=null){
//String sql = "select getdate()"; //測試SQL,根據不同的數據庫更改
//rs = stmt.executeQuery(sql);
//}
flag = true; //沒出異常,說明連接有效
// System.out.println("------testConnection(),連接有效");
}catch(SQLException e){
// System.out.println("------testConnection(), table name: "+testTableName);
// System.out.println("------testConnection(), 異常:");
// e.printStackTrace();
flag = false; //認定連接無效
}finally{
if(rs!=null)
try{
rs.close();
}catch(Exception ee){}
if(stmt!=null)
try{
stmt.close();
}catch(Exception ee){}
}
return flag;
}//end of method testConnection
}
/**
* 負責釋放過期的數據庫連接、重新建立連接替代它的線程
* <p>Title: </p>
* <p>Description: </p>
* <p>Copyright: Copyright (c) 2008</p>
* <p>Company: </p>
* @author hmy
* @version 1.0
*/
private class ReCreateThread extends Thread{
public void run(){
while(true){
try{
Thread.sleep(HOUR); //等待一個小時
}catch(Exception e){}
recreateConns(); //檢查連接池中的連接,對于已經過期的,釋放,用新的連接代替
}//end for while
}
//在空閑連接池中檢查已經過期的連接,將它釋放,新建一個連接代替它
private void recreateConns(){
//Debug.outInfo("ConnectionPool,ReCreateThread,巡視,檢查過期連接");
//用來存儲需要被釋放、重新連接的ConnectionWrapper對象
//已經超過有效期的、當前正處于空閑狀態的ConnectionWrapper對象需要被釋放、重連
Vector timeouts = new Vector();
//從空閑連接池取出所有過期連接,放入timeouts集合中
long currTime = System.currentTimeMillis(); //當前時間
ConnectionWrapper wrapper = null;
synchronized(freeWrappers){
//當前長度。本次操作期間,被用戶返回連接池的連接放到了隊列的最后,本次不操作它
int currSize = freeWrappers.size();
//遍歷空閑連接池中的連接(因為有刪除操作,所以從后面開始),把該釋放的對象取出
for (int i = currSize-1; i>=0; i--) {
wrapper = (ConnectionWrapper) freeWrappers.get(i);
//如果連接已經過了有效期:
if (currTime - wrapper.getCreatedTime() > HOUR * CONNECTION_ALIVE_TIME) {//已經超時
timeouts.addElement(freeWrappers.remove(i));
}//end if
}//end for i
}//end synchronized
//把需要重新連接的對象釋放,并新建連接替代它
Connection timeoutConn; //過期的物理連接
Connection newConn; //新的物理連接
for(int i=0;i<timeouts.size();i++){
wrapper = (ConnectionWrapper)timeouts.get(i);
timeoutConn = wrapper.getPhysicalConnection(); //取得包裝器所包裝的物理連接
try{
allConns.remove(timeoutConn); //把過期物理連接從物理連接池移除
timeoutConn.close(); //釋放過期物理連接
newConn = DriverManager.getConnection(dbURL,username,password);//新建一個物理連接
allConns.addElement(newConn); //放入物理連接池
//包裝后放入空閑連接池:
freeWrappers.addElement(new ConnectionWrapper(newConn,freeWrappers));
}catch(Exception e){
//
}
}//end for i
}//end of method
}//end class ReCreateThread
//測試
public static void main(String[] args){
Connection conn = null;
int m = 1;
while(true){
try{
//取得連接,持有1秒再釋放
conn = ConnectionPool.getConnection();
Debug.outInfo("得到連接: "+conn);
Debug.outInfo("conn.getAutoCommit(): "+conn.getAutoCommit());
Thread.sleep(1000); //等待1秒
conn.close();
Debug.outInfo("conn.getAutoCommit(): "+conn.getAutoCommit());
Debug.outInfo("------------------------------");
}catch(Exception e){
e.printStackTrace();
//Debug.outInfo("main(), e: "+e);
}
}//end while
}//end of method
}//end file
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -