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

connectionPool.DBConnectionManager

[java] view plain copy

  1. package connectionPool;  
  2.   
  3. import java.sql.Connection;  
  4. import java.sql.Driver;  
  5. import java.sql.DriverManager;  
  6. import java.sql.SQLException;  
  7. import java.util.ArrayList;  
  8. import java.util.Enumeration;  
  9. import java.util.HashSet;  
  10. import java.util.Hashtable;  
  11. import java.util.List;  
  12. import java.util.Map;  
  13. import java.util.Map.Entry;  
  14. import java.util.Set;  
  15.   
  16. import connectionPool.util.Logger;  
  17. import connectionPool.util.PropertiesMgr;  
  18.   
  19. /** 
  20.  * 连接池的设计思路 
  21.  * 1、 
  22.  * 初始化固定数目的连接(空闲连接与活跃连接在同一池中),建立连接的代理类,添加busy与startTime属性以便分发与回收连接 
  23.  * 另建立守护线程回收失效连接 
  24.  * 2、 
  25.  * 维护一空闲连接池,初始为空,需要连接时建立,用完的连接回收进入空闲连接池; 
  26.  * 后续所需连接从空闲连接池获取;activeNum记录活跃连接数目; 
  27.  * 当空闲连接池为空且活跃连接数达到上限时,请求等待,超时即获取连接失败,超时前有连接被释放方可获得连接 
  28.  * 第二个设计巧妙优势明显,采用第二种方式 
  29.  *  
  30.  * 数据库连接管理类,单例模式 
  31.  * 可以管理加载多个数据库驱动,维护多个数据库连接池 
  32.  * @author shijin 
  33.  * 
  34.  */  
  35. public class DBConnectionManager {  
  36.       
  37.     private static DBConnectionManager dbm = null;  
  38.       
  39.     //单例模式里的成员变量都相当于是static了?  
  40.     /** 
  41.      * 客户数目 
  42.      */  
  43.     private static int clients = 0;  
  44.     /** 
  45.      * 加载的驱动器集合 
  46.      */  
  47.     private Set<Driver> drivers = new HashSet<Driver>();  
  48.     /** 
  49.      * 数据库连接池字典 
  50.      */  
  51.     private Hashtable<String,DBConnectionPool> pools = new Hashtable<String,DBConnectionPool>();  
  52.       
  53.     private final Logger log = Logger.getInstance(DBConnectionPool.class);  
  54.       
  55.     private DBConnectionManager() {  
  56.         loadDrivers();  
  57.         createPools();  
  58.     }  
  59.   
  60.     /** 
  61.      * 装载和注册所有的JDBC驱动程序 
  62.      */  
  63.     private void loadDrivers() {  
  64.         String str_drivers = PropertiesMgr.getProperty("driver");  
  65.         for(String str_driver:str_drivers.split("\\s")) {  
  66.             Driver driver = null;  
  67.             try {  
  68.                 driver = (Driver)Class.forName(str_driver).newInstance();  
  69.                 DriverManager.registerDriver(driver);  
  70.                 log.info("成功加载JDBC驱动:" + str_driver);  
  71.             } catch (InstantiationException e) {  
  72.                 log.error("加载JDBC驱动" + str_driver + "时初始化异常,请检查配置文件");  
  73.             } catch (IllegalAccessException e) {  
  74.                 log.info("加载JDBC驱动" + str_driver + "时非法访问,请检查配置文件");  
  75.             } catch (ClassNotFoundException e) {  
  76.                 log.info("未找到JDBC驱动" + str_driver + "请引入相关包");  
  77.             } catch (SQLException e) {  
  78.                 log.info("加载JDBC驱动" + str_driver + "失败,请检查引入包的正确性");  
  79.             }  
  80.             drivers.add(driver);  
  81.         }  
  82.     }  
  83.   
  84.     /** 
  85.      * 根据配置文件创建数据库连接池 
  86.      */  
  87.     private void createPools() {  
  88.         Enumeration<?> elements = PropertiesMgr.propertiesNames();  
  89.         while(elements.hasMoreElements()) {  
  90.             String element = (String)elements.nextElement();  
  91.             if(element.endsWith(".url")) {  
  92.                 String poolName = element.substring(0, element.lastIndexOf("."));  
  93.                 String url = PropertiesMgr.getProperty(poolName + ".url");  
  94.                 if(url == null) {  
  95.                     log.error("无法连接到数据库" + poolName + "请检查配置文件连接字符串");  
  96.                     continue;  
  97.                 }  
  98.                 String user = PropertiesMgr.getProperty(poolName + ".user");  
  99.                 String pwd = PropertiesMgr.getProperty(poolName + ".password");  
  100.                 String str_max = PropertiesMgr.getProperty(poolName + ".maxconn", "0");  
  101.                 int maxConn = 0;  
  102.                 try{  
  103.                     maxConn = Integer.parseInt(str_max);  
  104.                 }catch(NumberFormatException e) {  
  105.                     log.error("数据库" + poolName + "最大连接数设置错误,默认设为20");  
  106.                     maxConn = 20;  
  107.                 }                 
  108.                 DBConnectionPool pool = new DBConnectionPool(maxConn,url,poolName,user,pwd);  
  109.                 pools.put(poolName, pool);  
  110.                 log.info("成功创建数据库连接池" + poolName);  
  111.             }  
  112.         }  
  113.     }  
  114.   
  115.     /** 
  116.      * 获得单例 
  117.      * @return DBConnectionManager单例 
  118.      */  
  119.     public synchronized static DBConnectionManager getInstance() {  
  120.         if(dbm == null) {  
  121.             dbm = new DBConnectionManager();  
  122.         }  
  123.         clients++;  
  124.         return dbm;  
  125.     }  
  126.   
  127.     /** 
  128.      * 从指定连接池中获取可用连接,无等待 
  129.      * @param poolName  要获取连接的连接池名称 
  130.      * @return  连接池中的一个可用连接或null 
  131.      */  
  132.     public Connection getConnection(String poolName) {  
  133.         DBConnectionPool pool = (DBConnectionPool)pools.get(poolName);  
  134.         return pool.getConnection();  
  135.     }  
  136.       
  137.     /** 
  138.      * 从指定的连接池获取可用连接,有等待超时 
  139.      * @param poolName  要获取连接的连接池名称 
  140.      * @param timeout   获取可用连接的超时时间,单位为秒 
  141.      * @return          连接池中的一个可用连接或null 
  142.      */  
  143.     public Connection getConnection(String poolName,long timeout) {  
  144.         DBConnectionPool  pool = (DBConnectionPool)pools.get(poolName);  
  145.         return pool.getConnection(timeout);  
  146.     }  
  147.       
  148.     /** 
  149.      * 回收指定连接池的连接 
  150.      * @param poolName  连接池名称 
  151.      * @param conn      要回收的连接 
  152.      */  
  153.     public void freeConnection(String poolName,Connection conn) {  
  154.         DBConnectionPool pool = (DBConnectionPool)pools.get(poolName);  
  155.         if(pool != null) {  
  156.             pool.freeConnection(conn);  
  157.         }  
  158.         log.error("找不到连接池,无法回收,请检查参数");  
  159.     }  
  160.       
  161.     /** 
  162.      * 关闭所有连接,撤销驱动器的注册 
  163.      */  
  164.     public synchronized void release() {  
  165.         //所有客户连接都关闭时才真正关闭连接撤销注册  
  166.         if(clients-- != 0) {  
  167.             return;  
  168.         }  
  169.         for(Map.Entry<String,DBConnectionPool> poolEntry:pools.entrySet()) {  
  170.             DBConnectionPool pool = poolEntry.getValue();  
  171.             pool.releaseAll();  
  172.         }  
  173.         log.info("已经关闭所有连接");  
  174.         for(Driver driver:drivers) {  
  175.             try {  
  176.                 DriverManager.deregisterDriver(driver);  
  177.                 log.info("撤销JDBC驱动器" + driver.getClass().getName() + "的注册");  
  178.             } catch (SQLException e) {  
  179.                 log.error("撤销JDBC驱动器" + driver.getClass().getName() + "的注册异常");  
  180.             }  
  181.         }  
  182.         log.info("驱动器撤销完成");  
  183.     }  
  184.       
  185.     /** 
  186.      * 此内部类定义了一个连接池. 
  187.      * 它能够获取数据库连接,直到预定的最 大连接数为止 
  188.      * 在返回连接给客户程序之前,它能够验证连接的有效性 
  189.      * @author shijin 
  190.      */  
  191.     private class DBConnectionPool {  
  192.         private int activeNum = 0;  
  193.         private int maxConn = 0;  
  194.         private String url = null;  
  195.         private String poolName = null;  
  196.         private String user = null;  
  197.         private String pwd = null;  
  198.         private List<Connection> freeConnections = new ArrayList<Connection>();  
  199.           
  200.         /** 
  201.          *  
  202.          * @param maxConn   设定的连接池允许的最大连接数 
  203.          * @param url       数据库连接url 
  204.          * @param poolName  连接池名称 
  205.          * @param user      数据库用户名,或null 
  206.          * @param pwd       数据库用户密码,或null 
  207.          */  
  208.         public DBConnectionPool(int maxConn, String url, String poolName,  
  209.                 String user, String pwd) {  
  210.             super();  
  211.             this.maxConn = maxConn;  
  212.             this.url = url;  
  213.             this.poolName = poolName;  
  214.             this.user = user;  
  215.             this.pwd = pwd;  
  216.         }  
  217.           
  218.         /** 
  219.          * 获得一个可用连接,不保证任何情况都能返回一个连接(比如超过最大连接数的时候返回null) 
  220.          * 1、若空闲连接池不为空,从空闲连接池取出一个连接后检查有效性,正常则返回,失效则重新获取连接 
  221.          * 2、若空闲连接池为空且未超过最大连接数限制,新建一个连接并返回 
  222.          * 3、空闲连接数为空且超过最大连接数限制,返回null 
  223.          * @return  获得的可用连接 
  224.          */  
  225.         public synchronized Connection getConnection() {  
  226.             Connection conn = null;  
  227.             //空闲连接池中有空闲连接,直接取  
  228.             if(freeConnections.size() > 0) {  
  229.                 //从空闲连接池中取出一个连接  
  230.                 conn = freeConnections.get(0);  
  231.                 freeConnections.remove(0);  
  232.                 //检测连接有效性  
  233.                 try{  
  234.                     if(conn.isClosed()) {  
  235.                         //由于已经从空闲连接池取出,所以不使用无效连接其就无法重新进入  
  236.                         //空闲连接池,意味着其已经被删除了,记入日志即可  
  237.                         log.info("从连接池" + poolName + "中取出的连接已关闭,重新获取连接");  
  238.                         //继续从连接池尝试获取连接  
  239.                         conn = getConnection();  
  240.                     }  
  241.                 }catch(SQLException e) {  
  242.                     log.info("从连接池" + poolName + "中取出的发生服务器访问错误,重新获取连接");  
  243.                     conn = getConnection();  
  244.                 }  
  245.             } else if(activeNum < maxConn) {  
  246.                 conn = newConnection();  
  247.             } else {  
  248.                 //未获得连接  
  249.             }  
  250.             if(conn != null) {  
  251.                 activeNum++;  
  252.             }  
  253.             return conn;  
  254.         }  
  255.           
  256.         /** 
  257.          * 当无空闲连接而又未达到最大连接数限制时创建新的连接 
  258.          * @return  新创建的连接 
  259.          */  
  260.         private Connection newConnection() {  
  261.             Connection conn = null;  
  262.             try{  
  263.                 if(user == null) {  
  264.                     conn = DriverManager.getConnection(url);  
  265.                 } else {  
  266.                     conn = DriverManager.getConnection(url, user, pwd);  
  267.                 }  
  268.                 log.info("与数据库" + poolName + "创建一个新连接");  
  269.             }catch(SQLException e) {  
  270.                 log.error("无法根据\"" + url + "\"与数据库" + poolName + "建立新连接");  
  271.             }  
  272.             return conn;  
  273.         }  
  274.           
  275.         /** 
  276.          * 获得一个可用连接,超过最大连接数时线程等待,直到有有连接释放时返回一个可用连接或者超时返回null 
  277.          * @param timeout 等待连接的超时时间,单位为秒 
  278.          * @return 
  279.          */  
  280.         public synchronized Connection getConnection(long timeout) {  
  281.             Connection conn = null;  
  282.             long startTime = System.currentTimeMillis();  
  283.             while((conn = getConnection()) == null) {  
  284.                 try{  
  285.                     //被notify(),notifyALL()唤醒或者超时自动苏醒  
  286.                     wait(timeout);  
  287.                 }catch(InterruptedException e) {  
  288.                     log.error("等待连接的线程被意外打断");  
  289.                 }  
  290.                 //若线程在超时前被唤醒,则不会返回null,继续循环尝试获取连接  
  291.                 if(System.currentTimeMillis() - startTime > timeout*1000000)  
  292.                     return null;  
  293.             }  
  294.             return conn;  
  295.         }  
  296.           
  297.         /** 
  298.          * 将释放的空闲连接加入空闲连接池,活跃连接数减一并激活等待连接的线程 
  299.          * @param conn  释放的连接 
  300.          */  
  301.         public synchronized void freeConnection(Connection conn) {  
  302.             freeConnections.add(conn);  
  303.             activeNum--;  
  304.             notifyAll();//通知正在由于达到最大连接数限制而wait的线程获取连接  
  305.         }  
  306.           
  307.         /** 
  308.          * 关闭空闲连接池中的所有连接 
  309.          */  
  310.         public synchronized void releaseAll() {  
  311.             for(Connection conn:freeConnections) {  
  312.                 try{  
  313.                     conn.close();  
  314.                     log.info("关闭空闲连接池" + poolName + "中的一个连接");  
  315.                 }catch(SQLException e) {  
  316.                     log.error("关闭空闲连接池" + poolName + "中的连接失败");  
  317.                 }  
  318.             }  
  319.             freeConnections.clear();  
  320.         }  
  321.     }  
  322. }  

