数据库连接池的设计思路及java实现 .

来源:互联网 发布:口译软件 编辑:程序博客网 时间:2024/05/18 03:07
package connectionPool;import java.sql.Connection;import java.sql.Driver;import java.sql.DriverManager;import java.sql.SQLException;import java.util.ArrayList;import java.util.Enumeration;import java.util.HashSet;import java.util.Hashtable;import java.util.List;import java.util.Map;import java.util.Map.Entry;import java.util.Set;import connectionPool.util.Logger;import connectionPool.util.PropertiesMgr;/** * 连接池的设计思路 * 1、 * 初始化固定数目的连接(空闲连接与活跃连接在同一池中),建立连接的代理类,添加busy与startTime属性以便分发与回收连接 * 另建立守护线程回收失效连接 * 2、 * 维护一空闲连接池,初始为空,需要连接时建立,用完的连接回收进入空闲连接池; * 后续所需连接从空闲连接池获取;activeNum记录活跃连接数目; * 当空闲连接池为空且活跃连接数达到上限时,请求等待,超时即获取连接失败,超时前有连接被释放方可获得连接 * 第二个设计巧妙优势明显,采用第二种方式 *  * 数据库连接管理类,单例模式 * 可以管理加载多个数据库驱动,维护多个数据库连接池 * @author shijin * */public class DBConnectionManager {private static DBConnectionManager dbm = null;//单例模式里的成员变量都相当于是static了?/** * 客户数目 */private static int clients = 0;/** * 加载的驱动器集合 */private Set<Driver> drivers = new HashSet<Driver>();/** * 数据库连接池字典 */private Hashtable<String,DBConnectionPool> pools = new Hashtable<String,DBConnectionPool>();private final Logger log = Logger.getInstance(DBConnectionPool.class);private DBConnectionManager() {loadDrivers();createPools();}/** * 装载和注册所有的JDBC驱动程序 */private void loadDrivers() {String str_drivers = PropertiesMgr.getProperty("driver");for(String str_driver:str_drivers.split("\\s")) {Driver driver = null;try {driver = (Driver)Class.forName(str_driver).newInstance();DriverManager.registerDriver(driver);log.info("成功加载JDBC驱动:" + str_driver);} catch (InstantiationException e) {log.error("加载JDBC驱动" + str_driver + "时初始化异常,请检查配置文件");} catch (IllegalAccessException e) {log.info("加载JDBC驱动" + str_driver + "时非法访问,请检查配置文件");} catch (ClassNotFoundException e) {log.info("未找到JDBC驱动" + str_driver + "请引入相关包");} catch (SQLException e) {log.info("加载JDBC驱动" + str_driver + "失败,请检查引入包的正确性");}drivers.add(driver);}}/** * 根据配置文件创建数据库连接池 */private void createPools() {Enumeration<?> elements = PropertiesMgr.propertiesNames();while(elements.hasMoreElements()) {String element = (String)elements.nextElement();if(element.endsWith(".url")) {String poolName = element.substring(0, element.lastIndexOf("."));String url = PropertiesMgr.getProperty(poolName + ".url");if(url == null) {log.error("无法连接到数据库" + poolName + "请检查配置文件连接字符串");continue;}String user = PropertiesMgr.getProperty(poolName + ".user");String pwd = PropertiesMgr.getProperty(poolName + ".password");String str_max = PropertiesMgr.getProperty(poolName + ".maxconn", "0");int maxConn = 0;try{maxConn = Integer.parseInt(str_max);}catch(NumberFormatException e) {log.error("数据库" + poolName + "最大连接数设置错误,默认设为20");maxConn = 20;}DBConnectionPool pool = new DBConnectionPool(maxConn,url,poolName,user,pwd);pools.put(poolName, pool);log.info("成功创建数据库连接池" + poolName);}}}/** * 获得单例 * @return DBConnectionManager单例 */public synchronized static DBConnectionManager getInstance() {if(dbm == null) {dbm = new DBConnectionManager();}clients++;return dbm;}/** * 从指定连接池中获取可用连接,无等待 * @param poolName要获取连接的连接池名称 * @return连接池中的一个可用连接或null */public Connection getConnection(String poolName) {DBConnectionPool pool = (DBConnectionPool)pools.get(poolName);return pool.getConnection();}/** * 从指定的连接池获取可用连接,有等待超时 * @param poolName要获取连接的连接池名称 * @param timeout获取可用连接的超时时间,单位为秒 * @return连接池中的一个可用连接或null */public Connection getConnection(String poolName,long timeout) {DBConnectionPool  pool = (DBConnectionPool)pools.get(poolName);return pool.getConnection(timeout);}/** * 回收指定连接池的连接 * @param poolName连接池名称 * @param conn要回收的连接 */public void freeConnection(String poolName,Connection conn) {DBConnectionPool pool = (DBConnectionPool)pools.get(poolName);if(pool != null) {pool.freeConnection(conn);}log.error("找不到连接池,无法回收,请检查参数");}/** * 关闭所有连接,撤销驱动器的注册 */public synchronized void release() {//所有客户连接都关闭时才真正关闭连接撤销注册if(clients-- != 0) {return;}for(Map.Entry<String,DBConnectionPool> poolEntry:pools.entrySet()) {DBConnectionPool pool = poolEntry.getValue();pool.releaseAll();}log.info("已经关闭所有连接");for(Driver driver:drivers) {try {DriverManager.deregisterDriver(driver);log.info("撤销JDBC驱动器" + driver.getClass().getName() + "的注册");} catch (SQLException e) {log.error("撤销JDBC驱动器" + driver.getClass().getName() + "的注册异常");}}log.info("驱动器撤销完成");}/** * 此内部类定义了一个连接池. * 它能够获取数据库连接,直到预定的最 大连接数为止 * 在返回连接给客户程序之前,它能够验证连接的有效性 * @author shijin */private class DBConnectionPool {private int activeNum = 0;private int maxConn = 0;private String url = null;private String poolName = null;private String user = null;private String pwd = null;private List<Connection> freeConnections = new ArrayList<Connection>();/** *  * @param maxConn设定的连接池允许的最大连接数 * @param url数据库连接url * @param poolName连接池名称 * @param user数据库用户名,或null * @param pwd数据库用户密码,或null */public DBConnectionPool(int maxConn, String url, String poolName,String user, String pwd) {super();this.maxConn = maxConn;this.url = url;this.poolName = poolName;this.user = user;this.pwd = pwd;}/** * 获得一个可用连接,不保证任何情况都能返回一个连接(比如超过最大连接数的时候返回null) * 1、若空闲连接池不为空,从空闲连接池取出一个连接后检查有效性,正常则返回,失效则重新获取连接 * 2、若空闲连接池为空且未超过最大连接数限制,新建一个连接并返回 * 3、空闲连接数为空且超过最大连接数限制,返回null * @return获得的可用连接 */public synchronized Connection getConnection() {Connection conn = null;//空闲连接池中有空闲连接,直接取if(freeConnections.size() > 0) {//从空闲连接池中取出一个连接conn = freeConnections.get(0);freeConnections.remove(0);//检测连接有效性try{if(conn.isClosed()) {//由于已经从空闲连接池取出,所以不使用无效连接其就无法重新进入//空闲连接池,意味着其已经被删除了,记入日志即可log.info("从连接池" + poolName + "中取出的连接已关闭,重新获取连接");//继续从连接池尝试获取连接conn = getConnection();}}catch(SQLException e) {log.info("从连接池" + poolName + "中取出的发生服务器访问错误,重新获取连接");conn = getConnection();}} else if(activeNum < maxConn) {conn = newConnection();} else {//未获得连接}if(conn != null) {activeNum++;}return conn;}/** * 当无空闲连接而又未达到最大连接数限制时创建新的连接 * @return新创建的连接 */private Connection newConnection() {Connection conn = null;try{if(user == null) {conn = DriverManager.getConnection(url);} else {conn = DriverManager.getConnection(url, user, pwd);}log.info("与数据库" + poolName + "创建一个新连接");}catch(SQLException e) {log.error("无法根据\"" + url + "\"与数据库" + poolName + "建立新连接");}return conn;}/** * 获得一个可用连接,超过最大连接数时线程等待,直到有有连接释放时返回一个可用连接或者超时返回null * @param timeout 等待连接的超时时间,单位为秒 * @return */public synchronized Connection getConnection(long timeout) {Connection conn = null;long startTime = System.currentTimeMillis();while((conn = getConnection()) == null) {try{//被notify(),notifyALL()唤醒或者超时自动苏醒wait(timeout);}catch(InterruptedException e) {log.error("等待连接的线程被意外打断");}//若线程在超时前被唤醒,则不会返回null,继续循环尝试获取连接if(System.currentTimeMillis() - startTime > timeout*1000000)return null;}return conn;}/** * 将释放的空闲连接加入空闲连接池,活跃连接数减一并激活等待连接的线程 * @param conn释放的连接 */public synchronized void freeConnection(Connection conn) {freeConnections.add(conn);activeNum--;notifyAll();//通知正在由于达到最大连接数限制而wait的线程获取连接}/** * 关闭空闲连接池中的所有连接 */public synchronized void releaseAll() {for(Connection conn:freeConnections) {try{conn.close();log.info("关闭空闲连接池" + poolName + "中的一个连接");}catch(SQLException e) {log.error("关闭空闲连接池" + poolName + "中的连接失败");}}freeConnections.clear();}}}

package connectionPool.util;import java.io.FileWriter;import java.io.IOException;import java.io.PrintWriter;import java.util.Date;/** * 日志文件创建维护类,单例模式 * @author shijin * */public class Logger {private static Logger logger= null;private PrintWriter log = null;private static int level = 0;private Class<?> c = null;private static final int DEBUGLEVEL = 1;private static final int INFOLEVEL = 2;private static final int ERRORLEVEL = 3;private Logger(Class<?> c) {String logFileName = PropertiesMgr.getProperty("logfile","DBlog.txt");String str_level = PropertiesMgr.getProperty("loglevel", "3");this.c = c;level = Integer.parseInt(str_level);try {log = new PrintWriter(new FileWriter(logFileName),true);} catch (IOException e) {System.err.println("无法打开日志文件" + logFileName);log = new PrintWriter(System.err);}}public synchronized static Logger getInstance(Class<?> c) {if(logger == null) {logger = new Logger(c);}return logger;}public void debug(String msg) {if(level > DEBUGLEVEL) {msg = "DEBUG:" + new Date() + "-" + msg;System.out.println(msg);log.println(msg);}}public void info(String msg) {if(level > INFOLEVEL) {msg = "INFO:" + new Date() + "-" + msg;System.out.println(msg);log.println(msg);}}public void error(String msg) {if(level > ERRORLEVEL) {msg = "ERROR:" + new Date() + "-" + c + "-" + msg;System.out.println(msg);log.println(msg);}}}

package connectionPool.util;import java.io.File;import java.io.IOException;import java.util.Enumeration;import java.util.Properties;/** * 属性文件加载管理类,单例模式 * @author shijin * */public class PropertiesMgr {private static Properties pro = new Properties();private PropertiesMgr(){}static {try {pro.load(PropertiesMgr.class.getClassLoader().getResourceAsStream("config" + File.separator + "DB.properties"));} catch (IOException e) {e.printStackTrace();}}public static String getProperty(String key) {return pro.getProperty(key);}public static String getProperty(String key,String defaultValue) {//找不到key用defaultValue,而不是说后面为空字符串采用defaultValuereturn pro.getProperty(key, defaultValue);}public static Enumeration<?> propertiesNames() {return pro.propertyNames();}}

driver=com.mysql.jdbc.Drivermysql.url=jdbc:mysql://127.0.0.1/caiwu?useUnicode=true&characterEncoding=gb2312mysql.user=rootmysql.password=123mysql.maxconn=1000#\u65E5\u5FD7\u6587\u4EF6\u7684\u540D\u79F0logfile=#\u65E5\u5FD7\u7EA7\u522Bloglevel=

                                             
0 0