用连接池提高
				Servlet
				访问数据库的效率(转载)
		
		
				
				
				
						
						
				
				
						
						
				 
		
				Java Servlet
				作为首选的服务器端数据处理技术,正在迅速取代
				CGI
				脚本。
				Servlet
				超越
				CGI
				的优势之一在于,不仅多个请求可以共享公用资源,而且还可以在不同用户请求之间保留持续数据。本文介绍一种充分发挥该特色的实用技术,即数据库连接池。
		
		
				
						
						 
				
		
		
				
						 
				
		
		
				一、实现连接池的意义
		
		
				
						 
				
		
		
				动态
				Web
				站点往往用数据库存储的信息生成
				Web
				页面,每一个页面请求导致一次数据库访问。连接数据库不仅要开销一定的通讯和内存资源,还必须完成用户验证、安全上下文配置这类任务,因而往往成为最为耗时的操作。当然,实际的连接时间开销千变万化,但
				1
				到
				2
				秒延迟并非不常见。如果某个基于数据库的
				Web
				应用只需建立一次初始连
		
		
				接,不同页面请求能够共享同一连接,就能获得显著的性能改善。
		
		
				Servlet
				是一个
				Java
				类。
				Servlet
				引擎(它可能是
				Web
				服务软件的一部分,也可能是一个独立的附加模块)在系统启动或
				Servlet
				第一次被请求时将该类装入
				Java
				虚拟机并创建它的一个实例。不同用户请求由同一
				Servlet
				实例的多个独立线程处理。那些要
		
		
				求在不同请求之间持续有效的数据既可以用
				Servlet
				的实例变量来保存,也可以保存在独立的辅助对象中。
		
		
				用
				JDBC
				访问数据库首先要创建与数据库之间的连接,获得一个连接对象(
				Connection
				),由连接对象提供执行
				SQL
				语句的方法。
		
		
				本文介绍的数据库连接池包括一个管理类
				DBConnectionManager
				,负责提供与多个连接池对象(
				DBConnectionPool
				类)之间的接口。每一个连接池对象管理一组
				JDBC
				连接对象,每一个连接对象可以被任意数量的
				Servlet
				共享。
		
		
				类
				DBConnectionPool
				提供以下功能:
		
		
				
						 
				
		
		
				1) 
				从连接池获取(或创建)可用连接。
		
		
				2) 
				把连接返回给连接池。
		
		
				3) 
				在系统关闭时释放所有资源,关闭所有连接。
		
		
				
						 
				
		
		
				此外,
				 DBConnectionPool
				类还能够处理无效连接(原来登记为可用的连接,由于某种原因不再可用,如超时,通讯问题)
		
		
				,并能够限制连接池中的连接总数不超过某个预定值。
		
		
				管理类
				DBConnectionManager
				用于管理多个连接池对象,它提供以下功能:
		
		
				
						 
				
		
		
				1) 
				装载和注册
				JDBC
				驱动程序。
		
		
				2) 
				根据在属性文件中定义的属性创建连接池对象。
		
		
				3) 
				实现连接池名字与其实例之间的映射。
		
		
				4) 
				跟踪客户程序对连接池的引用,保证在最后一个客户程序结束时安全地关闭所有连接池。
		
		
				
						 
				
		
		
				本文余下部分将详细说明这两个类,最后给出一个示例演示
				Servlet
				使用连接池的一般过程。
		
		
				
						 
				
		
		
				
						 
				
		
		
				二、具体实现
		
		
				
						 
				
		
		
				DBConnectionManager.java
				程序清单如下:
		
		
				
						 
				
		
		
				001 import java.io.*;
		
		
				002 import java.sql.*;
		
		
				003 import java.util.*;
		
		
				004 import java.util.Date;
		
		
				005
		
		
				006 /**
		
		
				007 * 
				管理类
				DBConnectionManager
				支持对一个或多个由属性文件定义的数据库连接
		
		
				008 * 
				池的访问
				.
				客户程序可以调用
				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 * 
				返回唯一实例
				.
				如果是第一次调用此方法
				,
				则创建实例
		
		
				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 * 
				建构函数私有以防止其它对象创建本类实例
		
		
				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 * 
				获得一个可用的
				(
				空闲的
				)
				连接
				.
				如果没有可用连接
				,
				且已有连接数小于最大连接数
		
		
				053 * 
				限制
				,
				则创建并返回新连接
		
		
				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 * 
				获得一个可用连接
				.
				若没有可用连接
				,
				且已有连接数小于最大连接数限制
				,
		
		
				068 * 
				则创建并返回新连接
				.
				否则
				,
				在指定的时间内等待其它线程释放连接
				.
		
		
				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 * 
				关闭所有连接
				,
				撤销驱动程序的注册
		
		
				084 */
		
		
				085 public synchronized void release() {
		
		
				086 // 
				等待直到最后一个客户程序调用
		
		
				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
				驱动程序
				 " + driver.getClass().getName()+"
				的注册
				");
		
		
				102 }
		
		
				103 catch (SQLException e) {
		
		
				104 log(e, "
				无法撤销下列
				JDBC
				驱动程序的注册
				: " + driver.getClass().getName());
		
		
				105 }
		
		
				106 }
		
		
				107 }
		
		
				108
		
		
				109 /**
		
		
				110 * 
				根据指定属性创建连接池实例
				.
		
		
				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("
				错误的最大连接数限制
				: " + maxconn + " .
				连接池
				: " + poolName);
		
		
				134 max = 0;
		
		
				135 }
		
		
				136 DBConnectionPool pool =
		
		
				137 new DBConnectionPool(poolName, url, user, password, max);
		
		
				138 pools.put(poolName, pool);
		
		
				139 log("
				成功创建连接池
				" + 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
				驱动程序
		
		
				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
				驱动程序
				" + driverClassName);
		
		
				186 }
		
		
				187 catch (Exception e) {
		
		
				188 log("
				无法注册
				JDBC
				驱动程序
				: " +
		
		
				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 * 
				此内部类定义了一个连接池
				.
				它能够根据要求创建新连接
				,
				直到预定的最
		
		
				211 * 
				大连接数为止
				.
				在返回连接给客户程序之前
				,
				它能够验证连接的有效性
				.
		
		
				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 * 
				创建新的连接池
		
		
				224 *
		
		
				225 * @param name 
				连接池名字
		
		
				226 * @param URL 
				数据库的
				JDBC URL
		
		
				227 * @param user 
				数据库帐号
				,
				或
				 null
		
		
				228 * @param password 
				密码
				,
				或
				 null
		
		
				229 * @param maxConn 
				此连接池允许建立的最大连接数
		
		
				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 * 
				从连接池获得一个可用连接
				.
				如没有空闲的连接且当前连接数小于最大连接
		
		
				254 * 
				数限制
				,
				则创建新连接
				.
				如原来登记为可用的连接不再有效
				,
				则从向量删除之
				,
		
		
				255 * 
				然后递归调用自己以尝试新的可用连接
				.
		
		
				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 // 
				递归调用自己
				,
				尝试再次获取可用连接
		
		
				267 con = getConnection();
		
		
				268 }
		
		
				269 }
		
		
				270 catch (SQLException e) {
		
		
				271 log("
				从连接池
				" + name+"
				删除一个无效连接
				");
		
		
				272 // 
				递归调用自己
				,
				尝试再次获取可用连接
		
		
				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 * 
				关闭所有连接
		
		
				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("
				关闭连接池
				" + name+"
				中的一个连接
				");
		
		
				317 }
		
		
				318 catch (SQLException e) {
		
		
				319 log(e, "
				无法关闭连接池
				" + name+"
				中的连接
				");
		
		
				320 }
		
		
				321 }
		
		
				322 freeConnections.removeAllElements();
		
		
				323 }
		
		
				324
		
		
				325 /**
		
		
				326 * 
				创建新的连接
		
		
				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+"
				创建一个新的连接
				");
		
		
				338 }
		
		
				339 catch (SQLException e) {
		
		
				340 log(e, "
				无法创建下列
				URL
				的连接
				: " + URL);
		
		
				341 return null;
		
		
				342 }
		
		
				343 return con;
		
		
				344 }
		
		
				345 }
		
		
				346 }
		
		
				转自:动态网制作指南
				 www.knowsky.com
		
|----------------------------------------------------------------------------------------|
                           版权声明  版权所有 @zhyiwww
            引用请注明来源 http://www.blogjava.net/zhyiwww    
|----------------------------------------------------------------------------------------|
	
posted on 2006-06-02 19:08 
zhyiwww 阅读(360) 
评论(0)  编辑  收藏  所属分类: 
j2ee