1. 程式人生 > >java建立資料庫連線池程式碼

java建立資料庫連線池程式碼

       資料庫連線池(Connection pooling)是程式啟動時建立足夠的資料庫連線,並將這些連線組成一個連線池,由程式動態地對池中的連線進行申請,使用,釋放。

       大家可以想一下,如果專案中沒有資料庫連線池會是一個什麼樣的結果?每訪問一次資料庫都會新建一個數據庫連線,如果同事有成百上千的請求需要訪問資料庫那麼專案會慘不忍睹。

       資料庫連線池就是在專案啟動是就已經建立了一定數量的資料庫連線,有請求訪問資料庫時,不需要新建資料庫連線,直接在連線池中拿就可以,用完了記得放到池子裡面就行。這樣的話,連線池中的資料庫連線會不斷的重複利用,大大提高了對資料庫操作的效能。

資料庫連線池的執行機制有以下幾點:

1.程式啟動時建立資料庫連線池。

2.使用連線時,從連線池中拿可用的資料庫連線。

3.使用完,將資料庫連線放入資料庫中。

那麼要想手動程式碼實現資料庫連線池,需要哪些步驟了?下面會貼出原始碼以及簡單的註釋。

首先需要一個數據庫連線池的配置檔案jdbc.properties,內容如下:

#驅動名稱  
jdbcDriver=com.mysql.jdbc.Driver  
#連線URL  
jdbcUrl=jdbc:mysql://localhost:3306/lcma  
#資料庫賬戶名  
userName=root  
#資料庫密碼  
password=iflytek  
#初始化連線數  
initCount=10  
#步進數量  
stepSize=4  
#最大連線池個數  
poolMaxSize=150  
       既然是資料連線池,那麼池子裡面放的肯定是很多個數據庫連線,一個數據庫連線就是一個物件,建一個數據庫連線類 PooledConnection.java代表資料庫連線物件。類裡面有個isBusy屬性,用來表示該連線物件是否正在使用,若使用中用true表示,反之為false。程式碼如下:
既然是資料連線池,那麼池子裡面放的肯定是很多個數據庫連線,一個數據庫連線就是一個物件,建一個數據庫連線類 PooledConnection.java代表資料庫連線物件。類裡面有個isBusy屬性,用來表示該連線物件是否正在使用,若使用中用true表示,反之為false。程式碼如下:
package com.lcma.conn;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

/**
 * <p>Title      : 儲存連線池中物件屬性</p>
 * <p>Description: [子應用名]_[模組名]</p>
 * <p>Copyright  : Copyright (c) 2016</p>
 * <p>Company    : 科大訊飛</p>
 * @author       : lcma
 * @version      : 1.0
 */
public class PooledConnection {
	
	/**
	 * 連線管道物件
	 */
	private Connection conn;
	
	/**
	 * 連線狀態,true-繁忙,false-空閒
	 */
	private boolean isBusy = false;
	
	public PooledConnection(Connection conn, boolean isBusy){
		this.conn = conn;
		this.isBusy = isBusy;
	}
	
	public void close(){
		this.isBusy = false;
	}

	public Connection getConn() {
		return conn;
	}

	public void setConn(Connection conn) {
		this.conn = conn;
	}

	public boolean isBusy() {
		return isBusy;
	}

	public void setBusy(boolean isBusy) {
		this.isBusy = isBusy;
	}

	/**
	 * <p>Discription:建立查詢方法,用於測試使用</p>
	 * @param sql
	 * @return
	 * @author       : lcma
	 * @update       : 2016年12月5日下午11:19:18
	 */
	public ResultSet queryBySql(String sql){
		ResultSet rs = null;
		Statement sm = null;
		try {
			sm = conn.createStatement();
			rs = sm.executeQuery(sql);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return rs;
	}
	
	
}
建立連線池介面類,IMyPool.java
package com.lcma.pools;  
  
import com.lcma.conn.PooledConnection;  
  
/** 
 * <p>Description: 連線池介面</p> 
 * <p>Copyright  : Copyright (c) 2017</p> 
 * @author       : lcma 
 * @version      : 1.0 
 */  
public interface IMyPool {  
      
    /** 
     * <p>Discription:建立連線</p> 
     * @param count 
     * @author       : lcma 
     * @update       : 2016年12月4日下午1:11:46 
     */  
    public void createConnection(int count);  
      
