數據庫連接池Java實現小結

   因爲工作需要要使用到連接池,所以拜讀了互聯網上衆多前輩的文章,學了不少經驗,這裏想做一個小結,加上自己的想法和在一起,希望能給大家一些幫助。 



  目的:



消除數據庫頻繁連接帶來的開銷和瓶頸。 

  解決方案:



不過多的限制用戶的使用,既不能太多的要求用戶按規定的方法得到和使用數據庫連 

儘量保持用戶的習慣 

  目前的很多方法都是要求用戶只能按規定方法使用連接,不能使用直接關閉數據連接的方法。解決辦法就是使用代理類,來中間解決。可以參考http://www-900.ibm.com/developerWorks/cn/java/l-connpoolproxy/index.shtml





能維護連接的正常狀態 





要求用戶按規定的方法得到和使用數據庫連 

儘量保持用戶的習慣 

  目前的很多方法都是要求用戶只能按規定方法使用連接,不能使用直接關閉數據連接的方法。解決辦法就是使用代理類,來中間解決。可以參考http://www-900.ibm.com/developerWorks/cn/java/l-connpoolproxy/index.shtml





能維護連接的正常狀態 

  因爲針對數據庫連接創建的資源,如果不能及時的釋放,就會影響下一次數據連接的使用。例如在sql 2k中,一個連接不同創建多條Statement否則操作時會有數據連接佔線的異常,所以必須在歸還連接以後釋放這些資源。 

//判斷是使用了createStatement語句

if (CREATESTATE.equals(method.getName()))

{

obj = method.invoke(conn, args);

statRef = (Statement)obj;//記錄語句

return obj;

}









  //判斷是否調用了close的方法,如果調用close方法則把連接置爲無用狀態

if(CLOSE.equals(method.getName()))

{

//設置不使用標誌

setIsFree(false);

//檢查是否有後續工作,清除該連接無用資源

if (statRef != null)

statRef.close();

if (prestatRef != null)

prestatRef.close();

return null;

}









正確保護類不被違例使用 

一個考慮就是不能讓用戶隨便使用代理類,而只能自己使用,一個就是用內部私有類,一個就是使用只有指定類才能調用的標誌。我的實現就是採用後者。



/**

* 創建連接的工廠,只能讓工廠調用

* @param factory 要調用工廠,並且一定被正確初始化

* @param param 連接參數

* @return 連接

*/

static public _Connection getConnection(ConnectionFactory factory, ConnectionParam param)

{

if (factory.isCreate())//判斷是否正確初始化的工廠

{

_Connection _conn = new _Connection(param);

return _conn;

}

else

return null;

}









提供良好的用戶接口,簡單實用 

  使用靜態方法創建工廠,然後來得到連接,使用完全和普通的Connection方法一樣,沒有限制。同時爲了方便,設置了連接參數類和工廠參數類。

ConnectionParam param = new ConnectionParam(driver,url,user,password);

ConnectionFactory cf = null;//new ConnectionFactory(param, new FactoryParam());

