* 大連接數為止
在返回連接給客戶程序之前
它能夠驗證連接的有效性
*/
class DBConnectionPool {
private int checkedOut;
private Vector freeConnections = new Vector();
private int maxConn;
private String name;
private String password;
private String URL;
private String user;
/**
* 創建新的連接池
*
* @param name 連接池名字
* @param URL 數據庫的JDBC URL
* @param user 數據庫帳號
或 null
* @param password 密碼
或 null
* @param maxConn 此連接池允許建立的最大連接數
*/
public DBConnectionPool(String name
String URL
String user
String password
int maxConn) {
this
name = name;
this
URL = URL;
this
user = user;
this
password = password;
this
maxConn = maxConn;
}
/**
* 將不再使用的連接返回給連接池
*
* @param con 客戶程序釋放的連接
*/
public synchronized void freeConnection(Connection con) {
// 將指定連接加入到向量末尾
freeConnections
addElement(con);
checkedOut
;
notifyAll();
}
/**
* 從連接池獲得一個可用連接
如沒有空閒的連接且當前連接數小於最大連接
* 數限制
則創建新連接
如原來登記為可用的連接不再有效
則從向量刪除之
* 然後遞歸調用自己以嘗試新的可用連接
*/
public synchronized Connection getConnection() {
Connection con = null;
if (freeConnections
size() >
) {
// 獲取向量中第一個可用連接
con = (Connection) freeConnections
firstElement();
freeConnections
removeElementAt(
);
try {
if (con
isClosed()) {
log(
從連接池
+ name+
刪除一個無效連接
);
// 遞歸調用自己
嘗試再次獲取可用連接
con = getConnection();
}
}
catch (SQLException e) {
log(
從連接池
+ name+
刪除一個無效連接
);
// 遞歸調用自己
嘗試再次獲取可用連接
con = getConnection();
}
}
else if (maxConn ==
|| checkedOut < maxConn) {
con = newConnection();
}
if (con != null) {
checkedOut++;
}
return con;
}
/**
* 從連接池獲取可用連接
可以指定客戶程序能夠等待的最長時間\\r
* 參見前一個getConnection()方法
*
* @param timeout 以毫秒計的等待時間限制
*/
public synchronized Connection getConnection(long timeout) {
long startTime = new Date()
getTime();
Connection con;
while ((con = getConnection()) == null) {
try {
wait(timeout);
}
catch (InterruptedException e) {}
if ((new Date()
getTime()
startTime) >= timeout) {
// wait()返回的原因是超時
return null;
}
}
return con;
}
/**
* 關閉所有連接
*/
public synchronized void release() {
Enumeration allConnections = freeConnections
elements();
while (allConnections
hasMoreElements()) {
Connection con = (Connection) allConnections
nextElement();
try {
con
close();
log(
關閉連接池
+ name+
中的一個連接
);
}
catch (SQLException e) {
log(e
無法關閉連接池
+ name+
中的連接
);
}
}
freeConnections
removeAllElements();
}
/**
* 創建新的連接
*/
private Connection newConnection() {
Connection con = null;
try {
if (user == null) {
con = DriverManager
getConnection(URL);
}
else {
con = DriverManager
getConnection(URL
user
password);
}
log(
連接池
+ name+
創建一個新的連接
);
}
catch (SQLException e) {
log(e
無法創建下列URL的連接:
+ URL);
return null;
}
return con;
}
}
}
三
類DBConnectionPool說明
該類在
至
行實現
它表示指向某個數據庫的連接池
數據庫由JDBC URL標識
一個JDBC URL由三部分組成
協議標識(總是jdbc)
驅動程序標識(如 odbc
idb
oracle等)
數據庫標識(其格式依賴於驅動程序)
例如
jdbc:odbc:demo
即是一個指向demo數據庫的JDBC URL
而且訪問該數據庫要使用JDBC
ODBC驅動程序
每個連接池都有一個供客戶程序使用的名字以及可選的用戶帳號
密碼
最大連接數限制
如果Web應用程序所支持的某些數據庫操作可以被所有用戶執行
而其它一些操作應由特別許可的用戶執行
則可以為兩類操作分別定義連接池
兩個連接池使用相同的JDBC URL
但使用不同的帳號和密碼
類DBConnectionPool的建構函數需要上述所有數據作為其參數
如
至
行所示
這些數據被保存為它的實例變量
如
至
行
至
行所示
客戶程序可以使用DBConnectionPool類提供的兩個方法獲取可用連接
兩者的共同之處在於
如連接池中存在可用連接
則直接返回
否則創建新的連接並返回
如果沒有可用連接且已有連接總數等於最大限制數
第一個方法將直接返回null
而第二個方法將等待直到有可用連接為止
所有的可用連接對象均登記在名為freeConnections的向量(Vector)中
如果向量中有多於一個的連接
getConnection()總是選取第一個
同時
由於新的可用連接總是從尾部加入向量
從而使得數據庫連接由於長時間閒置而被關閉的風險減低到最小程度
第一個getConnection()在返回可用連接給客戶程序之前
調用了isClosed()方法驗證連接仍舊有效
如果該連接被關閉或觸發異常
getConnection()遞歸地調用自己以嘗試獲取另外的可用連接
如果在向量freeConnections中不存在任何可用連接
getConnection()方法檢查是否已經指定最大連接數限制
如已經指定
則檢查當前連接數是否已經到達極限
此處maxConn為
表示沒有限制
如果沒有指定最大連接數限制或當前連接數小於該值
該方法嘗試創建新的連接
如創建成功
則增加已使用連接的計數並返回
否則返回空值
如
至
行所示
創建新連接由newConnection()方法實現
創建過程與是否已經指定數據庫帳號
密碼有關
JDBC的DriverManager類提供多個getConnection()方法
這些方法要用到JDBC URL與其它一些參數
如用戶帳號和密碼等
DriverManager將使用指定的JDBC URL確定適合於目標數據庫的驅動程序及建立連接
在
至
行實現的第二個getConnection()方法需要一個以毫秒為單位的時間參數
該參數表示客戶程序能夠等待的最長時間
建立連接的具體操作仍舊由第一個getConnection()方法實現
該方法執行時先將startTime初始化為當前時間
在while循環中嘗試獲得一個連接
如果失敗
則以給定的時間值為參數調用wait()
wait()的返回可能是由於其它線程調用notify()或notifyAll()
也可能是由於預定時間已到
為找出wait()返回的真正原因
程序用當前時間減開始時間(startTime)
如差值大於預定時間則返回空值
否則再次調用getConnection()
把空閒的連接登記到連接池由
至
行的freeConnection()方法實現
它的參數為返回給連接池的連接對象
該對象被加入到freeConnections向量的末尾
然後減少已使用連接計數
調用notifyAll()是為了通知其它正在等待可用連接的線程
許多Servlet引擎為實現安全關閉提供多種方法
數據庫連接池需要知道該事件以保證所有連接能夠正常關閉
DBConnectionManager類負協調整個關閉過程
但關閉連接池中所有連接的任務則由DBConnectionPool類負責
在
至
行實現的release()方法供DBConnectionManager調用
該方法遍歷freeConnections向量並關閉所有連接
然後從向量中刪除這些連接
四類DBConnectionManager 說明\ 該類只能創建一個實例
其它對象能夠調用其靜態方法(也稱為類方法)獲得該唯一實例的引用
如
至
行所示
DBConnectionManager類的建構函數是私有的
這是為了避免其它對象創建該類的實例
DBConnectionManager類的客戶程序可以調用getInstance()方法獲得對該類唯一實例的引用
如
至
行所示
類的唯一實例在getInstance()方法第一次被調用期間創建
此後其引用就一直保存在靜態變量instance中
每次調用getInstance()都增加一個DBConnectionManager的客戶程序計數
即
該計數代表引用DBConnectionManager唯一實例的客戶程序總數
它將被用於控制連接池的關閉操作
該類實例的初始化工作由
至
行之間的私有方法init()完成
其中 getResourceAsStream()方法用於定位並打開外部文件
外部文件的定位方法依賴於類裝載器的實現
標准的本地類裝載器查找操作總是開始於類文件所在路徑
也能夠搜索CLASSPATH中聲明的路徑
db
properties是一個屬性文件
它包含定義連接池的鍵
值對
可供定義的公用屬性如下
drivers 以空格分隔的JDBC驅動程序類列表
logfile 日志文件的絕對路徑
其它的屬性和特定連接池相關
其屬性名字前應加上連接池名字
< poolname>
url 數據庫的 JDBC URL
< poolname>
maxconn 允許建立的最大連接數
表示沒有限制
< poolname>
user 用於該連接池的數據庫帳號
< poolname>
password 相應的密碼
其中url屬性是必需的
而其它屬性則是可選的
數據庫帳號和密碼必須合法
用於Windows平台的db
properties文件示例如下
drivers=sun
jdbc
odbc
JdbcOdbcDriver jdbc
idbDriver
logfile=D:\\user\\src\\java\\DBConnectionManager\\log
txt
idb
url=jdbc:idb:c:\\local\\javawebserver
\\db\\db
prp
idb
maxconn=
access
url=jdbc:odbc:demo
access
user=demo
access
password=demopw
注意在Windows路徑中的反斜槓必須輸入
個
這是由於屬性文件中的反斜槓同時也是一個轉義字符
init()方法在創建屬性對象並讀取db
properties文件之後
就開始檢查logfile屬性
如果屬性文件中沒有指定日志文件
則默認為當前目錄下的DBConnectionManager
log文件
如日志文件無法使用
則向System
err輸出日志記錄
裝載和注冊所有在drivers屬性中指定的JDBC驅動程序由
至
行之間的loadDrivers()方法實現
該方法先用StringTokenizer將drivers屬性值分割為對應於驅動程序名稱的字符串
然後依次裝載這些類並創建其實例
最後在 DriverManager中注冊該實例並把它加入到一個私有的向量drivers
向量drivers將用於關閉服務時從DriverManager取消所有JDBC 驅動程序的注冊
init()方法的最後一個任務是調用私有方法createPools()創建連接池對象
如
至
行所示
createPools()方法先創建所有屬性名字的枚舉對象(即Enumeration對象
該對象可以想象為一個元素系列
逐次調用其nextElement()方法將順序返回各元素)
然後在其中搜索名字以
url
結尾的屬性
對於每一個符合條件的屬性
先提取其連接池名字部分
進而讀取所有屬於該連接池的屬性
最後創建連接池對象並把它保存在實例變量pools中
散列表(Hashtable類 )pools實現連接池名字到連接池對象之間的映射
此處以連接池名字為鍵
連接池對象為值
為便於客戶程序從指定連接池獲得可用連接或將連接返回給連接池
類DBConnectionManager提供了方法getConnection()和freeConnection()
所有這些方法都要求在參數中指定連接池名字
具體的連接獲取或返回操作則調用對應的連接池對象完成
它們的實現分別在
至
行
至
行
至
行
如
至
行所示
為實現連接池的安全關閉
DBConnectionManager提供了方法release()
在上面我們已經提到
所有DBConnectionManager的客戶程序都應該調用靜態方法getInstance()以獲得該管理器的引用
此調用將增加客戶程序計數
客戶程序在關閉時調用release()可以遞減該計數
當最後一個客戶程序調用release()
遞減後的引用計數為
就可以調用各個連接池的release()方法關閉所有連接了
管理類release()方法最後的任務是撤銷所有JDBC驅動程序的注冊
五Servlet使用連接池示例 Servlet API所定義的Servlet生命周期類如
) 創建並初始化Servlet(init()方法)
) 響應客戶程序的服務請求(service()方法)
) Servlet終止運行
釋放所有資源(destroy()方法)
本例演示連接池應用
上述關鍵步驟中的相關操作為
) 在init()
用實例變量connMgr 保存調用DBConnectionManager
getInstance()所返回的引用
) 在service()
調用getConnection()
執行數據庫操作
用freeConnection()將連接返回給連接池
) 在destroy()
調用release()關閉所有連接
釋放所有資源
示例程序清單如下
import java
io
*;import java
sql
*;import javax
servlet
*;import javax
servlet
http
*;public class TestServlet extends HttpServlet { private DBConnectionManager connMgr; public void init(ServletConfig conf) throws ServletException { super
init(conf); connMgr = DBConnectionManager
getInstance(); } public void service(HttpServletRequest req
HttpServletResponse res) throws IOException { res
setContentType(
text/html
); PrintWriter out = res
getWriter(); Connection con = connMgr
getConnection(
idb
); if (con == null) { out
println(
不能獲取數據庫連接
); return; } ResultSet rs = null; ResultSetMetaData md = null; Statement stmt = null; try { stmt = con
createStatement(); rs = stmt
executeQuery(
SELECT * FROM EMPLOYEE
); md = rs
getMetaData(); out
println(
< H
>職工數據< /H
>
); while (rs
next()) { out
println(
< BR>
); for (int i =
; i < md
getColumnCount(); i++) { out
print(rs
getString(i) +
); } } stmt
close(); rs
close(); } catch (SQLException e) { e
printStackTrace(out); } connMgr
freeConnection(
idb
con); } public void destroy() { connMgr
release(); super
destroy(); }}
con); } public void destroy() { connMgr
release(); super
destroy(); }}
From:http://tw.wingwit.com/Article/program/Oracle/201311/17555.html