一个基本成熟的数据库连接池

最近,本人着手开发要有一个有强大后台的网站,在使用连接池时,觉得使用服务器自带的连接池总有些受限制。同时,为了加深对Java的学习和研究。写下了下面的连接池类。
该连接池主要有一下功能;

1)初始化一次,到处使用。
2)强大的日志功能,记录每一个sql动作,包括Connection、ResultSet 和Statement
3)根据连接的数量,定时自动回收已经释放或超时的连接。
4)配置灵活,可以使用各种JDBC驱动程序,支持多驱动程序。

更新说明:
1)新增了字符集配置项。
2) 新增了调试开关,便于使用前调试。
3)更改了日志日期输出格式。
4)排除了createStatement l里的一个bug (感谢rouselion的使用反馈)
5)修正了被封装类的getXXX函数的潜在的问题。

源代码:

  1.  
  2. //file : *****< ConnectionManager.java >****
  3. /*
  4. *  @Title  连接池
  5. *  @Author: zxg
  6. *  @Version 2.5
  7. *  @Memo:定义数据库连接及其数据库连接池等
  8. */
  9. package com.dbaccess.dbpool;
  10.  
  11. import java.io.*;
  12. import java.sql.*;
  13. import java.util.*;
  14.  
  15. import com.mysql.jdbc.Driver;
  16.  
  17.  
  18. public class ConnectionManager {
  19.  
  20.     static private ConnectionManager instance; // 唯一实例
  21. //    static private int clients;
  22.     static private long checkperiod=0;
  23.     private Vector drivers = new Vector();
  24.     private Hashtable pools = new Hashtable();
  25.     private Timer checkConnTimer=new Timer();
  26.  
  27.     //调试模式开关
  28.     private static boolean debug=false;
  29.     
  30.     //字符集哈西表
  31.     static private Hashtable Characters= new Hashtable();
  32.  
  33.     static private PrintWriter log;
  34.     /**
  35.     * 返回唯一实例.如果是第一次调用此方法,则创建实例
  36.     *
  37.     * @return ConnectionManager 唯一实例
  38.     */
  39.     static synchronized public ConnectionManager getInstance() {
  40.         if (instance == null) {
  41.             instance = new ConnectionManager();
  42.         }
  43. //        clients++;
  44.         return instance;
  45.     }
  46.  
  47.     /**
  48.     * 建构函数私有以防止其它对象创建本类实例
  49.     */
  50.     private ConnectionManager() {
  51.         init();
  52.     }
  53.  
  54.     /**
  55.     * 读取属性完成初始化
  56.     */
  57.     private void init() {
  58.         Properties    dbProps=null;
  59.         try {
  60.  
  61.             InputStream    is =  getClass().getResourceAsStream("db.properties");
  62.  
  63.             dbProps = new Properties();
  64.  
  65.             dbProps.load(is);
  66.         }
  67.         catch (Exception e) {
  68.             e.printStackTrace();
  69.             System.err.println("不能读取属性文件= " +
  70.             "请确保db.properties在CLASSPATH指定的路径中");
  71.             return;
  72.         }
  73.         String logFile = dbProps.getProperty("logfile""log.txt");
  74.  
  75.         String logPath=System.getProperty("user.dir");
  76.  
  77.         if(!logPath.endsWith("/"))logPath=logPath+"/";
  78.  
  79.         logFile=logPath+logFile;
  80.         if(debug){
  81.             System.out.println(logFile);
  82.             System.out.println("===============DEBUG====================");
  83.         }
  84.         try {
  85.             log = new PrintWriter(new FileWriter(logFile, true), true);
  86.         }
  87.         catch (IOException e) {
  88.             System.err.println("无法打开日志文件: " + logFile);
  89.             log = new PrintWriter(System.err);
  90.         }
  91.  
  92.         String ckPeriod=dbProps.getProperty("checkperiod","5");
  93.  
  94.         try {
  95.              checkperiod= Long.valueOf(ckPeriod).longValue()*60*1000;
  96.  
  97.         }
  98.         catch (NumberFormatException e) {
  99.             log("错误的最大连接数限制: " + ckPeriod + "  连接池: ");
  100.             log("使用默认值 5 分钟");
  101.             checkperiod = 5*60*1000;
  102.         }
  103.  
  104.         loadDrivers(dbProps);
  105.         createPools(dbProps);
  106.     }
  107.  
  108.     /**
  109.     * 装载和注册所有JDBC驱动程序
  110.     *
  111.     * @param props 属性
  112.     */
  113.     private void loadDrivers(Properties props) {
  114.         String driverClasses = props.getProperty("drivers");
  115.         StringTokenizer st = new StringTokenizer(driverClasses);
  116.         while (st.hasMoreElements()) {
  117.             String driverClassName = st.nextToken().trim();
  118.  
  119.             try {
  120.  
  121.                 Driver driver = (Driver)Class.forName(driverClassName).newInstance();
  122.                 if(driver!=null){
  123.                     DriverManager.registerDriver(driver);
  124.                     drivers.addElement(driver);
  125.                     log("Begin");
  126.                     log("成功注册JDBC驱动程序" + driverClassName);
  127.                 }
  128.                 else{
  129.                     log("Begin");
  130.                     log("注册JDBC驱动程序" + driverClassName+"失败");
  131.                 }
  132.  
  133.             }
  134.             catch (Exception e) {
  135.                 log("Begin");
  136.                 log("无法注册JDBC驱动程序: " + driverClassName + ", 错误: " + e);
  137.             }
  138.         }
  139.     }
  140.  
  141.     /**
  142.     * 根据指定属性创建连接池实例.
  143.     *
  144.     * @param props 连接池属性
  145.     */
  146.     private void createPools(Properties props) {
  147.  
  148.         Enumeration propNames = props.propertyNames();
  149.  
  150.         while (propNames.hasMoreElements()) {
  151.  
  152.             String name = (String) propNames.nextElement();
  153.             if (name.endsWith(".url")) {
  154.  
  155.                 String poolName=name.substring(0,name.indexOf("."));
  156.                 //记录该连接池的字符集设置
  157.                 String character=props.getProperty(poolName+".character","gb2312");
  158.  
  159.                 Characters.put(poolName,character);
  160.  
  161.                 try{
  162.  
  163.                     PoolInfoObject pio=new PoolInfoObject(poolName,props);
  164.  
  165.                     ConnectionPool  pool = new ConnectionPool(pio);
  166.  
  167.                     pools.put(poolName, pool);
  168.  
  169.                     //1分钟后开始每隔checkperiod分钟检查一次连接池情况
  170.  
  171.                     checkConnTimer.schedule(pool,60*1000,checkperiod);
  172.  
  173.                     log("成功创建连接池" + poolName);
  174.  
  175.                 }catch(Exception e){
  176.                     log(e,"创建DBConnectionPool出错");
  177.                 }
  178.             }
  179.         }
  180.     }
  181.  
  182.     /**
  183.     * 将连接对象返回给由名字指定的连接池
  184.     *
  185.     * @param name 在属性文件中定义的连接池名字
  186.     * @param con 连接对象
  187.     */
  188.     public void freeConnection(String name, Connection conn) {
  189.         ConnectionPool pool = (ConnectionPool) pools.get(name);
  190.         if (pool != null) {
  191.             pool.freeConnection(conn);
  192.         }
  193.     }
  194.  
  195.     /**
  196.     * 获得一个可用的(空闲的)连接.如果没有可用连接,且已有连接数小于最大连接数
  197.     * 限制,则创建并返回新连接
  198.     *
  199.     * @param name 在属性文件中定义的连接池名字
  200.     * @return Connection 可用连接或null
  201.     */
  202.     public Connection getConnection(String name) {
  203.         ConnectionPool pool = (ConnectionPool) pools.get(name);
  204.         if (pool != null) {
  205.             return pool.getConnection();
  206.         }
  207.         return null;
  208.     }
  209.  
  210.     /**
  211.     * 获得一个可用连接.若没有可用连接,且已有连接数小于最大连接数限制,
  212.     * 则创建并返回新连接.否则,在指定的时间内等待其它线程释放连接.
  213.     *
  214.     * @param name 连接池名字
  215.     * @param time 以毫秒计的等待时间
  216.     * @return Connection 可用连接或null
  217.     */
  218.     public Connection getConnection(String name, long time) {
  219.         ConnectionPool pool = (ConnectionPool) pools.get(name);
  220.         if (pool != null) {
  221.             return pool.getConnection(time);
  222.         }
  223.         return null;
  224.     }
  225.  
  226.     /**
  227.     * 关闭所有连接,撤销驱动程序的注册
  228.     */
  229.     public synchronized void release() {
  230.  
  231.     // 等待直到最后一个客户程序调用
  232. //        if (--clients != 0) {
  233. //            return;
  234. //        }
  235.  
  236.         checkConnTimer.cancel();
  237.  
  238.         Enumeration allPools = pools.elements();
  239.         while (allPools.hasMoreElements()) {
  240.             ConnectionPool pool = (ConnectionPool) allPools.nextElement();
  241.             pool.cancel();
  242.             pool.release();
  243.         }
  244.         Enumeration allDrivers = drivers.elements();
  245.         while (allDrivers.hasMoreElements()) {
  246.             Driver driver = (Driver) allDrivers.nextElement();
  247.             try {
  248.                 DriverManager.deregisterDriver(driver);
  249.                 log("撤销JDBC驱动程序 " + driver.getClass().getName()+"的注册");
  250.             }
  251.             catch (SQLException e) {
  252.                 log(e,"无法撤销下列JDBC驱动程序的注册: " + driver.getClass().getName());
  253.             }
  254.         }
  255.  
  256.     }
  257.     /**
  258.      * 取字符集设置参数。
  259.      * @param poolName
  260.      * @return String
  261.     */
  262.     static public String getCharacter(String poolName) {
  263.  
  264.         return (String)Characters.get(poolName);
  265.     }
  266.  
  267.     /**
  268.     * 将文本信息写入日志文件
  269.     */
  270.     static public void log(String msg) {
  271.         if(debug==trueSystem.out.println( getLocalTime()+ " " + msg);
  272.         else log.println( getLocalTime()+ " " + msg);
  273.     }
  274.  
  275.     /**
  276.     * 将文本信息与异常写入日志文件
  277.     */
  278.     static public void log(Throwable e, String msg) {
  279.  
  280.         if(debug==true){
  281.             System.err.println( getLocalTime()+ " " + msg);
  282.             e.printStackTrace(log);
  283.         }else{
  284.             log.println(getLocalTime() + " " + msg);
  285.             e.printStackTrace(log);
  286.         }
  287.     }
  288.     /**
  289.      * 获取当前系统时间
  290.      * @return String
  291.      */
  292.     private static  String getLocalTime(){
  293.  
  294.         int tt[];
  295.  
  296.         String TT[];
  297.  
  298.         tt= new int[6];
  299.         TT=new String[6];
  300.  
  301.         Calendar cl=Calendar.getInstance();
  302.  
  303.         tt[5]=cl.get(Calendar.SECOND);
  304.          tt[4]=cl.get(Calendar.MINUTE);
  305.           tt[3]=cl.get(Calendar.HOUR_OF_DAY);
  306.            tt[2]=cl.get(Calendar.DAY_OF_MONTH);
  307.         tt[1]=cl.get(Calendar.MONTH)+1;
  308.         tt[0]=cl.get(Calendar.YEAR);
  309.  
  310.  
  311.         for(int i=0;i<6 ;i++){
  312.             if(tt[i]<10)
  313.                 TT[i]="0"+Integer.toString(tt[i]);
  314.             else
  315.                 TT[i]=Integer.toString(tt[i]);
  316.  
  317.         }
  318.  
  319.         String t="";
  320.  
  321.         //连接日期                        //连接时间
  322.         t+=TT[0]+"-"+TT[1]+"-"+TT[2]+" "+TT[3]+":"+TT[4]+":"+TT[5];
  323.  
  324.         return t;
  325.     }
  326. //test==========================================test
  327.     /**
  328.      * Method main
  329.      *
  330.      *
  331.      * @param args
  332.      *
  333.      */
  334.     public static void main(String[] args) {
  335.         ConnectionManager dcm=null;
  336.  
  337.         try{
  338.             dcm=ConnectionManager.getInstance();
  339.  
  340.             Connection conn=dcm.getConnection("mysql");
  341.             Connection conn1=dcm.getConnection("mysql");
  342.             Connection conn2=dcm.getConnection("mysql");
  343.  
  344.  
  345.             ResultSet rs;
  346.             System.out.println("start");
  347.             String sql="select * from user ";
  348.  
  349.             System.out.println(":::::::::1");
  350.             Statement st=conn.createStatement();
  351.             if(st==null) {
  352.                 ConnectionManager.log("main--error while get /"Statement/"");
  353.                 return;            }
  354.             System.out.println("执行第"+1+"次检索");
  355.             rs=st.executeQuery(sql);
  356.             if(rs==null){
  357.                 ConnectionManager.log("main--error while get /"ResultSet/"");
  358.                 return;
  359.             }
  360.             System.out.println("/r/n");
  361.  
  362.             while(rs.next()){
  363.                 System.out.println(rs.getString(1));
  364.                 System.out.println(rs.getString(2));
  365.                 System.out.println(rs.getString(3));
  366.                 System.out.println();
  367.  
  368.             }
  369.  
  370.             rs.close();
  371.             st.close();
  372.             //rs=null;
  373.             //st=null;
  374.             System.out.println(":::::::::2");
  375.  
  376.             st = conn.createStatement();
  377.  
  378.  
  379.             System.out.println("执行第"+2+"次检索");
  380.             sql="select * from user";
  381.             rs=st.executeQuery(sql);
  382.             if(rs==null){
  383.                 ConnectionManager.log("main--error while get /"ResultSet/"");
  384.                 return;
  385.             }
  386.  
  387.             while(rs.next()){
  388.                 System.out.println("TTT:::TTT");
  389.                 System.out.println(rs.getString(1));
  390.                 System.out.println(rs.getString(2));
  391.                 System.out.println(rs.getString(3));
  392.                 System.out.println();
  393.  
  394.             }
  395.  
  396.             rs.close();
  397.             st.close();
  398.             rs=null;
  399.             st=null;
  400.  
  401.        dcm.freeConnection("mysql",conn);
  402.           
  403.             conn1.close();
  404.             conn2.close();
  405.  
  406.  
  407.             conn=null;
  408.             conn1=null;
  409.             conn2=null;
  410.             dcm.release();
  411.  
  412.  
  413.         }catch(SQLException e){
  414.             ConnectionManager.log(e,"main--error");
  415.         }
  416.     }
  417. }
  418. //===========================================================================//
  419.  
  420. //file : *****< ConnectionPool.java >****
  421.  
  422. /***************连接池类***********/
  423.  
  424. /**
  425. * 此类定义了一个连接池.它能够根据要求创建新连接,直到预定的最
  426. * 大连接数为止.在返回连接给客户程序之前,它能够验证连接的有效性.
  427. * 它继承自 TimerTask 被 ConnectionManager 类的timer成员调度
  428. */
  429. package com.dbaccess.dbpool;
  430.  
  431. import java.sql.*;
  432. import java.util.*;
  433. import java.util.Date;
  434.  
  435. public class ConnectionPool extends TimerTask{
  436.  
  437.     private int countConn;
  438.  
  439.     private Vector freeConns = new Vector();
  440.     private Vector usingConns = new Vector();
  441.  
  442.     private long maxUseTime;//使用中的连接最大空闲时间
  443.     private long maxFreeTime;//空闲的连接最大空闲时间(在连接数未小于最小连接数时,关闭此连接)
  444.  
  445.     private long maxConn;//最大连接数
  446.     private long minConn;//最小连接数
  447.     private long OnlineFreeTime ;//最大在线时间
  448.     private long maxNoneOnlineTime;//无人在线最大保留时间
  449.  
  450.     private String name;//连接池名(name)
  451.  
  452.     private String url;
  453.     private String user;
  454.     private String password;
  455.     private String option;
  456.  
  457.     /**
  458.     * 创建新的连接池
  459.     *
  460.     * @param pio 连接池信息
  461.     */
  462.     public ConnectionPool(PoolInfoObject pio) {
  463.  
  464.         this.name = pio.poolName;
  465.         this.url = pio.url;
  466.  
  467.         this.user = pio.user;
  468.         this.password = pio.password;
  469.         this.option=pio.optionConnection;
  470.  
  471.         this.maxConn = pio.maxConnection;
  472.         this.minConn = pio.minConnection;
  473.  
  474.         this.OnlineFreeTime = pio.maxOnlineFreeTime;
  475.         this.maxNoneOnlineTime = pio.maxNoneOnlineTime;
  476.  
  477.         if(this.minConn<=0) this.minConn=10;
  478.  
  479.         log("End One Part/r/n");
  480.         for(int i=0; i            newConnection();
  481.         }
  482.     }
  483.     /**
  484.     * 将新建的连接添加到连接池
  485.     *
  486.     * @param connobj 新建的连接
  487.     */
  488.     public synchronized void freeConnection(ConnectionObject connobj) {
  489.         // 将指定连接加入到向量末尾
  490.         try{
  491.             connobj.setInUse(false);
  492.             freeConns.addElement(connobj);
  493.             usingConns.removeElement(connobj);
  494.  
  495.             log("成功记录一个新建连接或者回收一个已释放连接");
  496.             notifyAll();
  497.         }catch(ArrayIndexOutOfBoundsException e){
  498.             log(e,"freeConnection(ConnectionObject connobj) --失败");
  499.  
  500.         }
  501.     }
  502.     /**
  503.     * 将不再使用的连接返回给连接池
  504.     *
  505.     * @param conn 客户程序主动释放的连接
  506.     */
  507.     public synchronized void freeConnection(Connection conn) {
  508.         // 将指定连接加入到向量末尾
  509.         ConnectionObject connobj=null;
  510.         int i;
  511.  
  512.         for(i=0;i        {
  513.             connobj=(ConnectionObject)usingConns.get(i);
  514.  
  515.             if(connobj.getConnection(false)==conn)
  516.                 break;
  517.         }
  518.  
  519.         if(i            try{
  520.                 connobj.setInUse(false);
  521.                 freeConns.addElement(connobj);
  522.                 usingConns.removeElement(connobj);
  523.                 log("客户程序主动释放连接--成功回收一个连接");
  524.                 notifyAll();
  525.             }catch(Exception e){
  526.                 log(e,"客户程序主动释放连接--回收一个连接--失败");
  527.             }
  528.         }
  529.     }
  530.  
  531.     /**
  532.     * 从连接池获得一个可用连接.如没有空闲的连接且当前连接数小于最大连接
  533.     * 数限制,则创建新连接.如原来登记为可用的连接不再有效,则从向量删除之,
  534.     * 然后递归调用自己以尝试新的可用连接.
  535.     * @return Connection
  536.     */
  537.     public synchronized Connection getConnection() {
  538.         ConnectionObject connobj = null;
  539.         Connection conn=null;
  540.         // 获取向量中第一个可用连接
  541.         try {
  542.             connobj = (ConnectionObject) freeConns.get(0);
  543.  
  544.         }
  545.         catch (Exception e) {
  546.  
  547.             log("End One Part/r/n");
  548.  
  549.             log("从连接池" + name+"获取一个连接失败");
  550.             if( maxConn == 0 || countConn < maxConn) {
  551.                 connobj = newConnection();
  552.                 conn=connobj.getConnection(true);
  553.             }
  554.         }
  555.         //如没有空闲的连接且当前连接数小于最大连接数限制,则创建新连接
  556.         if(connobj==null && ( maxConn == 0 || countConn < maxConn)) {
  557.             log("从连接池" + name+"获取一个连接失败");
  558.             log("End One Part/r/n");
  559.             connobj = newConnection();
  560.             conn=connobj.getConnection(true);
  561.         }else if(connobj!=null)
  562.         {
  563.             conn=connobj.getConnection(false);
  564.             if (conn==null) conn=connobj.getConnection(true);
  565.         }
  566.  
  567.         if (conn != null) {
  568.  
  569.             connobj.setLastAccessTime(new Date().getTime());
  570.             connobj.setInUse(true);
  571.  
  572.             usingConns.addElement(connobj);
  573.             freeConns.removeElementAt(0);
  574.  
  575.  
  576.             return conn;
  577.  
  578.         }else{
  579.             log("获取连接" + name+"失败--连接数量已达最大上限");
  580.             return null;
  581.         }
  582.     }
  583.  
  584.     /**
  585.     * 从连接池获取可用连接.可以指定客户程序能够等待的最长时间
  586.     * 参见前一个getConnection()方法.
  587.     *
  588.     * @param timeout 以毫秒计的等待时间限制
  589.     */
  590.     public synchronized Connection getConnection(long timeout) {
  591.  
  592.         long startTime = new Date().getTime();
  593.         Connection conn=null;
  594.         while ((conn = getConnection()) == null) {
  595.             try {
  596.                 wait(timeout);//??????????????
  597.             }
  598.             catch (InterruptedException e){
  599.  
  600.             }
  601.             if ((new Date().getTime() - startTime) >= timeout) {
  602.                 // wait()返回的原因是超时?????????
  603.                 return null;
  604.             }
  605.         }
  606.         return conn;
  607.     }
  608.  
  609.     /**
  610.     * 关闭所有连接
  611.     */
  612.     public synchronized void release() {
  613. //        cancel();
  614.         Enumeration allConnections = freeConns.elements();
  615.  
  616.         while (allConnections.hasMoreElements()) {
  617.             ConnectionObject connobj = (ConnectionObject) allConnections.nextElement();
  618.             try {
  619.                 connobj.close();
  620.                 connobj=null;
  621.                 log("关闭连接池" + name+"中的一个连接");
  622.             }
  623.             catch (SQLException e) {//SQLException
  624.                 log(e, "无法关闭连接池" + name+"中的连接");
  625.             }
  626.         }
  627.         freeConns.removeAllElements();
  628.         //
  629.         allConnections = usingConns.elements();
  630.  
  631.         while (allConnections.hasMoreElements()) {
  632.             ConnectionObject connobj = (ConnectionObject) allConnections.nextElement();
  633.             try {
  634.                 connobj.close();
  635.                 connobj=null;
  636.                 log("关闭连接池" + name+"中的一个连接");
  637.             }
  638.             catch (SQLException e) {//SQLException
  639.                 log(e, "无法关闭连接池" + name+"中的连接");
  640.             }
  641.         }
  642.         usingConns.removeAllElements();
  643.     }
  644.  
  645.     /**
  646.     * 创建新的连接
  647.     */
  648.     private ConnectionObject newConnection() {
  649.         ConnectionObject connobj= null;
  650.         try {
  651.  
  652.             log("连接池" + name+"创建一个新的连接对象");
  653.  
  654.             String URL=url+option;
  655.  
  656.              log("URL=" +URL );
  657.  
  658.              Connection conn = DriverManager.getConnection(URL,user,password);
  659.  
  660.             connobj=new ConnectionObject(conn,false);
  661.  
  662.             connobj.setPoolName(name);
  663.  
  664.             freeConnection(connobj);
  665.  
  666.             countConn++;
  667.  
  668.         }
  669.         catch (SQLException e) {
  670.             log(e, "无法创建下列URL的连接: " + url+" for User= " +user+" Password="+password);
  671.             return null;
  672.         }
  673.         return connobj;
  674.     }
  675.     //检查各连接状态(每checkperiod分钟一次)
  676.     
  677.     public synchronized void run (){
  678.  
  679.         ConnectionObject connobj=null;
  680.         //是否在maxNoneOnlineTime长时间内没有任何连接被使用。
  681.         if(usingConns.size()==0 && (freeConns.size()==minConn || countConn==1)){
  682.  
  683.             boolean bCanFree=true;
  684.             if(countConn==1)
  685.                 return;
  686.             else{
  687.                 for(int j=0;j                    connobj=(ConnectionObject)freeConns.get(j);
  688.  
  689.                     long lt=new Date().getTime()-connobj.getLastAccessTime();
  690.  
  691.                     if(lt                        bCanFree=false;
  692.                         break;
  693.                     }
  694.                 }
  695.             }
  696.  
  697.             if(bCanFree){
  698.                 log("run--连接池里的连接太闲--现在关闭部分连接,保持一个连接 ");
  699.                 
  700.                 while(freeConns.size()>1){
  701.                     try{
  702.                         connobj=(ConnectionObject)freeConns.get(0);
  703.                         connobj.close();
  704.                         connobj=null;
  705.                         freeConns.removeElementAt(0);
  706.                         countConn--;
  707.                     }catch(SQLException e){
  708.                         log(e,"run--连接池的连接太闲--关闭部分连接,保持一个连接--失败");
  709.                     }
  710.                 }
  711.                 log("关闭连接数量"+Long.toString(minConn-1));
  712.             }
  713.             return;
  714.         }
  715.         //回收 正在使用中的已经"关闭"(释放)的连接 和 使用时间已经超时的连接
  716.  
  717.         int i=0;
  718.  
  719.         while(i
  720.              connobj=(ConnectionObject)usingConns.get(i);
  721.             if(connobj.isInUse()==false){
  722.                 try{
  723.                         log("run--回收 正在使用中的已经/"关闭/"(释放)的连接");
  724.                         freeConnection(connobj);
  725.                         i--;
  726.                 }catch(ArrayIndexOutOfBoundsException e){
  727.                     log(e,"run--回收 正在使用中的已经/"关闭/"(释放)的连接--失败");
  728.                 }
  729.             }else{
  730.  
  731.                 long nowtime=new Date().getTime();
  732.                 long t=nowtime-connobj.getLastAccessTime();
  733.                 try{
  734.                     if(t>OnlineFreeTime){//超时时间为OnlineFreeTime分钟
  735.                         log("run--回收 使用时间已经超时的连接");
  736.                         freeConnection(connobj);
  737.                         i--;
  738.                     }
  739.                 }catch(ArrayIndexOutOfBoundsException e ){
  740.                     log(e,"run--回收 使用时间已经超时的连接--失败");
  741.                 }
  742.             }
  743.             i++;
  744.         }
  745.         //删除 空闲的已经被意外关闭的连接
  746.         i=0;
  747.         while(i
  748.             connobj= (ConnectionObject)freeConns.get(i);
  749.             try{
  750.                 if(connobj.isClosed()){
  751.                     connobj=null;
  752.                     freeConns.removeElementAt(i);
  753.                     countConn--;
  754.                     i--;
  755.                     log("run--删除 空闲的已经被意外关闭的连接");
  756.                 }
  757.             }catch(Exception e){
  758.                 log(e,"run--删除 空闲的已经被意外关闭的连接-失败");
  759.             }
  760.             i++;
  761.         }
  762.  
  763.         //删除 从空闲连接中多余的(大于最小连接数的)连接
  764.         long cc=countConn-minConn;
  765.         i=0;
  766.         while(i1){
  767.             try{
  768.                 connobj=(ConnectionObject)freeConns.get(0);
  769.                 connobj.close();
  770.                 connobj=null;
  771.                 freeConns.removeElementAt(0);
  772.                 countConn--;
  773.                 log("run--删除 从空闲连接中多余的(大于最小连接数的)连接 ");
  774.             }catch(SQLException e){
  775.                 log(e,"run--从空闲连接中多余的(大于最小连接数的)连接--失败");
  776.             }
  777.             i++;
  778.         }
  779.         //增加连接 保持要求的最小连接数
  780.         if(cc<0){
  781.             cc=-cc;
  782.             log("End One Part/r/n");
  783.             log("run--增加连接 保持要求的最小连接数");
  784.             for(i=0;i                newConnection();
  785.             }
  786.         }
  787.         //增加连接 保持至少有一个可用连接
  788.         if(freeConns.size()<1){
  789.             log("End One Part/r/n");
  790.             log("run--增加连接 保持至少有一个可用连接");
  791.             newConnection();
  792.         }
  793.         log("run--once");
  794.     //    notifyAll();
  795.     }
  796.     /**
  797.     * 将文本信息写入日志文件
  798.     */
  799.     private void log(String msg) {
  800.         msg="POOLMSG:Name:["+name+"];Msg:"+msg;
  801.         ConnectionManager.log(msg);
  802.     }
  803.  
  804.     /**
  805.     * 将文本信息与异常写入日志文件
  806.     */
  807.     private void log(Throwable e, String msg) {
  808.         msg="POOLERR:Name:["+name+"];Msg:"+msg;
  809.         ConnectionManager.log(e,msg);
  810.     }
  811. }
  812.  
  813. //file : *****< PoolInfoObject .java >****
  814.  
  815. /***************连接池信息类***********/
  816.  
  817. package com.dbaccess.dbpool;
  818.  
  819. import java.util.*;
  820.  
  821.  
  822. public class PoolInfoObject {
  823.     
  824.     public String poolName="";
  825.     
  826.     public String url="";
  827.     public String user="";
  828.     public String password="";
  829.     public String optionConnection="";//连接选项
  830.     
  831.     public long maxConnection=0;//最大连接数
  832.     public long minConnection=0;// 最小连接数
  833.     
  834.     public long maxOnlineFreeTime=0;//最大在线空闲时间
  835.     public long maxNoneOnlineTime=0;//最大无人在线时间。超过此时间后仅保持一个连接
  836.     
  837.     public PoolInfoObject(String poolName,Properties props) throws Exception
  838.     {
  839.         this.poolName=poolName;
  840.         
  841.         url = props.getProperty(poolName + ".url");
  842.         
  843.         if (url == null) {
  844.             log("没有为连接池" + poolName + "指定URL");
  845.             throw new Exception("没有为连接池" + poolName + "指定URL");
  846.         }
  847.         
  848.         this.user = props.getProperty(poolName + ".user");
  849.         this.password = props.getProperty(poolName + ".password");
  850.         this.optionConnection=props.getProperty(poolName+".option","");
  851.         
  852.         String maxconn = props.getProperty(poolName + ".maxconn""0");
  853.         String minconn = props.getProperty(poolName + ".minconn""10");
  854.         
  855.         String maxoft=props.getProperty(poolName + ".maxonlinefreetime""30");
  856.         String maxnot=props.getProperty(poolName + ".maxnoneonlinetime""180");
  857.         
  858.         try {
  859.             maxConnection = Long.valueOf(maxconn).longValue();
  860.             
  861.         }
  862.         catch (NumberFormatException e) {
  863.             log("错误的最大连接数限制: " + maxconn + "  连接池: " + poolName);
  864.             log("使用默认值 0(无限制)");
  865.             maxConnection = 0;
  866.         }
  867.         
  868.         try {
  869.             minConnection = Long.valueOf(minconn).longValue();
  870.             
  871.         }
  872.         catch (NumberFormatException e) {
  873.             log("错误的最小连接数限制: " + minconn + "  连接池: " + poolName);
  874.             log("使用默认值 10 个");
  875.             minConnection = 10;
  876.         }
  877.         
  878.         
  879.         try {
  880.             maxOnlineFreeTime =Long.valueOf(maxoft).longValue()*60*1000;
  881.             
  882.         }
  883.         
  884.         catch (NumberFormatException e) {
  885.             log("错误的最大在线空闲时间: " + maxoft + " 连接池: " + poolName);
  886.             log("使用默认值 30 分钟");
  887.             
  888.             maxOnlineFreeTime = 30*60*1000;
  889.         }
  890.         
  891.         try {
  892.             
  893.             maxNoneOnlineTime = Long.valueOf(maxnot).longValue()*60*1000;
  894.             
  895.         }
  896.         
  897.         catch (NumberFormatException e) {
  898.             log("错误的最大空闲时间:  " + maxnot + " 连接池: " + poolName);
  899.             log("使用默认值 1 小时");
  900.             maxNoneOnlineTime = 60*60*1000;
  901.         }
  902.         
  903.     }
  904.     /**
  905.     * 将文本信息写入日志文件
  906.     */
  907.     private void log(String msg) {
  908.         msg="POOLMSG:Name:["+poolName+"];Msg:"+msg;
  909.         ConnectionManager.log(msg);
  910.     }
  911.     
  912.     /**
  913.     * 将文本信息与异常写入日志文件
  914.     */
  915.     private void log(Throwable e, String msg) {
  916.         msg="POOLERR:Name:["+poolName+"];Msg:"+msg;
  917.         ConnectionManager.log(e,msg);
  918.     }
  919.  
  920. }
  921.  
  922.  
  923. //file : *****< db.properties >****#pool info
  924. //典型的连接池配置文件
  925. #日志文件
  926. logfile=log.txt
  927.  
  928. #检测连接的频率 单位:分钟次
  929. checkperiod=5
  930. #如果有多个驱动可以以“:”分开如drivers=com.mysql.jdbc.Driver:com.oracle.jdbc.Driver
  931. drivers=com.mysql.jdbc.Driver
  932.  
  933. #指明你的数据库所使用的字符集(新添项)
  934. mysql.character=gb2312
  935.  
  936. mysql.url=jdbc:mysql://localhost:3306/test?
  937.  
  938. mysql.option=useUnicode=true&&characterEncoding=GB2312&autoReconnect=true
  939. mysql.user=zxg
  940. mysql.password=333444
  941.  
  942. #连接池数量限制
  943. mysql.maxconn=0
  944. mysql.minconn=10
  945.  
  946. #超时参数 单位:分钟
  947.  
  948. #最大在线空闲时间
  949. mysql.maxonlinefreetime=30
  950.  
  951. #最大无人在线时间
  952. mysql.maxnoneonlinetime=60

最近,本人着手开发要有一个有强大后台的网站,在使用连接池时,觉得使用服务器自带的连接池总有些受限制。同时,为了加深对Java的学习和研究。写下了下面的连接池类。
该连接池主要有一下功能;

1)初始化一次,到处使用。
2)强大的日志功能,记录每一个sql动作,包括Connection、ResultSet 和Statement
3)根据连接的数量,定时自动回收已经释放或超时的连接。
4)配置灵活,可以使用各种JDBC驱动程序,支持多驱动程序。