try{

cf = new ConnectionFactory(param,new FactoryParam());

Connection conn1 = cf.getFreeConnection();

Connection conn2 = cf.getFreeConnection();

Connection conn3 = cf.getFreeConnection();

Statement stmt = conn1.createStatement();

ResultSet rs = stmt.executeQuery("select * from requests");

if (rs.next())

{

System.out.println("conn1 y"); 

}

else

{

System.out.println("conn1 n"); 



stmt.close();

conn1.close(); 







爲了實現連接池的正常運作,使用了單態模 

  /**

* 使用指定的參數創建一個連接池

*/

public ConnectionFactory(ConnectionParam param, FactoryParam fparam)

throws SQLException 

{

//不允許參數爲空

if ((param == null)||(fparam == null))

throw new SQLException("ConnectionParam和FactoryParam不能爲空");

if (m_instance == null)

{

synchronized(ConnectionFactory.class){

if (m_instance == null)

{

//new instance

//參數定製

m_instance = new ConnectionFactory();

m_instance.connparam = param;

m_instance.MaxConnectionCount = fparam.getMaxConn();

m_instance.MinConnectionCount = fparam.getMinConn();

m_instance.ManageType = fparam.getType();

m_instance.isflag = true;

//初始化,創建MinConnectionCount個連接

System.out.println("connection factory 創建!");

try{

for (int i=0; i < m_instance.MinConnectionCount; i++)

{

_Connection _conn = _Connection.getConnection(m_instance, m_instance.connparam);

if (_conn == null) continue;

System.out.println("connection創建");

m_instance.FreeConnectionPool.add(_conn);//加入空閒連接池

m_instance.current_conn_count ++;

//標誌是否支持事務

m_instance.supportTransaction = _conn.isSupportTransaction(); 

}

}

catch(Exception e)

{

e.printStackTrace();

}

//根據策略判斷是否需要查詢

if (m_instance.ManageType != 0)

{

Thread t = new Thread(new FactoryMangeThread(m_instance));

t.start();



}

}

}

}









連接池的管理 

  對於連接池的管理,我是設想使用靜態管理和動態管理兩種策略,設置了最大限制,和恆定的連接數。使用了2個池,一個空閒池,一個使用池。靜態就是使用的時候發現空閒連接不夠再去檢查。動態就是使用了一個線程定時檢查。

//根據策略判斷是否需要查詢

if (m_instance.ManageType != 0)

{

Thread t = new Thread(new FactoryMangeThread(m_instance));

t.start();

}



//連接池調度線程

public class FactoryMangeThread implements Runnable {

ConnectionFactory cf = null;

long delay = 1000;

public FactoryMangeThread(ConnectionFactory obj)

{

cf = obj;

}

/* (non-Javadoc)

* @see java.lang.Runnable#run()

*/

public void run() {

while(true){

try{

Thread.sleep(delay);

}

catch(InterruptedException e){}

System.out.println("eeeee");

//判斷是否已經關閉了工廠,那就退出監聽

if (cf.isCreate())

cf.schedule();

else

System.exit(1);

}

}

}







最後給出完整的源代碼:



  _Connectio.java

package scut.ailab.connectionpool;



import java.lang.reflect.*;

import java.sql.*;



/**

* @author youyongming

* 定義數據庫連接的代理類

*/

public class _Connection implements InvocationHandler {

//定義連接

private Connection conn = null;

//定義監控連接創建的語句

private Statement statRef = null;

private PreparedStatement prestatRef = null;

//是否支持事務標誌

private boolean supportTransaction = false;

//數據庫的忙狀態

private boolean isFree = false;

//最後一次訪問時間

long lastAccessTime = 0;

//定義要接管的函數的名字

String CREATESTATE = "createStatement";

String CLOSE = "close";

String PREPARESTATEMENT = "prepareStatement";

String COMMIT = "commit";

String ROLLBACK = "rollback";



/**

* 構造函數,採用私有,防止被直接創建

* @param param 連接參數

*/

private _Connection(ConnectionParam param) {

//記錄日至



try{

//創建連接

Class.forName(param.getDriver()).newInstance();

conn = DriverManager.getConnection(param.getUrl(),param.getUser(), param.getPassword()); 

DatabaseMetaData dm = null;

dm = conn.getMetaData();

//判斷是否支持事務

supportTransaction = dm.supportsTransactions();

}

catch(Exception e)

{

e.printStackTrace();

}

}





/* (non-Javadoc)

* @see java.lang.reflect.InvocationHandler#invoke

 *(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])

*/

public Object invoke(Object proxy, Method method, Object[] args)

throws Throwable {

Object obj = null;

//判斷是否調用了close的方法,如果調用close方法則把連接置爲無用狀態

if(CLOSE.equals(method.getName()))

{

//設置不使用標誌

setIsFree(false);

//檢查是否有後續工作,清除該連接無用資源

if (statRef != null)

statRef.close();

if (prestatRef != null)

prestatRef.close();

return null;

}

//判斷是使用了createStatement語句

if (CREATESTATE.equals(method.getName()))

{

obj = method.invoke(conn, args);

statRef = (Statement)obj;//記錄語句

return obj;

}

//判斷是使用了prepareStatement語句

if (PREPARESTATEMENT.equals(method.getName()))

{

obj = method.invoke(conn, args);

prestatRef = (PreparedStatement)obj;

return obj;

}

//如果不支持事務,就不執行該事物的代碼

if ((COMMIT.equals(method.getName())||ROLLBACK.equals(method.getName()))

&& (!isSupportTransaction()))

return null; 

obj = method.invoke(conn, args); 

//設置最後一次訪問時間,以便及時清除超時的連接

lastAccessTime = System.currentTimeMillis();

return obj;

}



/**

* 創建連接的工廠,只能讓工廠調用

* @param factory 要調用工廠,並且一定被正確初始化

* @param param 連接參數

* @return 連接

*/

static public _Connection getConnection(ConnectionFactory factory, ConnectionParam param)

{

if (factory.isCreate())//判斷是否正確初始化的工廠

{

_Connection _conn = new _Connection(param);

return _conn;

}

else

return null;

}



public Connection getFreeConnection() {

//返回數據庫連接conn的接管類,以便截住close方法

Connection conn2 = (Connection)Proxy.newProxyInstance(

conn.getClass().getClassLoader(),

conn.getClass().getInterfaces(),this);

return conn2;

}



/**

* 該方法真正的關閉了數據庫的連接

* @throws SQLException

*/

void close() throws SQLException{

//由於類屬性conn是沒有被接管的連接,因此一旦調用close方法後就直接關閉連接

conn.close();

}



public void setIsFree(boolean value)

{

isFree = value;

}



public boolean isFree() {

return isFree;



/**

* 判斷是否支持事務

* @return boolean

*/

public boolean isSupportTransaction() {

return supportTransaction;



}









  ConnectionFactory.java 



  package scut.ailab.connectionpool;



/**

* @author youyongming

*

*/

import java.util.LinkedHashSet;

import java.sql.*;

import java.util.Iterator;



public class ConnectionFactory {

private static ConnectionFactory m_instance = null;

//在使用的連接池

private LinkedHashSet ConnectionPool = null;

//空閒連接池

private LinkedHashSet FreeConnectionPool = null;

//最大連接數

private int MaxConnectionCount = 4;

//最小連接數

private int MinConnectionCount = 2;

//當前連接數

private int current_conn_count = 0;

//連接參數

private ConnectionParam connparam = null;

//是否創建工廠的標誌

private boolean isflag = false;

//是否支持事務

private boolean supportTransaction = false;

//定義管理策略

private int ManageType = 0;



private ConnectionFactory() {

ConnectionPool = new LinkedHashSet();

FreeConnectionPool = new LinkedHashSet();

}



/**

* 使用指定的參數創建一個連接池

*/

public ConnectionFactory(ConnectionParam param, FactoryParam fparam)

throws SQLException 

{

//不允許參數爲空

if ((param == null)||(fparam == null))

throw new SQLException("ConnectionParam和FactoryParam不能爲空");

if (m_instance == null)

{

synchronized(ConnectionFactory.class){

if (m_instance == null)

{

//new instance

//參數定製

m_instance = new ConnectionFactory();

m_instance.connparam = param;

m_instance.MaxConnectionCount = fparam.getMaxConn();

m_instance.MinConnectionCount = fparam.getMinConn();

m_instance.ManageType = fparam.getType();

m_instance.isflag = true;

//初始化,創建MinConnectionCount個連接

System.out.println("connection factory 創建!");

try{

for (int i=0; i < m_instance.MinConnectionCount; i++)

{

_Connection _conn = _Connection.getConnection(m_instance, m_instance.connparam);

if (_conn == null) continue;

System.out.println("connection創建");

m_instance.FreeConnectionPool.add(_conn);//加入空閒連接池

m_instance.current_conn_count ++;

//標誌是否支持事務

m_instance.supportTransaction = _conn.isSupportTransaction(); 

}

}

catch(Exception e)

{

e.printStackTrace();

}

//根據策略判斷是否需要查詢

if (m_instance.ManageType != 0)

{

Thread t = new Thread(new FactoryMangeThread(m_instance));

t.start();



}

}

}

}



/**

* 標誌工廠是否已經創建

* @return boolean

*/ 

public boolean isCreate()

{

return m_instance.isflag;

}



/**

* 從連接池中取一個空閒的連接

* @return Connection

* @throws SQLException

*/

public synchronized Connection getFreeConnection() 

throws SQLException

{

Connection conn = null;

//獲取空閒連接

Iterator iter = m_instance.FreeConnectionPool.iterator();

while(iter.hasNext()){

_Connection _conn = (_Connection)iter.next();

//找到未用連接

if(!_conn.isFree()){

conn = _conn.getFreeConnection();

_conn.setIsFree(true);

//移出空閒區

m_instance.FreeConnectionPool.remove(_conn);

//加入連接池 

m_instance.ConnectionPool.add(_conn); 

break;

}

}

//檢查空閒池是否爲空

if (m_instance.FreeConnectionPool.isEmpty())

{

//再檢查是否能夠分配

if (m_instance.current_conn_count < m_instance.MaxConnectionCount)

{

//新建連接到空閒連接池

int newcount = 0 ;

//取得要建立的數目

if (m_instance.MaxConnectionCount - m_instance.current_conn_count >=

         m_instance.MinConnectionCount)

{

newcount = m_instance.MinConnectionCount;

}

else

{

newcount = m_instance.MaxConnectionCount - m_instance.current_conn_count;

}

//創建連接

for (int i=0;i <newcount; i++)

{

_Connection _conn = _Connection.getConnection(m_instance, m_instance.connparam);

m_instance.FreeConnectionPool.add(_conn);

m_instance.current_conn_count ++;

}

}

else

{//如果不能新建,檢查是否有已經歸還的連接

iter = m_instance.ConnectionPool.iterator();

while(iter.hasNext()){

_Connection _conn = (_Connection)iter.next();

if(!_conn.isFree()){

conn = _conn.getFreeConnection();

_conn.setIsFree(false);

m_instance.ConnectionPool.remove(_conn); 

m_instance.FreeConnectionPool.add(_conn); 

break;

}



}

}//if (FreeConnectionPool.isEmpty())

//再次檢查是否能分配連接

if(conn == null){

iter = m_instance.FreeConnectionPool.iterator();

while(iter.hasNext()){

_Connection _conn = (_Connection)iter.next();

if(!_conn.isFree()){

conn = _conn.getFreeConnection();

_conn.setIsFree(true);

m_instance.FreeConnectionPool.remove(_conn); 

m_instance.ConnectionPool.add(_conn); 

break;

}

}

if(conn == null)//如果不能則說明無連接可用

throw new SQLException("沒有可用的數據庫連接");

}

System.out.println("get connection");

return conn;

}



/**

* 關閉該連接池中的所有數據庫連接

* @throws SQLException

*/

public synchronized void close() throws SQLException

{

this.isflag = false;

SQLException excp = null;

//關閉空閒池

Iterator iter = m_instance.FreeConnectionPool.iterator();

while(iter.hasNext()){

try{

((_Connection)iter.next()).close();

System.out.println("close connection:free");

m_instance.current_conn_count --;

}catch(Exception e){

if(e instanceof SQLException)

excp = (SQLException)e;

}

}

//關閉在使用的連接池

iter = m_instance.ConnectionPool.iterator();

while(iter.hasNext()){

try{

((_Connection)iter.next()).close();

System.out.println("close connection:inused");

m_instance.current_conn_count --;

}catch(Exception e){

if(e instanceof SQLException)

excp = (SQLException)e;

}



if(excp != null)

throw excp;





/**

* 返回是否支持事務

* @return boolean

*/

public boolean isSupportTransaction() {

return m_instance.supportTransaction;



/**

* 連接池調度管理

*

*/

public void schedule()

{

Connection conn = null;

//再檢查是否能夠分配

Iterator iter = null;

//檢查是否有已經歸還的連接

{

iter = m_instance.ConnectionPool.iterator();

while(iter.hasNext()){

_Connection _conn = (_Connection)iter.next();

if(!_conn.isFree()){

conn = _conn.getFreeConnection();

_conn.setIsFree(false);

m_instance.ConnectionPool.remove(_conn); 

m_instance.FreeConnectionPool.add(_conn); 

break;

}



}

if (m_instance.current_conn_count < m_instance.MaxConnectionCount)

{

//新建連接到空閒連接池

int newcount = 0 ;

//取得要建立的數目

if (m_instance.MaxConnectionCount - m_instance.current_conn_count

    >=m_instance.MinConnectionCount)

{

newcount = m_instance.MinConnectionCount;

}

else

{

newcount = m_instance.MaxConnectionCount - m_instance.current_conn_count;

}

//創建連接

for (int i=0;i <newcount; i++)

{

_Connection _conn = _Connection.getConnection(m_instance, m_instance.connparam);

m_instance.FreeConnectionPool.add(_conn);

m_instance.current_conn_count ++;

}

}

}

}



  ConnectionParam.java 



  package scut.ailab.connectionpool;



import java.io.Serializable;



/**

* @author youyongming

* 實現數據庫連接的參數類

*/

public class ConnectionParam implements Serializable {

private String driver; //數據庫驅動程序

private String url; //數據連接的URL

private String user; //數據庫用戶名

private String password; //數據庫密碼



/**

* 唯一的構造函數,需要指定連接的四個必要參數

* @param driver 數據驅動

* @param url 數據庫連接url

* @param user 用戶名

* @param password 密碼

*/

public ConnectionParam(String driver,String url,String user,String password)

{

this.driver = driver;

this.url = url;

this.user = user;

this.password = password;

}



public String getDriver() {

return driver;

}



public String getPassword() {

return password;

}



public String getUrl() {

return url;

}



public String getUser() {

return user;

}



public void setDriver(String driver) {

this.driver = driver;

}



public void setPassword(String password) {

this.password = password;

}



public void setUrl(String url) {

this.url = url;

}



public void setUser(String user) {

this.user = user;

}



/**

* @see java.lang.Object#clone()

*/

public Object clone(){

ConnectionParam param = new ConnectionParam(driver,url,user,password);

return param;

}



/**

* @see java.lang.Object#equals(java.lang.Object)

*/

public boolean equals(Object obj) {

if(obj instanceof ConnectionParam){

ConnectionParam param = (ConnectionParam)obj;

return ((driver.compareToIgnoreCase(param.getDriver()) == 0)&&

(url.compareToIgnoreCase(param.getUrl()) == 0)&&

(user.compareToIgnoreCase(param.getUser()) == 0)&&

(password.compareToIgnoreCase(param.getPassword()) == 0));

}

return false;

}

}









  FactoryMangeThread.java 



  /*

* Created on 2003-5-13

*

* To change the template for this generated file go to

* Window>Preferences>Java>Code Generation>Code and Comments

*/

package scut.ailab.connectionpool;



/**

* @author youyongming

*

*/

//連接池調度線程

public class FactoryMangeThread implements Runnable {

ConnectionFactory cf = null;

long delay = 1000;

public FactoryMangeThread(ConnectionFactory obj)

{

cf = obj;

}

/* (non-Javadoc)

* @see java.lang.Runnable#run()

*/

public void run() {

while(true){

try{

Thread.sleep(delay);

}

catch(InterruptedException e){}

System.out.println("eeeee");

//判斷是否已經關閉了工廠,那就退出監聽

if (cf.isCreate())

cf.schedule();

else

System.exit(1);

}

}

}









  FactoryParam.java 



  /*

* Created on 2003-5-13

*

* To change the template for this generated file go to

* Window>Preferences>Java>Code Generation>Code and Comments

*/

package scut.ailab.connectionpool;



/**

* @author youyongming

*

*/

//連接池工廠參數

public class FactoryParam {

//最大連接數

private int MaxConnectionCount = 4;

//最小連接數

private int MinConnectionCount = 2; 

//回收策略

private int ManageType = 0;



public FactoryParam() {

}



/**

* 構造連接池工廠參數的對象

* @param max 最大連接數

* @param min 最小連接數

* @param type 管理策略

*/

public FactoryParam(int max, int min, int type)

{

this.ManageType = type;

this.MaxConnectionCount = max;

this.MinConnectionCount = min;

}



/**

* 設置最大的連接數

* @param value

*/

public void setMaxConn(int value)

{

this.MaxConnectionCount = value;

}

/**

* 獲取最大連接數

* @return

*/

public int getMaxConn()

{

return this.MaxConnectionCount;

}

/**

* 設置最小連接數

* @param value

*/

public void setMinConn(int value)

{

this.MinConnectionCount = value;

}

/**

* 獲取最小連接數

* @return

*/

public int getMinConn()

{

return this.MinConnectionCount;

}

public int getType()

{

return this.ManageType;

}

}







  testmypool.java 



  /*

* Created on 2003-5-13

*

* To change the template for this generated file go to

* Window>Preferences>Java>Code Generation>Code and Comments

*/

package scut.ailab.connectionpool;



/**

* @author youyongming

*

*/

import java.sql.*;



public class testmypool {



public void test1()

{

String user = "DevTeam";

String password = "DevTeam";

String driver = "sun.jdbc.odbc.JdbcOdbcDriver";

String url = "jdbc:odbc:gfqh2";

ConnectionParam param = new ConnectionParam(driver,url,user,password);

ConnectionFactory cf = null;//new ConnectionFactory(param, new FactoryParam());

try{

cf = new ConnectionFactory(param,new FactoryParam());

Connection conn1 = cf.getFreeConnection();

Connection conn2 = cf.getFreeConnection();

Connection conn3 = cf.getFreeConnection();

Statement stmt = conn1.createStatement();

ResultSet rs = stmt.executeQuery("select * from requests");

if (rs.next())

{

System.out.println("conn1 y"); 

}

else

{

System.out.println("conn1 n"); 



stmt.close();

conn1.close(); 

Connection conn4 = cf.getFreeConnection();

Connection conn5 = cf.getFreeConnection();

stmt = conn5.createStatement();

rs = stmt.executeQuery("select * from requests");

if (rs.next())

{

System.out.println("conn5 y"); 

}

else

{

System.out.println("conn5 n"); 



conn2.close();

conn3.close();

conn4.close();

conn5.close();

}

catch(Exception e)

{

e.printStackTrace();

}

finally{

try{

cf.close();

}

catch(Exception e)

{

e.printStackTrace();

}



}

public static void main(String[] args) {

String user = "DevTeam";

String password = "DevTeam";

String driver = "sun.jdbc.odbc.JdbcOdbcDriver";

String url = "jdbc:odbc:gfqh2";

ConnectionParam param = new ConnectionParam(driver,url,user,password);

ConnectionFactory cf = null;//new ConnectionFactory(param,new FactoryParam());

try{

cf = new ConnectionFactory(param,new FactoryParam());

ConnectionFactory cf1= new ConnectionFactory(param,new FactoryParam());

Connection conn1 = null;

long time = System.currentTimeMillis();

for (int i=0; i <10;i++)

{

conn1 = cf.getFreeConnection();

Statement stmt = conn1.createStatement();

ResultSet rs = stmt.executeQuery("select * from requests");

if (rs.next())

{

System.out.println("conn1 y"); 

}

else

{

System.out.println("conn1 n"); 



conn1.close(); 

}

System.out.println("pool:" + (System.currentTimeMillis()-time));

time = System.currentTimeMillis();

Class.forName(param.getDriver()).newInstance();

for (int i=0; i <10;i++)

{

conn1 = DriverManager.getConnection(param.getUrl(),

      param.getUser(), param.getPassword()); 

Statement stmt = conn1.createStatement();

ResultSet rs = stmt.executeQuery("select * from requests");

if (rs.next())

{

System.out.println("conn1 y"); 

}

else

{

System.out.println("conn1 n"); 



conn1.close(); 



System.out.println("no pool:" + (System.currentTimeMillis()-time));

}

catch(Exception e)

{

e.printStackTrace();

}

finally{

try{

cf.close();

}

catch(Exception e)

{

e.printStackTrace();

}

}

}

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章