    /** 
     * <p>Discription:獲取連線</p> 
     * @return 
     * @author       : lcma 
     * @update       : 2016年12月4日下午1:11:34 
     */  
    public PooledConnection getConnection();  
  
}
實現連線池介面類,MyPoolImpl.java

1.讀取配置檔案,建立連線池

2.獲取連線,判斷是否有空閒的連線並且是有效的連線

package com.lcma.pools.impl;  
  
import java.io.IOException;  
import java.io.InputStream;  
import java.sql.Connection;  
import java.sql.Driver;  
import java.sql.DriverManager;  
import java.sql.SQLException;  
import java.util.Properties;  
import java.util.Vector;  
  
import com.lcma.conn.PooledConnection;  
import com.lcma.pools.IMyPool;  
  
/** 
 * <p>Title      : 連線池介面實現類</p> 
 * <p>Description: [子應用名]_[模組名]</p> 
 * <p>Copyright  : Copyright (c) 2016</p> 
 * <p>Company    : 科大訊飛</p> 
 * @author       : lcma 
 * @version      : 1.0 
 */  
public class MyPoolImpl implements IMyPool {  
    /** 
     * 驅動名稱 
     */  
    private String jdbcDriver;  
    /** 
     * 連線地址 
     */  
    private String jdbcUrl;  
    /** 
     * 使用者名稱 
     */  
    private String userName;  
    /** 
     * 密碼 
     */  
    private String password;  
    /** 
     * 初始化連線數 
     */  
    private int initCount;  
    /** 
     * 步進連線數 
     */  
    private int stepSize;  
    /** 
     * 最大連線數 
     */  
    private int poolMaxSize;  
      
    /** 
     * 連線池容器 
     */  
    private static Vector<PooledConnection> pooledConnections = new Vector<PooledConnection>();  
      
    /** 
     * 建構函式,執行初始化方法 
     */  
    public MyPoolImpl() {  
        init();  
    }  
      