connectionpool.util.Logger

[java] view plain copy

  1. package connectionPool.util;  
  2.   
  3. import java.io.FileWriter;  
  4. import java.io.IOException;  
  5. import java.io.PrintWriter;  
  6. import java.util.Date;  
  7.   
  8. /** 
  9.  * 日志文件创建维护类,单例模式 
  10.  * @author shijin 
  11.  * 
  12.  */  
  13. public class Logger {  
  14.       
  15.     private static Logger logger= null;  
  16.     private PrintWriter log = null;  
  17.     private static int level = 0;  
  18.     private Class<?> c = null;  
  19.     private static final int DEBUGLEVEL = 1;  
  20.     private static final int INFOLEVEL = 2;  
  21.     private static final int ERRORLEVEL = 3;  
  22.       
  23.     private Logger(Class<?> c) {  
  24.         String logFileName = PropertiesMgr.getProperty("logfile","DBlog.txt");  
  25.         String str_level = PropertiesMgr.getProperty("loglevel", "3");  
  26.         this.c = c;  
  27.         level = Integer.parseInt(str_level);  
  28.         try {  
  29.             log = new PrintWriter(new FileWriter(logFileName),true);  
  30.         } catch (IOException e) {  
  31.             System.err.println("无法打开日志文件" + logFileName);  
  32.             log = new PrintWriter(System.err);  
  33.         }  
  34.     }  
  35.       
  36.     public synchronized static Logger getInstance(Class<?> c) {  
  37.         if(logger == null) {  
  38.             logger = new Logger(c);  
  39.         }  
  40.         return logger;  
  41.     }  
  42.       
  43.       
  44.     public void debug(String msg) {  
  45.         if(level > DEBUGLEVEL) {  
  46.             msg = "DEBUG:" + new Date() + "-" + msg;  
  47.             System.out.println(msg);  
  48.             log.println(msg);  
  49.         }  
  50.               
  51.     }  
  52.       
  53.     public void info(String msg) {  
  54.         if(level > INFOLEVEL) {  
  55.             msg = "INFO:" + new Date() + "-" + msg;  
  56.             System.out.println(msg);  
  57.             log.println(msg);  
  58.         }  
  59.               
  60.     }  
  61.       
  62.     public void error(String msg) {  
  63.         if(level > ERRORLEVEL) {  
  64.             msg = "ERROR:" + new Date() + "-" + c + "-" + msg;  
  65.             System.out.println(msg);  
  66.             log.println(msg);  
  67.         }  
  68.     }  
  69. }  

