Terry.Li-彬

          虛其心,可解天下之問;專其心,可治天下之學;靜其心,可悟天下之理;恒其心,可成天下之業。

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            143 隨筆 :: 344 文章 :: 130 評論 :: 0 Trackbacks

          一個經試用效果非常不錯的數據庫連接池

          前言:

          雖說現在許多企業級的應用服務器均自己帶有數據庫連接池功能,就連 Tomcat 也支持了這種功能。然而在許多時候,我們還是要使用數據庫連接池,如:訪問數據庫的 Java 桌面應用程序等。這個數據庫連接池是我根據《 Inside Servlets 》一書中的示例改寫而成,經過試用,效果非常不錯。特發布共享。 ( 作者: abnerchai 聯系我: mailto:josserchai@yahoo.com)

          源代碼

          //ConnectionPool.java

          package com.abner.dbconnector;

          import java.sql.*;

          import java.util.*;

          /**

          * ConnectionPool 類創建了一個對特定數據庫指定大小的連接池。連接池對象

          * 允許客戶端指定 JDBC 驅動程序,數據庫,使用數據庫的用戶名和密碼。而且,

          * 客戶端能指定連接池的在初始創建是產生數據庫連接的數量,和指定當連接

          * 不夠時每次自動增加連接的數量及連接池最多的數據庫連接的數量。

          *

          * 對外提供的方法有: ConnectionPool :構造函數

          * getInitialConnections: 返回連接池初始化大小

          * setInitialConnections: 設置連接池初始化大小

          * getIncrementalConnections: 返回連接池自動增加的增量

          * setIncrementalConnections: 設置連接池自動增加的大小

          * getMaxConnections :獲得連接池的最大可允許的連接數

          * setMaxConnections :設置連接池的最大可允許的連接數

          * getTestTable :獲得測試表的名字

          * setTestTable :設置測試表的名字

          * createPool: 創建連接池 , 線程己同步

          * getConnection: 從連接池中獲得一個數據庫連接

          * returnConnection: 返回一個連接到連接池中

          * refreshConnections: 刷新連接池

          * closeConnectionPool: 關閉連接池

          *

          *

          * @author abnerchai Email: josserchai@yahoo.com

          * @version 1.0.0

          *

          */

          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 型

          /**

          * 構造函數

          *

          * @param jdbcDriver String JDBC 驅動類串

          * @param dbUrl String 數據庫 URL

          * @param dbUsername String 連接數據庫用戶名

          * @param dbPassword String 連接數據庫用戶的密碼

          *

          */

          public ConnectionPool(String jdbcDriver,String dbUrl,String dbUsername,String dbPassword) {

          this.jdbcDriver = jdbcDriver;

          this.dbUrl = dbUrl;

          this.dbUsername = dbUsername;

          this.dbPassword = dbPassword;

          }

          /**

          * 返回連接池的初始大小

          *

          * @return 初始連接池中可獲得的連接數量

          */

          public int getInitialConnections() {

          return this.initialConnections;

          }

          /**

          * 設置連接池的初始大小

          *

          * @param 用于設置初始連接池中連接的數量

          */

          public void setInitialConnections(int initialConnections) {

          this.initialConnections = initialConnections;

          }

          /**

          * 返回連接池自動增加的大小 、

          *

          * @return 連接池自動增加的大小

          */

          public int getIncrementalConnections() {

          return this.incrementalConnections;

          }

          /**

          * 設置連接池自動增加的大小

          * @param 連接池自動增加的大小

          */

          public void setIncrementalConnections(int incrementalConnections) {

          this.incrementalConnections = incrementalConnections;

          }

          /**

          * 返回連接池中最大的可用連接數量

          * @return 連接池中最大的可用連接數量

          */

          public int getMaxConnections() {

          return this.maxConnections;

          }

          /**

          * 設置連接池中最大可用的連接數量

          *

          * @param 設置連接池中最大可用的連接數量值

          */

          public void setMaxConnections(int maxConnections) {

          this.maxConnections = maxConnections;

          }

          /**

          * 獲取測試數據庫表的名字

          *

          * @return 測試數據庫表的名字

          */

          public String getTestTable() {

          return this.testTable;

          }

          /**

          * 設置測試表的名字

          * @param testTable String 測試表的名字

          */

          public void setTestTable(String testTable) {

          this.testTable = testTable;

          }

          /**

          *

          * 創建一個數據庫連接池,連接池中的可用連接的數量采用類成員

          * initialConnections 中設置的值

          */

          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(" 數據庫連接池創建成功! ");

          }

          /**

          * 創建由 numConnections 指定數目的數據庫連接 , 并把這些連接

          * 放入 connections 向量中

          *

          * @param numConnections 要創建的數據庫連接的數目

          */

          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(" 數據庫連接己創建 ......");

          }

          }

          /**

          * 創建一個新的數據庫連接并返回它

          *

          * @return 返回一個新創建的數據庫連接

          */

          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; // 返回創建的新的數據庫連接

          }

          /**

          * 通過調用 getFreeConnection() 函數返回一個可用的數據庫連接 ,

          * 如果當前沒有可用的數據庫連接,并且更多的數據庫連接不能創

          * 建(如連接池大小的限制),此函數等待一會再嘗試獲取。

          *

          * @return 返回一個可用的數據庫連接對象

          */

          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;// 返回獲得的可用的連接

          }

          /**

          * 本函數從連接池向量 connections 中返回一個可用的的數據庫連接,如果

          * 當前沒有可用的數據庫連接,本函數則根據 incrementalConnections 設置

          * 的值創建幾個數據庫連接,并放入連接池中。

          * 如果創建后,所有的連接仍都在使用中,則返回 null

          * @return 返回一個可用的數據庫連接

          */

          private Connection getFreeConnection() throws SQLException {

          // 從連接池中獲得一個可用的數據庫連接

          Connection conn = findFreeConnection();

          if (conn == null) {

          // 如果目前連接池中沒有可用的連接

          // 創建一些連接

          createConnections(incrementalConnections);

          // 重新從池中查找是否有可用連接

          conn = findFreeConnection();

          if (conn == null) {

          // 如果創建連接后仍獲得不到可用的連接,則返回 null

          return null;

          }

          }

          return conn;

          }

          /**

          * 查找連接池中所有的連接,查找一個可用的數據庫連接,

          * 如果沒有可用的連接,返回 null

          *

          * @return 返回一個可用的數據庫連接

          */

          private Connection findFreeConnection() throws SQLException {

          Connection conn = null;

          PooledConnection pConn = null;

          // 獲得連接池向量中所有的對象

          Enumeration enum = connections.elements();

          // 遍歷所有的對象,看是否有可用的連接

          while (enum.hasMoreElements()) {

          pConn = (PooledConnection) enum.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;// 返回找到到的可用連接

          }

          /**

          * 測試一個連接是否可用,如果不可用,關掉它并返回 false

          * 否則可用返回 true

          *

          * @param conn 需要測試的數據庫連接

          * @return 返回 true 表示此連接可用, false 表示不可用

          */

          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;

          }

          /**

          * 此函數返回一個數據庫連接到連接池中,并把此連接置為空閑。

          * 所有使用連接池獲得的數據庫連接均應在不使用此連接時返回它。

          *

          * @param 需返回到連接池中的連接對象

          */

          public void returnConnection(Connection conn) {

          // 確保連接池存在,如果連接沒有創建(不存在),直接返回

          if (connections == null) {

          System.out.println(" 連接池不存在,無法返回此連接到連接池中 !");

          return;

          }

          PooledConnection pConn = null;

          Enumeration enum = connections.elements();

          // 遍歷連接池中的所有連接,找到這個要返回的連接對象

          while (enum.hasMoreElements()) {

          pConn = (PooledConnection) enum.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 enum = connections.elements();

          while (enum.hasMoreElements()) {

          // 獲得一個連接對象

          pConn = (PooledConnection) enum.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 enum = connections.elements();

          while (enum.hasMoreElements()) {

          pConn = (PooledConnection) enum.nextElement();

          // 如果忙,等 5 秒

          if (pConn.isBusy()) {

          wait(5000); // 等 5 秒

          }

          //5 秒后直接關閉它

          closeConnection(pConn.getConnection());

          // 從連接池向量中刪除它

          connections.removeElement(pConn);

          }

          // 置連接池為空

          connections = null;

          }

          /**

          * 關閉一個數據庫連接

          *

          * @param 需要關閉的數據庫連接

          */

          private void closeConnection(Connection conn) {

          try {

          conn.close();

          }catch (SQLException e) {

          System.out.println(" 關閉數據庫連接出錯: "+e.getMessage());

          }

          }

          /**

          * 使程序等待給定的毫秒數

          *

          * @param 給定的毫秒數

          */

          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;

          }

          }

          }

          這個程序所有的解釋我都詳細地寫在了源程序中,我想就不必多解說了吧。

          posted on 2007-09-06 18:15 禮物 閱讀(265) 評論(0)  編輯  收藏 所屬分類: java
          主站蜘蛛池模板: 安义县| 江源县| 全椒县| 武鸣县| 本溪市| 吴川市| 乐山市| 申扎县| 保靖县| 信丰县| 齐齐哈尔市| 林芝县| 衡南县| 句容市| 宜兴市| 突泉县| 旅游| 淮滨县| 横山县| 乳山市| 格尔木市| 鹤峰县| 吉木乃县| 靖江市| 奉节县| 金塔县| 武清区| 大安市| 杨浦区| 多伦县| 罗平县| 交口县| 平顺县| 宁武县| 穆棱市| 湘潭市| 沧州市| 西城区| 泸水县| 宁阳县| 永仁县|