package studytest;java
//
// 一個效果很是不錯的JAVA數據庫鏈接池.
// from:http://www.jxer.com/home/?uid-195-action-viewspace-itemid-332
// 雖然如今用APACHE COMMONS DBCP能夠很是方便的創建數據庫鏈接池,
// 可是像這篇文章把數據庫鏈接池的內部原理寫的這麼透徹,注視這麼完整,
// 真是很是可貴,讓開發人員能夠更深層次的理解數據庫鏈接池,真是很是感
// 謝這篇文章的做者。
//
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Enumeration;
import java.util.Vector;sql
public class ConnectionPool {數據庫
private String jdbcDriver = ""; // 數據庫驅動
private String dbUrl = ""; // 數據 URL
private String dbUsername = ""; // 數據庫用戶名
private String dbPassword = ""; // 數據庫用戶密碼
private String testTable = ""; // 測試鏈接是否可用的測試表名,默認沒有測試表
private int initialConnections = 10; // 鏈接池的初始大小
private int incrementalConnections = 5; // 鏈接池自動增長的大小
private int maxConnections = 50; // 鏈接池最大的大小
private Vector connections = null; // 存放鏈接池中數據庫鏈接的向量 , 初始時爲 null
// 它中存放的對象爲 PooledConnection 型oracle
public ConnectionPool(String jdbcDriver, String dbUrl, String dbUsername,
String dbPassword) {函數
this.jdbcDriver = jdbcDriver;
this.dbUrl = dbUrl;
this.dbUsername = dbUsername;
this.dbPassword = dbPassword;
}測試
public int getInitialConnections() {
return this.initialConnections;
}ui
public void setInitialConnections(int initialConnections) {
this.initialConnections = initialConnections;
}this
public int getIncrementalConnections() {
return this.incrementalConnections;
}spa
public void setIncrementalConnections(int incrementalConnections) {
this.incrementalConnections = incrementalConnections;
}orm
public int getMaxConnections() {
return this.maxConnections;
}
public void setMaxConnections(int maxConnections) {
this.maxConnections = maxConnections;
}
public String getTestTable() {
return this.testTable;
}
public void setTestTable(String testTable) {
this.testTable = testTable;
}
public synchronized void createPool() throws Exception {
// 確保鏈接池沒有建立
// 假如鏈接池己經建立了,保存鏈接的向量 connections 不會爲空
if (connections != null) {
return; // 假如己經建立,則返回
}
// 實例化 JDBC Driver 中指定的驅動類實例
Driver driver = (Driver) (Class.forName(this.jdbcDriver).newInstance());
DriverManager.registerDriver(driver); // 註冊 JDBC 驅動程序
// 建立保存鏈接的向量 , 初始時有 0 個元素
connections = new Vector();
// 根據 initialConnections 中設置的值,建立鏈接。
createConnections(this.initialConnections);
System.out.println(" 數據庫鏈接池建立成功! ");
}
private void createConnections(int numConnections) throws SQLException {
// 循環建立指定數目的數據庫鏈接
for (int x = 0; x < numConnections; x++) {
// 是否鏈接池中的數據庫鏈接的數量己經達到最大?最大值由類成員 maxConnections
// 指出,假如 maxConnections 爲 0 或負數,表示鏈接數量沒有限制。
// 假如鏈接數己經達到最大,即退出。
if (this.maxConnections > 0 &&
this.connections.size() >= this.maxConnections) {
break;
}
//add a new PooledConnection object to connections vector
// 增長一個鏈接到鏈接池中(向量 connections 中)
try {
connections.addElement(new PooledConnection(newConnection()));
} catch (SQLException e) {
System.out.println(" 建立數據庫鏈接失敗! " + e.getMessage());
throw new SQLException();
}
System.out.println(" 數據庫鏈接己建立 ......");
}
}
private Connection newConnection() throws SQLException {
// 建立一個數據庫鏈接
Connection conn = DriverManager.getConnection(dbUrl, dbUsername,
dbPassword);
// 假如這是第一次建立數據庫鏈接,即檢查數據庫,得到此數據庫答應支持的
// 最大客戶鏈接數目
//connections.size()==0 表示目前沒有鏈接己被建立
if (connections.size() == 0) {
DatabaseMetaData metaData = conn.getMetaData();
int driverMaxConnections = metaData.getMaxConnections();
// 數據庫返回的 driverMaxConnections 若爲 0 ,表示此數據庫沒有最大
// 鏈接限制,或數據庫的最大鏈接限制不知道
//driverMaxConnections 爲返回的一個整數,表示此數據庫答應客戶鏈接的數目
// 假如鏈接池中設置的最大鏈接數量大於數據庫答應的鏈接數目 , 則置鏈接池的最大
// 鏈接數目爲數據庫答應的最大數目
if (driverMaxConnections > 0 &&
this.maxConnections > driverMaxConnections) {
this.maxConnections = driverMaxConnections;
}
}
return conn; // 返回建立的新的數據庫鏈接
}
public synchronized Connection getConnection() throws SQLException {
// 確保鏈接池己被建立
if (connections == null) {
return null; // 鏈接池還沒建立,則返回 null
}
Connection conn = getFreeConnection(); // 得到一個可用的數據庫鏈接
// 假如目前沒有能夠使用的鏈接,即全部的鏈接都在使用中
while (conn == null) {
// 等一會再試
wait(250);
conn = getFreeConnection(); // 從新再試,直到得到可用的鏈接,假如
//getFreeConnection() 返回的爲 null
// 則代表建立一批鏈接後也不可得到可用鏈接
}
return conn; // 返回得到的可用的鏈接
}
private Connection getFreeConnection() throws SQLException {
// 從鏈接池中得到一個可用的數據庫鏈接
Connection conn = findFreeConnection();
if (conn == null) {
// 假如目前鏈接池中沒有可用的鏈接
// 建立一些鏈接
createConnections(incrementalConnections);
// 從新從池中查找是否有可用鏈接
conn = findFreeConnection();
if (conn == null) {
// 假如建立鏈接後仍得到不到可用的鏈接,則返回 null
return null;
}
}
return conn;
}
private Connection findFreeConnection() throws SQLException {
Connection conn = null;
PooledConnection pConn = null;
// 得到鏈接池向量中全部的對象
Enumeration enumerate = connections.elements();
// 遍歷全部的對象,看是否有可用的鏈接
while (enumerate.hasMoreElements()) {
pConn = (PooledConnection) enumerate.nextElement();
if (!pConn.isBusy()) {
// 假如此對象不忙,則得到它的數據庫鏈接並把它設爲忙
conn = pConn.getConnection();
pConn.setBusy(true);
// 測試此鏈接是否可用
if (!testConnection(conn)) {
// 假如此鏈接不可再用了,則建立一個新的鏈接,
// 並替換此不可用的鏈接對象,假如建立失敗,返回 null
try {
conn = newConnection();
} catch (SQLException e) {
System.out.println(" 建立數據庫鏈接失敗! " + e.getMessage());
return null;
}
pConn.setConnection(conn);
}
break; // 己經找到一個可用的鏈接,退出
}
}
return conn; // 返回找到到的可用鏈接
}
private boolean testConnection(Connection conn) {
try {
// 斷定測試表是否存在
if (testTable.equals("")) {
// 假如測試表爲空,試着使用此鏈接的 setAutoCommit() 方法
// 來斷定鏈接否可用(此方法只在部分數據庫可用,假如不可用 ,
// 拋出異常)。注重:使用測試表的方法更可靠
conn.setAutoCommit(true);
} else { // 有測試表的時候使用測試表測試
//check if this connection is valid
Statement stmt = conn.createStatement();
stmt.execute("select count(*) from " + testTable);
}
} catch (SQLException e) {
// 上面拋出異常,此鏈接己不可用,關閉它,並返回 false;
closeConnection(conn);
return false;
}
// 鏈接可用,返回 true
return true;
}
public void returnConnection(Connection conn) {
// 確保鏈接池存在,假如鏈接沒有建立(不存在),直接返回
if (connections == null) {
System.out.println(" 鏈接池不存在,沒法返回此鏈接到鏈接池中 !");
return;
}
PooledConnection pConn = null;
Enumeration enumerate = connections.elements();
// 遍歷鏈接池中的全部鏈接,找到這個要返回的鏈接對象
while (enumerate.hasMoreElements()) {
pConn = (PooledConnection) enumerate.nextElement();
// 先找到鏈接池中的要返回的鏈接對象
if (conn == pConn.getConnection()) {
// 找到了 , 設置此鏈接爲空閒狀態
pConn.setBusy(false);
break;
}
}
}
public synchronized void refreshConnections() throws SQLException {
// 確保鏈接池己創新存在
if (connections == null) {
System.out.println(" 鏈接池不存在,沒法刷新 !");
return;
}
PooledConnection pConn = null;
Enumeration enumerate = connections.elements();
while (enumerate.hasMoreElements()) {
// 得到一個鏈接對象
pConn = (PooledConnection) enumerate.nextElement();
// 假如對象忙則等 5 秒 ,5 秒後直接刷新
if (pConn.isBusy()) {
wait(5000); // 等 5 秒
}
// 關閉此鏈接,用一個新的鏈接代替它。
closeConnection(pConn.getConnection());
pConn.setConnection(newConnection());
pConn.setBusy(false);
}
}
public synchronized void closeConnectionPool() throws SQLException {
// 確保鏈接池存在,假如不存在,返回
if (connections == null) {
System.out.println(" 鏈接池不存在,沒法關閉 !");
return;
}
PooledConnection pConn = null;
Enumeration enumerate = connections.elements();
while (enumerate.hasMoreElements()) {
pConn = (PooledConnection) enumerate.nextElement();
// 假如忙,等 5 秒
if (pConn.isBusy()) {
wait(5000); // 等 5 秒
}
//5 秒後直接關閉它
closeConnection(pConn.getConnection());
// 從鏈接池向量中刪除它
connections.removeElement(pConn);
}
// 置鏈接池爲空
connections = null;
}
private void closeConnection(Connection conn) {
try {
conn.close();
} catch (SQLException e) {
System.out.println(" 關閉數據庫鏈接出錯: " + e.getMessage());
}
}
private void wait(int mSeconds) {
try {
Thread.sleep(mSeconds);
} catch (InterruptedException e) {
}
}
class PooledConnection {
Connection connection = null; // 數據庫鏈接
boolean busy = false; // 此鏈接是否正在使用的標誌,默認沒有正在使用
// 構造函數,根據一個 Connection 構告一個 PooledConnection 對象
public PooledConnection(Connection connection) {
this.connection = connection;
}
// 返回此對象中的鏈接
public Connection getConnection() {
return connection;
}
// 設置此對象的,鏈接
public void setConnection(Connection connection) {
this.connection = connection;
}
// 得到對象鏈接是否忙
public boolean isBusy() {
return busy;
}
// 設置對象的鏈接正在忙
public void setBusy(boolean busy) {
this.busy = busy;
}
}
public static void main(String[] args) {
ConnectionPool connPool
= new ConnectionPool("oracle.jdbc.driver.OracleDriver",
"jdbc:oracle:thin:@*.*.*.*"
, "name", "password");
try {
connPool.createPool();
} catch (Exception ex) {
ex.printStackTrace();
}
try {
Connection conn = connPool.getConnection();
} catch (SQLException ex1) {
ex1.printStackTrace();
}
}
}