    /** 
     * <p>Discription:初始化連線池</p> 
     * @author       : lcma 
     * @update       : 2016年12月5日下午8:42:37 
     */  
    private void init(){  
        InputStream is = this.getClass().getClassLoader().getResourceAsStream("jdbc.properties");  
        Properties pro = new Properties();  
        try {  
            pro.load(is);  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
        jdbcDriver = pro.getProperty("jdbcDriver");  
        jdbcUrl = pro.getProperty("jdbcUrl");  
        userName = pro.getProperty("userName");  
        password = pro.getProperty("password");  
        initCount = Integer.parseInt(pro.getProperty("initCount"));  
        stepSize = Integer.parseInt(pro.getProperty("stepSize"));  
        poolMaxSize = Integer.parseInt(pro.getProperty("poolMaxSize"));  
          
        try {  
            Driver driver = (Driver)Class.forName(jdbcDriver).newInstance();  
            //將driver註冊  
            DriverManager.registerDriver(driver);;  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        //建立連線  
        createConnection(initCount);  
    }  
  
    /** 
     * <p>Discription:建立連線</p> 
     * @param count 
     * @author       : lcma 
     * @update       : 2016年12月4日下午1:11:46 
     */  
    @Override  
    public void createConnection(int count) {  
        if(poolMaxSize<=0 || pooledConnections.size()+count > poolMaxSize){  
            System.out.println("建立連線失敗,超過最大連線數");  
            throw new RuntimeException("建立連線失敗,超過最大連線數");  
        }  
        try {  
            //迴圈建立連線  
            for(int i = 0; i < count; i++){  
                 //建立連線  
                Connection connection = DriverManager.getConnection(jdbcUrl, userName, password);  
                //例項化連線池中的連線  
                PooledConnection pooledConnection = new PooledConnection(connection, false);  
                //存入連線池容器  
                pooledConnections.add(pooledConnection);  
            }  
         } catch (SQLException e) {  
            e.printStackTrace();  
        }  
          
    }  
  
    /** 
     * <p>Discription:獲取連線</p> 
     * @return 
     * @author       : lcma 
     * @update       : 2016年12月4日下午1:11:34 
     */  
    @Override  
    public PooledConnection getConnection() {  
        if(pooledConnections.size()<=0){  
            System.out.println("獲取連線失敗,連線池為空");  
            throw new RuntimeException("獲取連線失敗,連線池為空");  
        }  
        PooledConnection connection = getRealConnection();  
        //判斷是否為空  
        while(connection == null){  
            //建立connection,步進數  
            createConnection(stepSize);  
            //重新獲取連線,有可能獲取的還為空,採用while迴圈判斷  
            getRealConnection();  
            //防止其他執行緒過來拿連線  
            try {  
                Thread.sleep(300);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
        return connection;  
    }  
      
    //判斷我們是否拿到有效的連線物件  
    private synchronized PooledConnection getRealConnection(){  
        //先判斷連線池是不是有我們需要的空閒連線物件  
        for(PooledConnection connection : pooledConnections){  
            //未處於繁忙狀態  
            if(!connection.isBusy()){  
                Connection conn = connection.getConn();  
                try {  
                    //判斷這個連線是不是有效,isValid就是建立了一個statement,執行sql語句,看是否成功  
                    if(!conn.isValid(2000)){  
                        Connection validConn = DriverManager.getConnection(jdbcUrl, userName, password);  
                        connection.setConn(validConn);  
                    }  
                } catch (SQLException e) {  
                    e.printStackTrace();  
                }  
                //設定為繁忙  
                connection.setBusy(true);  
                return connection;  
            }  
        }  
        return null;  
    }  
  
    public String getJdbcDriver() {  
        return jdbcDriver;  
    }  
  
    public void setJdbcDriver(String jdbcDriver) {  
        this.jdbcDriver = jdbcDriver;  
    }  
  
    public String getJdbcUrl() {  
        return jdbcUrl;  
    }  
  
    public void setJdbcUrl(String jdbcUrl) {  
        this.jdbcUrl = jdbcUrl;  
    }  
  
    public String getUserName() {  
        return userName;  
    }  
  
    public void setUserName(String userName) {  
        this.userName = userName;  
    }  
  
    public String getPassword() {  
        return password;  
    }  
  
    public void setPassword(String password) {  
        this.password = password;  
    }  
  
    public int getInitCount() {  
        return initCount;  
    }  
  
    public void setInitCount(int initCount) {  
        this.initCount = initCount;  
    }  
  
    public int getStepSize() {  
        return stepSize;  
    }  
  
    public void setStepSize(int stepSize) {  
        this.stepSize = stepSize;  
    }  
  
    public int getPoolMaxSize() {  
        return poolMaxSize;  
    }  
  
    public void setPoolMaxSize(int poolMaxSize) {  
        this.poolMaxSize = poolMaxSize;  
    }  
  
  
} 
建立PoolManager類,利用內部類單例模式解決多執行緒問題,多個執行緒在載入內部類的時候執行緒是互斥的,解決了執行緒安全問題。
package com.lcma.manager;  
  
import com.lcma.pools.impl.MyPoolImpl;  
  
/** 
 * <p>Title      : 利用內部類單例模式解決多執行緒問題</p> 
 * <p>Description: [子應用名]_[模組名]</p> 
 * <p>Copyright  : Copyright (c) 2016</p> 
 * <p>Company    : 科大訊飛</p> 
 * @author       : lcma 
 * @version      : 1.0 
 */  
public class PoolManager {  
      
    private static class creatPool{  
        private static MyPoolImpl poolImpl = new MyPoolImpl();  
    }  
      
    //多個執行緒在載入內部類的時候執行緒是互斥的,所以用單例模式的內部類形式避免執行緒混亂  
    public static MyPoolImpl getInstace(){  
        return creatPool.poolImpl;  
    }  
  
} 
測試類MyPoolMain,測試2000個執行緒同時請求資料庫,程式應對自如,這就是資料庫連線池的強大之處。
package com.lcma.main;  
  
import java.sql.ResultSet;  
import java.sql.SQLException;  
  
import com.lcma.conn.PooledConnection;  
import com.lcma.manager.PoolManager;  
import com.lcma.pools.impl.MyPoolImpl;  
  
/**  
 * <p>Description: 測試類</p>  
 * <p>Copyright  : Copyright (c) 2017</p>  
 * @author       : lcma  
 * @version      : 1.0  
 */  
public class MyPoolMain {  
      
    /**  
     * 獲取連線池容器實現類  
     */  
    private static MyPoolImpl poolImpl = PoolManager.getInstace();  
      
    /**  
     * 單個連線查詢測試  
     */  
    public synchronized static void selectData(){  
        PooledConnection connection = poolImpl.getConnection();  
        ResultSet rs = connection.queryBySql("select * from class");  
        try {  
            while(rs.next()){  
                System.out.print(rs.getString("ID") + "\t\t");  
                System.out.print(rs.getString("NAME") + "\t\t");  
                System.out.print(rs.getString("TEACHER") + "\t\t");  
                System.out.println();  
            }  
        } catch (SQLException e) {  
            e.printStackTrace();  
        }finally {  
            try {rs.close();} catch (SQLException e) {e.printStackTrace();}  
            connection.close();  
        }  
    }  
      
    /**  
     * <p>Discription:測試2000個執行緒</p>  
     * @param args  
     * @author       : lcma  
     * @update       : 2016年12月5日下午11:44:18  
     */  
    public static void main(String[] args){  
        for (int i = 0; i < 2000; i++) {  
            new Thread(new Runnable() {  
                public void run() {  
                    selectData();  
                    System.out.println();  
                }  
            }).start();  
        }  
          
    }  
  
} 

注:上面程式碼只對功能做出實現,對程式碼規範性並沒有做太高要求。