connection.util.PropertiesMgr

[java] view plain copy

  1. package connectionPool.util;  
  2.   
  3. import java.io.File;  
  4. import java.io.IOException;  
  5. import java.util.Enumeration;  
  6. import java.util.Properties;  
  7. /** 
  8.  * 属性文件加载管理类,单例模式 
  9.  * @author shijin 
  10.  * 
  11.  */  
  12. public class PropertiesMgr {  
  13.     private static Properties pro = new Properties();  
  14.     private PropertiesMgr(){}  
  15.     static {  
  16.         try {  
  17.             pro.load(PropertiesMgr.class.getClassLoader().getResourceAsStream("config" + File.separator + "DB.properties"));  
  18.         } catch (IOException e) {  
  19.             e.printStackTrace();  
  20.         }  
  21.     }  
  22.       
  23.     public static String getProperty(String key) {  
  24.         return pro.getProperty(key);  
  25.     }  
  26.       
  27.     public static String getProperty(String key,String defaultValue) {  
  28.         //找不到key用defaultValue,而不是说后面为空字符串采用defaultValue  
  29.         return pro.getProperty(key, defaultValue);  
  30.     }  
  31.       
  32.     public static Enumeration<?> propertiesNames() {  
  33.         return pro.propertyNames();  
  34.     }  
  35. }  

DB.properties

[plain] view plain copy

  1. driver=com.mysql.jdbc.Driver  
  2.   
  3. mysql.url=jdbc:mysql://127.0.0.1/caiwu?useUnicode=true&characterEncoding=gb2312  
  4.   
  5. mysql.user=root  
  6.   
  7. mysql.password=123  
  8.   
  9. mysql.maxconn=1000  
  10.   
  11. #\u65E5\u5FD7\u6587\u4EF6\u7684\u540D\u79F0  
  12. logfile=  
  13. #\u65E5\u5FD7\u7EA7\u522B  
  14. loglevel=  

转载于:https://my.oschina.net/newchaos/blog/1555846

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值