zhyiwww
          用平實的筆,記錄編程路上的點點滴滴………
          posts - 536,comments - 394,trackbacks - 0

          用連接池提高 Servlet 訪問數(shù)據(jù)庫的效率(轉(zhuǎn)載)

          ?

          Java Servlet 作為首選的服務(wù)器端數(shù)據(jù)處理技術(shù),正在迅速取代 CGI 腳本。 Servlet 超越 CGI 的優(yōu)勢之一在于,不僅多個請求可以共享公用資源,而且還可以在不同用戶請求之間保留持續(xù)數(shù)據(jù)。本文介紹一種充分發(fā)揮該特色的實用技術(shù),即數(shù)據(jù)庫連接池。

          ?

          ?

          一、實現(xiàn)連接池的意義

          ?

          動態(tài) Web 站點往往用數(shù)據(jù)庫存儲的信息生成 Web 頁面,每一個頁面請求導致一次數(shù)據(jù)庫訪問。連接數(shù)據(jù)庫不僅要開銷一定的通訊和內(nèi)存資源,還必須完成用戶驗證、安全上下文配置這類任務(wù),因而往往成為最為耗時的操作。當然,實際的連接時間開銷千變?nèi)f化,但 1 2 秒延遲并非不常見。如果某個基于數(shù)據(jù)庫的 Web 應(yīng)用只需建立一次初始連

          接,不同頁面請求能夠共享同一連接,就能獲得顯著的性能改善。

          Servlet 是一個 Java 類。 Servlet 引擎(它可能是 Web 服務(wù)軟件的一部分,也可能是一個獨立的附加模塊)在系統(tǒng)啟動或 Servlet 第一次被請求時將該類裝入 Java 虛擬機并創(chuàng)建它的一個實例。不同用戶請求由同一 Servlet 實例的多個獨立線程處理。那些要

          求在不同請求之間持續(xù)有效的數(shù)據(jù)既可以用 Servlet 的實例變量來保存,也可以保存在獨立的輔助對象中。

          JDBC 訪問數(shù)據(jù)庫首先要創(chuàng)建與數(shù)據(jù)庫之間的連接,獲得一個連接對象( Connection ),由連接對象提供執(zhí)行 SQL 語句的方法。

          本文介紹的數(shù)據(jù)庫連接池包括一個管理類 DBConnectionManager ,負責提供與多個連接池對象( DBConnectionPool 類)之間的接口。每一個連接池對象管理一組 JDBC 連接對象,每一個連接對象可以被任意數(shù)量的 Servlet 共享。

          DBConnectionPool 提供以下功能:

          ?

          1) 從連接池獲取(或創(chuàng)建)可用連接。

          2) 把連接返回給連接池。

          3) 在系統(tǒng)關(guān)閉時釋放所有資源,關(guān)閉所有連接。

          ?

          此外, DBConnectionPool 類還能夠處理無效連接(原來登記為可用的連接,由于某種原因不再可用,如超時,通訊問題)

          ,并能夠限制連接池中的連接總數(shù)不超過某個預(yù)定值。

          管理類 DBConnectionManager 用于管理多個連接池對象,它提供以下功能:

          ?

          1) 裝載和注冊 JDBC 驅(qū)動程序。

          2) 根據(jù)在屬性文件中定義的屬性創(chuàng)建連接池對象。

          3) 實現(xiàn)連接池名字與其實例之間的映射。

          4) 跟蹤客戶程序?qū)B接池的引用,保證在最后一個客戶程序結(jié)束時安全地關(guān)閉所有連接池。

          ?

          本文余下部分將詳細說明這兩個類,最后給出一個示例演示 Servlet 使用連接池的一般過程。

          ?

          ?

          二、具體實現(xiàn)

          ?

          DBConnectionManager.java 程序清單如下:

          ?

          001 import java.io.*;

          002 import java.sql.*;

          003 import java.util.*;

          004 import java.util.Date;

          005

          006 /**

          007 * 管理類 DBConnectionManager 支持對一個或多個由屬性文件定義的數(shù)據(jù)庫連接

          008 * 池的訪問 . 客戶程序可以調(diào)用 getInstance() 方法訪問本類的唯一實例 .

          009 */

          010 public class DBConnectionManager {

          011 static private DBConnectionManager instance; // 唯一實例

          012 static private int clients;

          013

          014 private Vector drivers = new Vector();

          015 private PrintWriter log;

          016 private Hashtable pools = new Hashtable();

          017

          018 /**

          019 * 返回唯一實例 . 如果是第一次調(diào)用此方法 , 則創(chuàng)建實例

          020 *

          021 * @return DBConnectionManager 唯一實例

          022 */

          023 static synchronized public DBConnectionManager getInstance() {

          024 if (instance == null) {

          025 instance = new DBConnectionManager();

          026 }

          027 clients++;

          028 return instance;

          029 }

          030

          031 /**

          032 * 建構(gòu)函數(shù)私有以防止其它對象創(chuàng)建本類實例

          033 */

          034 private DBConnectionManager() {

          035 init();

          036 }

          037

          038 /**

          039 * 將連接對象返回給由名字指定的連接池

          040 *

          041 * @param name 在屬性文件中定義的連接池名字

          042 * @param con 連接對象

          043 */

          044 public void freeConnection(String name, Connection con) {

          045 DBConnectionPool pool = (DBConnectionPool) pools.get(name);

          046 if (pool != null) {

          047 pool.freeConnection(con);

          048 }

          049 }

          050

          051 /**

          052 * 獲得一個可用的 ( 空閑的 ) 連接 . 如果沒有可用連接 , 且已有連接數(shù)小于最大連接數(shù)

          053 * 限制 , 則創(chuàng)建并返回新連接

          054 *

          055 * @param name 在屬性文件中定義的連接池名字

          056 * @return Connection 可用連接或 null

          057 */

          058 public Connection getConnection(String name) {

          059 DBConnectionPool pool = (DBConnectionPool) pools.get(name);

          060 if (pool != null) {

          061 return pool.getConnection();

          062 }

          063 return null;

          064 }

          065

          066 /**

          067 * 獲得一個可用連接 . 若沒有可用連接 , 且已有連接數(shù)小于最大連接數(shù)限制 ,

          068 * 則創(chuàng)建并返回新連接 . 否則 , 在指定的時間內(nèi)等待其它線程釋放連接 .

          069 *

          070 * @param name 連接池名字

          071 * @param time 以毫秒計的等待時間

          072 * @return Connection 可用連接或 null

          073 */

          074 public Connection getConnection(String name, long time) {

          075 DBConnectionPool pool = (DBConnectionPool) pools.get(name);

          076 if (pool != null) {

          077 return pool.getConnection(time);

          078 }

          079 return null;

          080 }

          081

          082 /**

          083 * 關(guān)閉所有連接 , 撤銷驅(qū)動程序的注冊

          084 */

          085 public synchronized void release() {

          086 // 等待直到最后一個客戶程序調(diào)用

          087 if (--clients != 0) {

          088 return;

          089 }

          090

          091 Enumeration allPools = pools.elements();

          092 while (allPools.hasMoreElements()) {

          093 DBConnectionPool pool = (DBConnectionPool) allPools.nextElement();

          094 pool.release();

          095 }

          096 Enumeration allDrivers = drivers.elements();

          097 while (allDrivers.hasMoreElements()) {

          098 Driver driver = (Driver) allDrivers.nextElement();

          099 try {

          100 DriverManager.deregisterDriver(driver);

          101 log(" 撤銷 JDBC 驅(qū)動程序 " + driver.getClass().getName()+" 的注冊 ");

          102 }

          103 catch (SQLException e) {

          104 log(e, " 無法撤銷下列 JDBC 驅(qū)動程序的注冊 : " + driver.getClass().getName());

          105 }

          106 }

          107 }

          108

          109 /**

          110 * 根據(jù)指定屬性創(chuàng)建連接池實例 .

          111 *

          112 * @param props 連接池屬性

          113 */

          114 private void createPools(Properties props) {

          115 Enumeration propNames = props.propertyNames();

          116 while (propNames.hasMoreElements()) {

          117 String name = (String) propNames.nextElement();

          118 if (name.endsWith(".url")) {

          119 String poolName = name.substring(0, name.lastIndexOf("."));

          120 String url = props.getProperty(poolName + ".url");

          121 if (url == null) {

          122 log(" 沒有為連接池 " + poolName + " 指定 URL");

          123 continue;

          124 }

          125 String user = props.getProperty(poolName + ".user");

          126 String password = props.getProperty(poolName + ".password");

          127 String maxconn = props.getProperty(poolName + ".maxconn", "0");

          128 int max;

          129 try {

          130 max = Integer.valueOf(maxconn).intValue();

          131 }

          132 catch (NumberFormatException e) {

          133 log(" 錯誤的最大連接數(shù)限制 : " + maxconn + " . 連接池 : " + poolName);

          134 max = 0;

          135 }

          136 DBConnectionPool pool =

          137 new DBConnectionPool(poolName, url, user, password, max);

          138 pools.put(poolName, pool);

          139 log(" 成功創(chuàng)建連接池 " + poolName);

          140 }

          141 }

          142 }

          143

          144 /**

          145 * 讀取屬性完成初始化

          146 */

          147 private void init() {

          148 InputStream is = getClass().getResourceAsStream("/db.properties");

          149 Properties dbProps = new Properties();

          150 try {

          151 dbProps.load(is);

          152 }

          153 catch (Exception e) {

          154 System.err.println(" 不能讀取屬性文件 . " +

          155 " 請確保 db.properties CLASSPATH 指定的路徑中 ");

          156 return;

          157 }

          158 String logFile = dbProps.getProperty("logfile", "DBConnectionManager.log");

          159 try {

          160 log = new PrintWriter(new FileWriter(logFile, true), true);

          161 }

          162 catch (IOException e) {

          163 System.err.println(" 無法打開日志文件 : " + logFile);

          164 log = new PrintWriter(System.err);

          165 }

          166 loadDrivers(dbProps);

          167 createPools(dbProps);

          168 }

          169

          170 /**

          171 * 裝載和注冊所有 JDBC 驅(qū)動程序

          172 *

          173 * @param props 屬性

          174 */

          175 private void loadDrivers(Properties props) {

          176 String driverClasses = props.getProperty("drivers");

          177 StringTokenizer st = new StringTokenizer(driverClasses);

          178 while (st.hasMoreElements()) {

          179 String driverClassName = st.nextToken().trim();

          180 try {

          181 Driver driver = (Driver)

          182 Class.forName(driverClassName).newInstance();

          183 DriverManager.registerDriver(driver);

          184 drivers.addElement(driver);

          185 log(" 成功注冊 JDBC 驅(qū)動程序 " + driverClassName);

          186 }

          187 catch (Exception e) {

          188 log(" 無法注冊 JDBC 驅(qū)動程序 : " +

          189 driverClassName + ", 錯誤 : " + e);

          190 }

          191 }

          192 }

          193

          194 /**

          195 * 將文本信息寫入日志文件

          196 */

          197 private void log(String msg) {

          198 log.println(new Date() + ": " + msg);

          199 }

          200

          201 /**

          202 * 將文本信息與異常寫入日志文件

          203 */

          204 private void log(Throwable e, String msg) {

          205 log.println(new Date() + ": " + msg);

          206 e.printStackTrace(log);

          207 }

          208

          209 /**

          210 * 此內(nèi)部類定義了一個連接池 . 它能夠根據(jù)要求創(chuàng)建新連接 , 直到預(yù)定的最

          211 * 大連接數(shù)為止 . 在返回連接給客戶程序之前 , 它能夠驗證連接的有效性 .

          212 */

          213 class DBConnectionPool {

          214 private int checkedOut;

          215 private Vector freeConnections = new Vector();

          216 private int maxConn;

          217 private String name;

          218 private String password;

          219 private String URL;

          220 private String user;

          221

          222 /**

          223 * 創(chuàng)建新的連接池

          224 *

          225 * @param name 連接池名字

          226 * @param URL 數(shù)據(jù)庫的 JDBC URL

          227 * @param user 數(shù)據(jù)庫帳號 , null

          228 * @param password 密碼 , null

          229 * @param maxConn 此連接池允許建立的最大連接數(shù)

          230 */

          231 public DBConnectionPool(String name, String URL, String user, String password,

          232 int maxConn) {

          233 this.name = name;

          234 this.URL = URL;

          235 this.user = user;

          236 this.password = password;

          237 this.maxConn = maxConn;

          238 }

          239

          240 /**

          241 * 將不再使用的連接返回給連接池

          242 *

          243 * @param con 客戶程序釋放的連接

          244 */

          245 public synchronized void freeConnection(Connection con) {

          246 // 將指定連接加入到向量末尾

          247 freeConnections.addElement(con);

          248 checkedOut--;

          249 notifyAll();

          250 }

          251

          252 /**

          253 * 從連接池獲得一個可用連接 . 如沒有空閑的連接且當前連接數(shù)小于最大連接

          254 * 數(shù)限制 , 則創(chuàng)建新連接 . 如原來登記為可用的連接不再有效 , 則從向量刪除之 ,

          255 * 然后遞歸調(diào)用自己以嘗試新的可用連接 .

          256 */

          257 public synchronized Connection getConnection() {

          258 Connection con = null;

          259 if (freeConnections.size() > 0) {

          260 // 獲取向量中第一個可用連接

          261 con = (Connection) freeConnections.firstElement();

          262 freeConnections.removeElementAt(0);

          263 try {

          264 if (con.isClosed()) {

          265 log(" 從連接池 " + name+" 刪除一個無效連接 ");

          266 // 遞歸調(diào)用自己 , 嘗試再次獲取可用連接

          267 con = getConnection();

          268 }

          269 }

          270 catch (SQLException e) {

          271 log(" 從連接池 " + name+" 刪除一個無效連接 ");

          272 // 遞歸調(diào)用自己 , 嘗試再次獲取可用連接

          273 con = getConnection();

          274 }

          275 }

          276 else if (maxConn == 0 || checkedOut < maxConn) {

          277 con = newConnection();

          278 }

          279 if (con != null) {

          280 checkedOut++;

          281 }

          282 return con;

          283 }

          284

          285 /**

          286 * 從連接池獲取可用連接 . 可以指定客戶程序能夠等待的最長時間

          287 * 參見前一個 getConnection() 方法 .

          288 *

          289 * @param timeout 以毫秒計的等待時間限制

          290 */

          291 public synchronized Connection getConnection(long timeout) {

          292 long startTime = new Date().getTime();

          293 Connection con;

          294 while ((con = getConnection()) == null) {

          295 try {

          296 wait(timeout);

          297 }

          298 catch (InterruptedException e) {}

          299 if ((new Date().getTime() - startTime) >= timeout) {

          300 // wait() 返回的原因是超時

          301 return null;

          302 }

          303 }

          304 return con;

          305 }

          306

          307 /**

          308 * 關(guān)閉所有連接

          309 */

          310 public synchronized void release() {

          311 Enumeration allConnections = freeConnections.elements();

          312 while (allConnections.hasMoreElements()) {

          313 Connection con = (Connection) allConnections.nextElement();

          314 try {

          315 con.close();

          316 log(" 關(guān)閉連接池 " + name+" 中的一個連接 ");

          317 }

          318 catch (SQLException e) {

          319 log(e, " 無法關(guān)閉連接池 " + name+" 中的連接 ");

          320 }

          321 }

          322 freeConnections.removeAllElements();

          323 }

          324

          325 /**

          326 * 創(chuàng)建新的連接

          327 */

          328 private Connection newConnection() {

          329 Connection con = null;

          330 try {

          331 if (user == null) {

          332 con = DriverManager.getConnection(URL);

          333 }

          334 else {

          335 con = DriverManager.getConnection(URL, user, password);

          336 }

          337 log(" 連接池 " + name+" 創(chuàng)建一個新的連接 ");

          338 }

          339 catch (SQLException e) {

          340 log(e, " 無法創(chuàng)建下列 URL 的連接 : " + URL);

          341 return null;

          342 }

          343 return con;

          344 }

          345 }

          346 }

          轉(zhuǎn)自:動態(tài)網(wǎng)制作指南 www.knowsky.com



          |----------------------------------------------------------------------------------------|
                                     版權(quán)聲明  版權(quán)所有 @zhyiwww
                      引用請注明來源 http://www.aygfsteel.com/zhyiwww   
          |----------------------------------------------------------------------------------------|
          posted on 2006-06-02 19:08 zhyiwww 閱讀(353) 評論(0)  編輯  收藏 所屬分類: j2ee
          主站蜘蛛池模板: 大足县| 阜宁县| 六枝特区| 宁国市| 搜索| 柳江县| 谷城县| 尉犁县| 合山市| 鹤峰县| 大足县| 绵竹市| 敦化市| 宁远县| 玛多县| 同仁县| 五台县| 靖江市| 太原市| 肇东市| 阿勒泰市| 车致| 都兰县| 乐陵市| 兴国县| 娱乐| 兴业县| 永修县| 刚察县| 阳谷县| 昌宁县| 江西省| 前郭尔| 成武县| 邢台市| 漳浦县| 紫金县| 阿瓦提县| 洛阳市| 绥宁县| 惠州市|