更新说明:
1)新增了字符集配置项。
2) 新增了调试开关,便于使用前调试。
3)更改了日志日期输出格式。
4)排除了createStatement l里的一个bug (感谢rouselion的使用反馈)
5)修正了被封装类的getXXX函数的潜在的问题。

源代码:

  1.  
  2. //file : *****< ConnectionManager.java >****
  3. /*
  4. *  @Title  连接池
  5. *  @Author: zxg
  6. *  @Version 2.5
  7. *  @Memo:定义数据库连接及其数据库连接池等
  8. */
  9. package com.dbaccess.dbpool;
  10.  
  11. import java.io.*;
  12. import java.sql.*;
  13. import java.util.*;
  14.  
  15. import com.mysql.jdbc.Driver;
  16.  
  17.  
  18. public class ConnectionManager {
  19.  
  20.     static private ConnectionManager instance; // 唯一实例
  21. //    static private int clients;
  22.     static private long checkperiod=0;
  23.     private Vector drivers = new Vector();
  24.     private Hashtable pools = new Hashtable();
  25.     private Timer checkConnTimer=new Timer();
  26.  
  27.     //调试模式开关
  28.     private static boolean debug=false;
  29.     
  30.     //字符集哈西表
  31.     static private Hashtable Characters= new Hashtable();
  32.  
  33.     static private PrintWriter log;
  34.     /**
  35.     * 返回唯一实例.如果是第一次调用此方法,则创建实例
  36.     *
  37.     * @return ConnectionManager 唯一实例
  38.     */
  39.     static synchronized public ConnectionManager getInstance() {
  40.         if (instance == null) {
  41.             instance = new ConnectionManager();
  42.         }
  43. //        clients++;
  44.         return instance;
  45.     }
  46.  
  47.     /**
  48.     * 建构函数私有以防止其它对象创建本类实例
  49.     */
  50.     private ConnectionManager() {
  51.         init();
  52.     }
  53.  
  54.     /**
  55.     * 读取属性完成初始化
  56.     */
  57.     private void init() {
  58.         Properties    dbProps=null;
  59.         try {
  60.  
  61.             InputStream    is =  getClass().getResourceAsStream("db.properties");
  62.  
  63.             dbProps = new Properties();
  64.  
  65.             dbProps.load(is);
  66.         }
  67.         catch (Exception e) {
  68.             e.printStackTrace();
  69.             System.err.println("不能读取属性文件= " +
  70.             "请确保db.properties在CLASSPATH指定的路径中");
  71.             return;
  72.         }
  73.         String logFile = dbProps.getProperty("logfile""log.txt");
  74.  
  75.         String logPath=System.getProperty("user.dir");
  76.  
  77.         if(!logPath.endsWith("/"))logPath=logPath+"/";
  78.  
  79.         logFile=logPath+logFile;
  80.         if(debug){
  81.             System.out.println(logFile);
  82.             System.out.println("===============DEBUG====================");
  83.         }
  84.         try {
  85.             log = new PrintWriter(new FileWriter(logFile, true), true);
  86.         }
  87.         catch (IOException e) {
  88.             System.err.println("无法打开日志文件: " + logFile);
  89.             log = new PrintWriter(System.err);
  90.         }
  91.  
  92.         String ckPeriod=dbProps.getProperty("checkperiod","5");
  93.  
  94.         try {
  95.              checkperiod= Long.valueOf(ckPeriod).longValue()*60*1000;
  96.  
  97.         }
  98.         catch (NumberFormatException e) {
  99.             log("错误的最大连接数限制: " + ckPeriod + "  连接池: ");
  100.             log("使用默认值 5 分钟");
  101.             checkperiod = 5*60*1000;
  102.         }
  103.  
  104.         loadDrivers(dbProps);
  105.         createPools(dbProps);
  106.     }
  107.  
  108.     /**
  109.     * 装载和注册所有JDBC驱动程序
  110.     *
  111.     * @param props 属性
  112.     */
  113.     private void loadDrivers(Properties props) {
  114.         String driverClasses = props.getProperty("drivers");
  115.         StringTokenizer st = new StringTokenizer(driverClasses);
  116.         while (st.hasMoreElements()) {
  117.             String driverClassName = st.nextToken().trim();
  118.  
  119.             try {
  120.  
  121.                 Driver driver = (Driver)Class.forName(driverClassName).newInstance();
  122.                 if(driver!=null){
  123.                     DriverManager.registerDriver(driver);
  124.                     drivers.addElement(driver);
  125.                     log("Begin");
  126.                     log("成功注册JDBC驱动程序" + driverClassName);
  127.                 }
  128.                 else{
  129.                     log("Begin");
  130.                     log("注册JDBC驱动程序" + driverClassName+"失败");
  131.                 }
  132.  
  133.             }
  134.             catch (Exception e) {
  135.                 log("Begin");
  136.                 log("无法注册JDBC驱动程序: " + driverClassName + ", 错误: " + e);
  137.             }
  138.         }
  139.     }
  140.  
  141.     /**
  142.     * 根据指定属性创建连接池实例.
  143.     *
  144.     * @param props 连接池属性
  145.     */
  146.     private void createPools(Properties props) {
  147.  
  148.         Enumeration propNames = props.propertyNames();
  149.  
  150.         while (propNames.hasMoreElements()) {
  151.  
  152.             String name = (String) propNames.nextElement();
  153.             if (name.endsWith(".url")) {
  154.  
  155.                 String poolName=name.substring(0,name.indexOf("."));
  156.                 //记录该连接池的字符集设置
  157.                 String character=props.getProperty(poolName+".character","gb2312");
  158.  
  159.                 Characters.put(poolName,character);
  160.  
  161.                 try{
  162.  
  163.                     PoolInfoObject pio=new PoolInfoObject(poolName,props);
  164.  
  165.                     ConnectionPool  pool = new ConnectionPool(pio);
  166.  
  167.                     pools.put(poolName, pool);
  168.  
  169.                     //1分钟后开始每隔checkperiod分钟检查一次连接池情况
  170.  
  171.                     checkConnTimer.schedule(pool,60*1000,checkperiod);
  172.  
  173.                     log("成功创建连接池" + poolName);
  174.  
  175.                 }catch(Exception e){
  176.                     log(e,"创建DBConnectionPool出错");
  177.                 }
  178.             }
  179.         }
  180.     }
  181.  
  182.     /**
  183.     * 将连接对象返回给由名字指定的连接池
  184.     *
  185.     * @param name 在属性文件中定义的连接池名字
  186.     * @param con 连接对象
  187.     */
  188.     public void freeConnection(String name, Connection conn) {
  189.         ConnectionPool pool = (ConnectionPool) pools.get(name);
  190.         if (pool != null) {
  191.             pool.freeConnection(conn);
  192.         }
  193.     }
  194.  
  195.     /**
  196.     * 获得一个可用的(空闲的)连接.如果没有可用连接,且已有连接数小于最大连接数
  197.     * 限制,则创建并返回新连接
  198.     *
  199.     * @param name 在属性文件中定义的连接池名字
  200.     * @return Connection 可用连接或null
  201.     */
  202.     public Connection getConnection(String name) {
  203.         ConnectionPool pool = (ConnectionPool) pools.get(name);
  204.         if (pool != null) {
  205.             return pool.getConnection();
  206.         }
  207.         return null;
  208.     }
  209.  
  210.     /**
  211.     * 获得一个可用连接.若没有可用连接,且已有连接数小于最大连接数限制,
  212.     * 则创建并返回新连接.否则,在指定的时间内等待其它线程释放连接.
  213.     *
  214.     * @param name 连接池名字
  215.     * @param time 以毫秒计的等待时间
  216.     * @return Connection 可用连接或null
  217.     */
  218.     public Connection getConnection(String name, long time) {
  219.         ConnectionPool pool = (ConnectionPool) pools.get(name);
  220.         if (pool != null) {
  221.             return pool.getConnection(time);
  222.         }
  223.         return null;
  224.     }
  225.  
  226.     /**
  227.     * 关闭所有连接,撤销驱动程序的注册
  228.     */
  229.     public synchronized void release() {
  230.  
  231.     // 等待直到最后一个客户程序调用
  232. //        if (--clients != 0) {
  233. //            return;
  234. //        }
  235.  
  236.         checkConnTimer.cancel();
  237.  
  238.         Enumeration allPools = pools.elements();
  239.         while (allPools.hasMoreElements()) {
  240.             ConnectionPool pool = (ConnectionPool) allPools.nextElement();
  241.             pool.cancel();
  242.             pool.release();
  243.         }
  244.         Enumeration allDrivers = drivers.elements();
  245.         while (allDrivers.hasMoreElements()) {
  246.             Driver driver = (Driver) allDrivers.nextElement();
  247.             try {
  248.                 DriverManager.deregisterDriver(driver);
  249.                 log("撤销JDBC驱动程序 " + driver.getClass().getName()+"的注册");
  250.             }
  251.             catch (SQLException e) {
  252.                 log(e,"无法撤销下列JDBC驱动程序的注册: " + driver.getClass().getName());
  253.             }
  254.         }
  255.  
  256.     }
  257.     /**
  258.      * 取字符集设置参数。
  259.      * @param poolName
  260.      * @return String
  261.     */
  262.     static public String getCharacter(String poolName) {
  263.  
  264.         return (String)Characters.get(poolName);
  265.     }
  266.  
  267.     /**
  268.     * 将文本信息写入日志文件
  269.     */
  270.     static public void log(String msg) {
  271.         if(debug==trueSystem.out.println( getLocalTime()+ " " + msg);
  272.         else log.println( getLocalTime()+ " " + msg);
  273.     }
  274.  
  275.     /**
  276.     * 将文本信息与异常写入日志文件
  277.     */
  278.     static public void log(Throwable e, String msg) {
  279.  
  280.         if(debug==true){
  281.             System.err.println( getLocalTime()+ " " + msg);
  282.             e.printStackTrace(log);
  283.         }else{
  284.             log.println(getLocalTime() + " " + msg);
  285.             e.printStackTrace(log);
  286.         }
  287.     }
  288.     /**
  289.      * 获取当前系统时间
  290.      * @return String
  291.      */
  292.     private static  String getLocalTime(){
  293.  
  294.         int tt[];
  295.  
  296.         String TT[];
  297.  
  298.         tt= new int[6];
  299.         TT=new String[6];
  300.  
  301.         Calendar cl=Calendar.getInstance();
  302.  
  303.         tt[5]=cl.get(Calendar.SECOND);
  304.          tt[4]=cl.get(Calendar.MINUTE);
  305.           tt[3]=cl.get(Calendar.HOUR_OF_DAY);
  306.            tt[2]=cl.get(Calendar.DAY_OF_MONTH);
  307.         tt[1]=cl.get(Calendar.MONTH)+1;
  308.         tt[0]=cl.get(Calendar.YEAR);
  309.  
  310.  
  311.         for(int i=0;i<6 ;i++){
  312.             if(tt[i]<10)
  313.                 TT[i]="0"+Integer.toString(tt[i]);
  314.             else
  315.                 TT[i]=Integer.toString(tt[i]);
  316.  
  317.         }
  318.  
  319.         String t="";
  320.  
  321.         //连接日期                        //连接时间
  322.         t+=TT[0]+"-"+TT[1]+"-"+TT[2]+" "+TT[3]+":"+TT[4]+":"+TT[5];
  323.  
  324.         return t;
  325.     }
  326. //test==========================================test
  327.     /**
  328.      * Method main
  329.      *
  330.      *
  331.      * @param args
  332.      *
  333.      */
  334.     public static void main(String[] args) {
  335.         ConnectionManager dcm=null;
  336.  
  337.         try{
  338.             dcm=ConnectionManager.getInstance();
  339.  
  340.             Connection conn=dcm.getConnection("mysql");
  341.             Connection conn1=dcm.getConnection("mysql");
  342.             Connection conn2=dcm.getConnection("mysql");
  343.  
  344.  
  345.             ResultSet rs;
  346.             System.out.println("start");
  347.             String sql="select * from user ";
  348.  
  349.             System.out.println(":::::::::1");
  350.             Statement st=conn.createStatement();
  351.             if(st==null) {
  352.                 ConnectionManager.log("main--error while get /"Statement/"");
  353.                 return;            }
  354.             System.out.println("执行第"+1+"次检索");
  355.             rs=st.executeQuery(sql);
  356.             if(rs==null){
  357.                 ConnectionManager.log("main--error while get /"ResultSet/"");
  358.                 return;
  359.             }
  360.             System.out.println("/r/n");
  361.  
  362.             while(rs.next()){
  363.                 System.out.println(rs.getString(1));
  364.                 System.out.println(rs.getString(2));
  365.                 System.out.println(rs.getString(3));
  366.                 System.out.println();
  367.  
  368.             }
  369.  
  370.             rs.close();
  371.             st.close();
  372.             //rs=null;
  373.             //st=null;
  374.             System.out.println(":::::::::2");
  375.  
  376.             st = conn.createStatement();
  377.  
  378.  
  379.             System.out.println("执行第"+2+"次检索");
  380.             sql="select * from user";
  381.             rs=st.executeQuery(sql);
  382.             if(rs==null){
  383.                 ConnectionManager.log("main--error while get /"ResultSet/"");
  384.                 return;
  385.             }
  386.  
  387.             while(rs.next()){
  388.                 System.out.println("TTT:::TTT");
  389.                 System.out.println(rs.getString(1));
  390.                 System.out.println(rs.getString(2));
  391.                 System.out.println(rs.getString(3));
  392.                 System.out.println();
  393.  
  394.             }
  395.  
  396.             rs.close();
  397.             st.close();
  398.             rs=null;
  399.             st=null;
  400.  
  401.        dcm.freeConnection("mysql",conn);
  402.           
  403.             conn1.close();
  404.             conn2.close();
  405.  
  406.  
  407.             conn=null;
  408.             conn1=null;
  409.             conn2=null;
  410.             dcm.release();
  411.  
  412.  
  413.         }catch(SQLException e){
  414.             ConnectionManager.log(e,"main--error");
  415.         }
  416.     }
  417. }
  418. //===========================================================================//
  419.  
  420. //file : *****< ConnectionPool.java >****
  421.  
  422. /***************连接池类***********/
  423.  
  424. /**
  425. * 此类定义了一个连接池.它能够根据要求创建新连接,直到预定的最
  426. * 大连接数为止.在返回连接给客户程序之前,它能够验证连接的有效性.
  427. * 它继承自 TimerTask 被 ConnectionManager 类的timer成员调度
  428. */
  429. package com.dbaccess.dbpool;
  430.  
  431. import java.sql.*;
  432. import java.util.*;
  433. import java.util.Date;
  434.  
  435. public class ConnectionPool extends TimerTask{
  436.  
  437.     private int countConn;
  438.  
  439.     private Vector freeConns = new Vector();
  440.     private Vector usingConns = new Vector();
  441.  
  442.     private long maxUseTime;//使用中的连接最大空闲时间
  443.     private long maxFreeTime;//空闲的连接最大空闲时间(在连接数未小于最小连接数时,关闭此连接)
  444.  
  445.     private long maxConn;//最大连接数
  446.     private long minConn;//最小连接数
  447.     private long OnlineFreeTime ;//最大在线时间
  448.     private long maxNoneOnlineTime;//无人在线最大保留时间
  449.  
  450.     private String name;//连接池名(name)
  451.  
  452.     private String url;
  453.     private String user;
  454.     private String password;
  455.     private String option;
  456.  
  457.     /**
  458.     * 创建新的连接池
  459.     *
  460.     * @param pio 连接池信息
  461.     */
  462.     public ConnectionPool(PoolInfoObject pio) {
  463.  
  464.         this.name = pio.poolName;
  465.         this.url = pio.url;
  466.  
  467.         this.user = pio.user;
  468.         this.password = pio.password;
  469.         this.option=pio.optionConnection;
  470.  
  471.         this.maxConn = pio.maxConnection;
  472.         this.minConn = pio.minConnection;
  473.  
  474.         this.OnlineFreeTime = pio.maxOnlineFreeTime;
  475.         this.maxNoneOnlineTime = pio.maxNoneOnlineTime;
  476.  
  477.         if(this.minConn<=0) this.minConn=10;
  478.  
  479.         log("End One Part/r/n");
  480.         for(int i=0; i            newConnection();
  481.         }
  482.     }
  483.     /**
  484.     * 将新建的连接添加到连接池
  485.     *
  486.     * @param connobj 新建的连接
  487.     */
  488.     public synchronized void freeConnection(ConnectionObject connobj) {
  489.         // 将指定连接加入到向量末尾
  490.         try{
  491.             connobj.setInUse(false);
  492.             freeConns.addElement(connobj);
  493.             usingConns.removeElement(connobj);
  494.  
  495.             log("成功记录一个新建连接或者回收一个已释放连接");
  496.             notifyAll();
  497.         }catch(ArrayIndexOutOfBoundsException e){
  498.             log(e,"freeConnection(ConnectionObject connobj) --失败");
  499.  
  500.         }
  501.     }
  502.     /**
  503.     * 将不再使用的连接返回给连接池
  504.     *
  505.     * @param conn 客户程序主动释放的连接
  506.     */
  507.     public synchronized void freeConnection(Connection conn) {
  508.         // 将指定连接加入到向量末尾
  509.         ConnectionObject connobj=null;
  510.         int i;
  511.  
  512.         for(i=0;i        {
  513.             connobj=(ConnectionObject)usingConns.get(i);
  514.  
  515.             if(connobj.getConnection(false)==conn)
  516.                 break;
  517.         }
  518.  
  519.         if(i            try{
  520.                 connobj.setInUse(false);
  521.                 freeConns.addElement(connobj);
  522.                 usingConns.removeElement(connobj);
  523.                 log("客户程序主动释放连接--成功回收一个连接");
  524.                 notifyAll();
  525.             }catch(Exception e){
  526.                 log(e,"客户程序主动释放连接--回收一个连接--失败");
  527.             }
  528.         }
  529.     }
  530.  
  531.     /**
  532.     * 从连接池获得一个可用连接.如没有空闲的连接且当前连接数小于最大连接
  533.     * 数限制,则创建新连接.如原来登记为可用的连接不再有效,则从向量删除之,
  534.     * 然后递归调用自己以尝试新的可用连接.
  535.     * @return Connection
  536.     */
  537.     public synchronized Connection getConnection() {
  538.         ConnectionObject connobj = null;
  539.         Connection conn=null;
  540.         // 获取向量中第一个可用连接
  541.         try {
  542.             connobj = (ConnectionObject) freeConns.get(0);
  543.  
  544.         }
  545.         catch (Exception e) {
  546.  
  547.             log("End One Part/r/n");
  548.  
  549.             log("从连接池" + name+"获取一个连接失败");
  550.             if( maxConn == 0 || countConn < maxConn) {
  551.                 connobj = newConnection();
  552.                 conn=connobj.getConnection(true);
  553.             }
  554.         }
  555.         //如没有空闲的连接且当前连接数小于最大连接数限制,则创建新连接
  556.         if(connobj==null && ( maxConn == 0 || countConn < maxConn)) {
  557.             log("从连接池" + name+"获取一个连接失败");
  558.             log("End One Part/r/n");
  559.             connobj = newConnection();
  560.             conn=connobj.getConnection(true);
  561.         }else if(connobj!=null)
  562.         {
  563.             conn=connobj.getConnection(false);
  564.             if (conn==null) conn=connobj.getConnection(true);
  565.         }
  566.  
  567.         if (conn != null) {
  568.  
  569.             connobj.setLastAccessTime(new Date().getTime());
  570.             connobj.setInUse(true);
  571.  
  572.             usingConns.addElement(connobj);
  573.             freeConns.removeElementAt(0);
  574.  
  575.  
  576.             return conn;
  577.  
  578.         }else{
  579.             log("获取连接" + name+"失败--连接数量已达最大上限");
  580.             return null;
  581.         }
  582.     }
  583.  
  584.     /**
  585.     * 从连接池获取可用连接.可以指定客户程序能够等待的最长时间
  586.     * 参见前一个getConnection()方法.
  587.     *
  588.     * @param timeout 以毫秒计的等待时间限制
  589.     */
  590.     public synchronized Connection getConnection(long timeout) {
  591.  
  592.         long startTime = new Date().getTime();
  593.         Connection conn=null;
  594.         while ((conn = getConnection()) == null) {
  595.             try {
  596.                 wait(timeout);//??????????????
  597.             }
  598.             catch (InterruptedException e){
  599.  
  600.             }
  601.             if ((new Date().getTime() - startTime) >= timeout) {
  602.                 // wait()返回的原因是超时?????????
  603.                 return null;
  604.             }
  605.         }
  606.         return conn;
  607.     }
  608.  
  609.     /**
  610.     * 关闭所有连接
  611.     */
  612.     public synchronized void release() {
  613. //        cancel();
  614.         Enumeration allConnections = freeConns.elements();
  615.  
  616.         while (allConnections.hasMoreElements()) {
  617.             ConnectionObject connobj = (ConnectionObject) allConnections.nextElement();
  618.             try {
  619.                 connobj.close();
  620.                 connobj=null;
  621.                 log("关闭连接池" + name+"中的一个连接");
  622.             }
  623.             catch (SQLException e) {//SQLException
  624.                 log(e, "无法关闭连接池" + name+"中的连接");
  625.             }
  626.         }
  627.         freeConns.removeAllElements();
  628.         //
  629.         allConnections = usingConns.elements();
  630.  
  631.         while (allConnections.hasMoreElements()) {
  632.             ConnectionObject connobj = (ConnectionObject) allConnections.nextElement();
  633.             try {
  634.                 connobj.close();
  635.                 connobj=null;
  636.                 log("关闭连接池" + name+"中的一个连接");
  637.             }
  638.             catch (SQLException e) {//SQLException
  639.                 log(e, "无法关闭连接池" + name+"中的连接");
  640.             }
  641.         }
  642.         usingConns.removeAllElements();
  643.     }
  644.  
  645.     /**
  646.     * 创建新的连接
  647.     */
  648.     private ConnectionObject newConnection() {
  649.         ConnectionObject connobj= null;
  650.         try {
  651.  
  652.             log("连接池" + name+"创建一个新的连接对象");
  653.  
  654.             String URL=url+option;
  655.  
  656.              log("URL=" +URL );
  657.  
  658.              Connection conn = DriverManager.getConnection(URL,user,password);
  659.  
  660.             connobj=new ConnectionObject(conn,false);
  661.  
  662.             connobj.setPoolName(name);
  663.  
  664.             freeConnection(connobj);
  665.  
  666.             countConn++;
  667.  
  668.         }
  669.         catch (SQLException e) {
  670.             log(e, "无法创建下列URL的连接: " + url+" for User= " +user+" Password="+password);
  671.             return null;
  672.         }
  673.         return connobj;
  674.     }
  675.     //检查各连接状态(每checkperiod分钟一次)
  676.     
  677.     public synchronized void run (){
  678.  
  679.         ConnectionObject connobj=null;
  680.         //是否在maxNoneOnlineTime长时间内没有任何连接被使用。
  681.         if(usingConns.size()==0 && (freeConns.size()==minConn || countConn==1)){
  682.  
  683.             boolean bCanFree=true;
  684.             if(countConn==1)
  685.                 return;
  686.             else{
  687.                 for(int j=0;j                    connobj=(ConnectionObject)freeConns.get(j);
  688.  
  689.                     long lt=new Date().getTime()-connobj.getLastAccessTime();
  690.  
  691.                     if(lt                        bCanFree=false;
  692.                         break;
  693.                     }
  694.                 }
  695.             }
  696.  
  697.             if(bCanFree){
  698.                 log("run--连接池里的连接太闲--现在关闭部分连接,保持一个连接 ");
  699.                 
  700.                 while(freeConns.size()>1){
  701.                     try{
  702.                         connobj=(ConnectionObject)freeConns.get(0);
  703.                         connobj.close();
  704.                         connobj=null;
  705.                         freeConns.removeElementAt(0);
  706.                         countConn--;
  707.                     }catch(SQLException e){
  708.                         log(e,"run--连接池的连接太闲--关闭部分连接,保持一个连接--失败");
  709.                     }
  710.                 }
  711.                 log("关闭连接数量"+Long.toString(minConn-1));
  712.             }
  713.             return;
  714.         }
  715.         //回收 正在使用中的已经"关闭"(释放)的连接 和 使用时间已经超时的连接
  716.  
  717.         int i=0;
  718.  
  719.         while(i
  720.              connobj=(ConnectionObject)usingConns.get(i);
  721.             if(connobj.isInUse()==false){
  722.                 try{
  723.                         log("run--回收 正在使用中的已经/"关闭/"(释放)的连接");
  724.                         freeConnection(connobj);
  725.                         i--;
  726.                 }catch(ArrayIndexOutOfBoundsException e){
  727.                     log(e,"run--回收 正在使用中的已经/"关闭/"(释放)的连接--失败");
  728.                 }
  729.             }else{
  730.  
  731.                 long nowtime=new Date().getTime();
  732.                 long t=nowtime-connobj.getLastAccessTime();
  733.                 try{
  734.                     if(t>OnlineFreeTime){//超时时间为OnlineFreeTime分钟
  735.                         log("run--回收 使用时间已经超时的连接");
  736.                         freeConnection(connobj);
  737.                         i--;
  738.                     }
  739.                 }catch(ArrayIndexOutOfBoundsException e ){
  740.                     log(e,"run--回收 使用时间已经超时的连接--失败");
  741.                 }
  742.             }
  743.             i++;
  744.         }
  745.         //删除 空闲的已经被意外关闭的连接
  746.         i=0;
  747.         while(i
  748.             connobj= (ConnectionObject)freeConns.get(i);
  749.             try{
  750.                 if(connobj.isClosed()){
  751.                     connobj=null;
  752.                     freeConns.removeElementAt(i);
  753.                     countConn--;
  754.                     i--;
  755.                     log("run--删除 空闲的已经被意外关闭的连接");
  756.                 }
  757.             }catch(Exception e){
  758.                 log(e,"run--删除 空闲的已经被意外关闭的连接-失败");
  759.             }
  760.             i++;
  761.         }
  762.  
  763.         //删除 从空闲连接中多余的(大于最小连接数的)连接
  764.         long cc=countConn-minConn;
  765.         i=0;
  766.         while(i1){
  767.             try{
  768.                 connobj=(ConnectionObject)freeConns.get(0);
  769.                 connobj.close();
  770.                 connobj=null;
  771.                 freeConns.removeElementAt(0);
  772.                 countConn--;
  773.                 log("run--删除 从空闲连接中多余的(大于最小连接数的)连接 ");
  774.             }catch(SQLException e){
  775.                 log(e,"run--从空闲连接中多余的(大于最小连接数的)连接--失败");
  776.             }
  777.             i++;
  778.         }
  779.         //增加连接 保持要求的最小连接数
  780.         if(cc<0){
  781.             cc=-cc;
  782.             log("End One Part/r/n");
  783.             log("run--增加连接 保持要求的最小连接数");
  784.             for(i=0;i                newConnection();
  785.             }
  786.         }
  787.         //增加连接 保持至少有一个可用连接
  788.         if(freeConns.size()<1){
  789.             log("End One Part/r/n");
  790.             log("run--增加连接 保持至少有一个可用连接");
  791.             newConnection();
  792.         }
  793.         log("run--once");
  794.     //    notifyAll();
  795.     }
  796.     /**
  797.     * 将文本信息写入日志文件
  798.     */
  799.     private void log(String msg) {
  800.         msg="POOLMSG:Name:["+name+"];Msg:"+msg;
  801.         ConnectionManager.log(msg);
  802.     }
  803.  
  804.     /**
  805.     * 将文本信息与异常写入日志文件
  806.     */
  807.     private void log(Throwable e, String msg) {
  808.         msg="POOLERR:Name:["+name+"];Msg:"+msg;
  809.         ConnectionManager.log(e,msg);
  810.     }
  811. }
  812.  
  813. //file : *****< PoolInfoObject .java >****
  814.  
  815. /***************连接池信息类***********/
  816.  
  817. package com.dbaccess.dbpool;
  818.  
  819. import java.util.*;
  820.  
  821.  
  822. public class PoolInfoObject {
  823.     
  824.     public String poolName="";
  825.     
  826.     public String url="";
  827.     public String user="";
  828.     public String password="";
  829.     public String optionConnection="";//连接选项
  830.     
  831.     public long maxConnection=0;//最大连接数
  832.     public long minConnection=0;// 最小连接数
  833.     
  834.     public long maxOnlineFreeTime=0;//最大在线空闲时间
  835.     public long maxNoneOnlineTime=0;//最大无人在线时间。超过此时间后仅保持一个连接
  836.     
  837.     public PoolInfoObject(String poolName,Properties props) throws Exception
  838.     {
  839.         this.poolName=poolName;
  840.         
  841.         url = props.getProperty(poolName + ".url");
  842.         
  843.         if (url == null) {
  844.             log("没有为连接池" + poolName + "指定URL");
  845.             throw new Exception("没有为连接池" + poolName + "指定URL");
  846.         }
  847.         
  848.         this.user = props.getProperty(poolName + ".user");
  849.         this.password = props.getProperty(poolName + ".password");
  850.         this.optionConnection=props.getProperty(poolName+".option","");
  851.         
  852.         String maxconn = props.getProperty(poolName + ".maxconn""0");
  853.         String minconn = props.getProperty(poolName + ".minconn""10");
  854.         
  855.         String maxoft=props.getProperty(poolName + ".maxonlinefreetime""30");
  856.         String maxnot=props.getProperty(poolName + ".maxnoneonlinetime""180");
  857.         
  858.         try {
  859.             maxConnection = Long.valueOf(maxconn).longValue();
  860.             
  861.         }
  862.         catch (NumberFormatException e) {
  863.             log("错误的最大连接数限制: " + maxconn + "  连接池: " + poolName);
  864.             log("使用默认值 0(无限制)");
  865.             maxConnection = 0;
  866.         }
  867.         
  868.         try {
  869.             minConnection = Long.valueOf(minconn).longValue();
  870.             
  871.         }
  872.         catch (NumberFormatException e) {
  873.             log("错误的最小连接数限制: " + minconn + "  连接池: " + poolName);
  874.             log("使用默认值 10 个");
  875.             minConnection = 10;
  876.         }
  877.         
  878.         
  879.         try {
  880.             maxOnlineFreeTime =Long.valueOf(maxoft).longValue()*60*1000;
  881.             
  882.         }
  883.         
  884.         catch (NumberFormatException e) {
  885.             log("错误的最大在线空闲时间: " + maxoft + " 连接池: " + poolName);
  886.             log("使用默认值 30 分钟");
  887.             
  888.             maxOnlineFreeTime = 30*60*1000;
  889.         }
  890.         
  891.         try {
  892.             
  893.             maxNoneOnlineTime = Long.valueOf(maxnot).longValue()*60*1000;
  894.             
  895.         }
  896.         
  897.         catch (NumberFormatException e) {
  898.             log("错误的最大空闲时间:  " + maxnot + " 连接池: " + poolName);
  899.             log("使用默认值 1 小时");
  900.             maxNoneOnlineTime = 60*60*1000;
  901.         }
  902.         
  903.     }
  904.     /**
  905.     * 将文本信息写入日志文件
  906.     */
  907.     private void log(String msg) {
  908.         msg="POOLMSG:Name:["+poolName+"];Msg:"+msg;
  909.         ConnectionManager.log(msg);
  910.     }
  911.     
  912.     /**
  913.     * 将文本信息与异常写入日志文件
  914.     */
  915.     private void log(Throwable e, String msg) {
  916.         msg="POOLERR:Name:["+poolName+"];Msg:"+msg;
  917.         ConnectionManager.log(e,msg);
  918.     }
  919.  
  920. }
  921.  
  922.  
  923. //file : *****< db.properties >****#pool info
  924. //典型的连接池配置文件
  925. #日志文件
  926. logfile=log.txt
  927.  
  928. #检测连接的频率 单位:分钟次
  929. checkperiod=5
  930. #如果有多个驱动可以以“:”分开如drivers=com.mysql.jdbc.Driver:com.oracle.jdbc.Driver
  931. drivers=com.mysql.jdbc.Driver
  932.  
  933. #指明你的数据库所使用的字符集(新添项)
  934. mysql.character=gb2312
  935.  
  936. mysql.url=jdbc:mysql://localhost:3306/test?
  937.  
  938. mysql.option=useUnicode=true&&characterEncoding=GB2312&autoReconnect=true
  939. mysql.user=zxg
  940. mysql.password=333444
  941.  
  942. #连接池数量限制
  943. mysql.maxconn=0
  944. mysql.minconn=10
  945.  
  946. #超时参数 单位:分钟
  947.  
  948. #最大在线空闲时间
  949. mysql.maxonlinefreetime=30
  950.  
  951. #最大无人在线时间
  952. mysql.maxnoneonlinetime=60

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值