zhyiwww
          用平實(shí)的筆,記錄編程路上的點(diǎn)點(diǎn)滴滴………
          posts - 536,comments - 394,trackbacks - 0

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

          ?

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

          ?

          ?

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

          ?

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

          接,不同頁(yè)面請(qǐng)求能夠共享同一連接,就能獲得顯著的性能改善。

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

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

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

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

          DBConnectionPool 提供以下功能:

          ?

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

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

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

          ?

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

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

          管理類 DBConnectionManager 用于管理多個(gè)連接池對(duì)象,它提供以下功能:

          ?

          1) 裝載和注冊(cè) JDBC 驅(qū)動(dòng)程序。

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

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

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

          ?

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

          ?

          ?

          二、具體實(shí)現(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 支持對(duì)一個(gè)或多個(gè)由屬性文件定義的數(shù)據(jù)庫(kù)連接

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

          009 */

          010 public class DBConnectionManager {

          011 static private DBConnectionManager instance; // 唯一實(shí)例

          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 * 返回唯一實(shí)例 . 如果是第一次調(diào)用此方法 , 則創(chuàng)建實(shí)例

          020 *

          021 * @return DBConnectionManager 唯一實(shí)例

          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ù)私有以防止其它對(duì)象創(chuàng)建本類實(shí)例

          033 */

          034 private DBConnectionManager() {

          035 init();

          036 }

          037

          038 /**

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

          040 *

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

          042 * @param con 連接對(duì)象

          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 * 獲得一個(gè)可用的 ( 空閑的 ) 連接 . 如果沒有可用連接 , 且已有連接數(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 * 獲得一個(gè)可用連接 . 若沒有可用連接 , 且已有連接數(shù)小于最大連接數(shù)限制 ,

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

          069 *

          070 * @param name 連接池名字

          071 * @param time 以毫秒計(jì)的等待時(shí)間

          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ū)動(dòng)程序的注冊(cè)

          084 */

          085 public synchronized void release() {

          086 // 等待直到最后一個(gè)客戶程序調(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ū)動(dòng)程序 " + driver.getClass().getName()+" 的注冊(cè) ");

          102 }

          103 catch (SQLException e) {

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

          105 }

          106 }

          107 }

          108

          109 /**

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

          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(" 錯(cuò)誤的最大連接數(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 " 請(qǐng)確保 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 * 裝載和注冊(cè)所有 JDBC 驅(qū)動(dòng)程序

          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(" 成功注冊(cè) JDBC 驅(qū)動(dòng)程序 " + driverClassName);

          186 }

          187 catch (Exception e) {

          188 log(" 無法注冊(cè) JDBC 驅(qū)動(dòng)程序 : " +

          189 driverClassName + ", 錯(cuò)誤 : " + 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)部類定義了一個(gè)連接池 . 它能夠根據(jù)要求創(chuàng)建新連接 , 直到預(yù)定的最

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

          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ù)庫(kù)的 JDBC URL

          227 * @param user 數(shù)據(jù)庫(kù)帳號(hào) , 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 * 從連接池獲得一個(gè)可用連接 . 如沒有空閑的連接且當(dāng)前連接數(shù)小于最大連接

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

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

          256 */

          257 public synchronized Connection getConnection() {

          258 Connection con = null;

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

          260 // 獲取向量中第一個(gè)可用連接

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

          262 freeConnections.removeElementAt(0);

          263 try {

          264 if (con.isClosed()) {

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

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

          267 con = getConnection();

          268 }

          269 }

          270 catch (SQLException e) {

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

          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 * 從連接池獲取可用連接 . 可以指定客戶程序能夠等待的最長(zhǎng)時(shí)間

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

          288 *

          289 * @param timeout 以毫秒計(jì)的等待時(shí)間限制

          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() 返回的原因是超時(shí)

          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+" 中的一個(gè)連接 ");

          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)建一個(gè)新的連接 ");

          338 }

          339 catch (SQLException e) {

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

          341 return null;

          342 }

          343 return con;

          344 }

          345 }

          346 }

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



          |----------------------------------------------------------------------------------------|
                                     版權(quán)聲明  版權(quán)所有 @zhyiwww
                      引用請(qǐng)注明來源 http://www.aygfsteel.com/zhyiwww   
          |----------------------------------------------------------------------------------------|
          posted on 2006-06-02 19:08 zhyiwww 閱讀(354) 評(píng)論(0)  編輯  收藏 所屬分類: j2ee
          主站蜘蛛池模板: 扬中市| 米林县| 甘泉县| 射阳县| 平利县| 来宾市| 商丘市| 彭水| 文昌市| 文登市| 柳林县| 兴义市| 沂南县| 庐江县| 安宁市| 寻甸| 锦州市| 兴山县| 从江县| 余庆县| 贵南县| 阜宁县| 广水市| 武陟县| 南丹县| 广州市| 凭祥市| 丰城市| 和顺县| 永吉县| 疏勒县| 晋城| 眉山市| 伊春市| 南和县| 博白县| 磐石市| 韶山市| 三穗县| 塘沽区| 获嘉县|