朴素的java数据库连接池实现(一)

在服务器端涉及数据库访问的应用程序里头,数据库连接池是一项确保性能的关键技术。一谈起java数据库连接池,大家都可以罗列出一堆开源实现。

它们也各有优劣:
1. DBCP  TOMCAT自带的
2. proxool

3. c3p0:  see:  http://sourceforge.net/projects/c3p0

4. DBPool
5. Taobao的druid

这些都是蛮不错的连接池实现。基于不用重复造轮子一说,完全可以挑一个自己喜欢的去用。已有的轮子如果没出问题,还好说,出了问题,往往很难解决。
本文的主旨是阐述连接池的基本思想:
1. 能将池化的资源(连接)控制在一定范围以内[min, max]
2. 能在jvm退出之前,释放这些连接资源
3. 能尽量简化用户的代码
4. 能确保连接池的连接始终是活跃的

下面还是上代码吧,  以下的代码是整理而成,缺点还是不少,但是也足够一般情况下的使用,至少是可控的。

1.  一个hook类,用于jvm退出前释放资源

[java]  view plain copy
  1. package com.hisql;  
  2.   
  3. import java.sql.SQLException;  
  4.   
  5. import org.apache.log4j.Logger;  
  6.   
  7.   
  8. public class ReleasePoolHook implements Runnable  
  9. {  
  10.     public static final Logger logger = Logger.getLogger(ReleasePoolHook.class);  
  11.     ConnectionPool connpool;  
  12.   
  13.     public ReleasePoolHook(ConnectionPool pool)  
  14.     {  
  15.         // register it  
  16.         connpool = pool;  
  17.         Runtime.getRuntime().addShutdownHook(new Thread(this));  
  18.         logger.info(">>> shutdown hook registered...");  
  19.     }  
  20.       
  21.     @Override  
  22.     public void run()  
  23.     {  
  24.         // TODO Auto-generated method stub  
  25.         logger.info("\n>>> About to execute: " + ReleasePoolHook.class.getName() + ".run() to clean up before JVM exits.");   
  26.         this.cleanUp();   
  27.         logger.info(">>> Finished execution: " + ReleasePoolHook.class.getName() + ".run()");   
  28.     }  
  29.       
  30.     private void cleanUp()  
  31.     {  
  32.         if (connpool != null)  
  33.         {  
  34.             try  
  35.             {  
  36.                 connpool.closeConnectionPool();  
  37.                 logger.info("Pool realeased....");  
  38.             }  
  39.             catch (SQLException e)  
  40.             {  
  41.                 // TODO Auto-generated catch block  
  42.                 e.printStackTrace();  
  43.                 logger.warn("Pool released with exception", e);  
  44.             }  
  45.               
  46.         }  
  47.     }  
  48.   
  49. }  

2. PooledConnection类, 用于对Connection接口类的一个封装
    很粗糙,标准的做法,应该是将Connection所有的方法都delegate,这样,用户用起来就蛮方便。这里只是为了阐述思想,如下:

[java]  view plain copy
  1. package com.hisql;  
  2.   
  3. import java.sql.Connection;  
  4.   
  5. public class PooledConnection  
  6. {  
  7.     Connection connection = null;  
  8.     boolean busy = false;  
  9.       
  10.     public PooledConnection(Connection connection)  
  11.     {  
  12.         this.connection = connection;  
  13.     }  
  14.       
  15.     public Connection getConnection()  
  16.     {  
  17.         return connection;  
  18.     }  
  19.       
  20.     public void setConnection(Connection connection)  
  21.     {  
  22.         this.connection = connection;  
  23.     }  
  24.       
  25.     public boolean isBusy()  
  26.     {  
  27.         return busy;  
  28.     }  
  29.       
  30.     public void setBusy(boolean busy)  
  31.     {  
  32.         this.busy = busy;  
  33.     }  
  34. }  

3.  ConnectionPool类

这是主体实现类:

[java]  view plain copy
  1. package com.hisql;  
  2.   
  3. import java.sql.*;  
  4. import java.util.*;  
  5.   
  6. import org.apache.log4j.Logger;  
  7.   
  8. public class ConnectionPool  
  9. {  
  10.     private String jdbcDriver;  
  11.     private String dbUrl;  
  12.     private String dbUsername;  
  13.     private String dbPassword;  
  14.     private String pingSql = "select 1"// the test sql statement to ping the target database  
  15.     private int minConnections = 5;  
  16.     private int incrementalConnections = 2;  
  17.     private int maxConnections = 20;  
  18.       
  19.     private Vector<PooledConnection> connections;  
  20.     private ReleasePoolHook hook;  
  21.       
  22.     public static final Logger logger = Logger.getLogger(ConnectionPool.class);  
  23.       
  24.     public ConnectionPool(String driver, String url, String username, String password)  
  25.     {  
  26.         jdbcDriver = driver;  
  27.         dbUrl = url;  
  28.         dbUsername = username;  
  29.         dbPassword = password;  
  30.         hook = new ReleasePoolHook(this);  
  31.     }  
  32.       
  33.     public int getInitialConnections()  
  34.     {  
  35.         return this.minConnections;  
  36.     }  
  37.       
  38.     public void setInitialConnections(int initialConnections)  
  39.     {  
  40.         this.minConnections = initialConnections;  
  41.     }  
  42.       
  43.     public int getIncrementalConnections()  
  44.     {  
  45.         return this.incrementalConnections;  
  46.     }  
  47.       
  48.     public void setIncrementalConnections(int incrementalConnections)  
  49.     {  
  50.         this.incrementalConnections = incrementalConnections;  
  51.     }  
  52.       
  53.     public int getMaxConnections()  
  54.     {  
  55.         return this.maxConnections;  
  56.     }  
  57.       
  58.     public void setMaxConnections(int maxConnections)  
  59.     {  
  60.         this.maxConnections = maxConnections;  
  61.     }  
  62.       
  63.     public String getPingSql()  
  64.     {  
  65.         return this.pingSql;  
  66.     }  
  67.       
  68.     public void setPingSql(String sql)  
  69.     {  
  70.         this.pingSql = sql;  
  71.     }  
  72.       
  73.     /** 
  74.      * intialize the pool 
  75.      * @throws Exception 
  76.      */  
  77.     public synchronized void initialize() throws Exception   
  78.     {  
  79.         if (connections != null)  
  80.         {  
  81.             return;  
  82.         }  
  83.           
  84.         Class.forName(this.jdbcDriver);  
  85.         connections = new Vector();  
  86.           
  87.         createConnections(this.minConnections);  
  88.     }  
  89.       
  90.     private void createConnections(int numConnections) throws SQLException  
  91.     {  
  92.         for (int i=0; i<numConnections; i++)  
  93.         {  
  94.             if (this.maxConnections > 0 && this.connections.size() >= this.maxConnections)  
  95.             {  
  96.                 break;  
  97.             }  
  98.             // add a new PooledConnection object  
  99.             try  
  100.             {  
  101.                 connections.addElement(new PooledConnection(newConnection()));  
  102.             }  
  103.             catch (SQLException e)  
  104.             {  
  105.                 logger.error("create connection failed: ", e);  
  106.                 throw new SQLException();  
  107.             }  
  108.             logger.info(" connection created ......");  
  109.         }  
  110.     }  
  111.       
  112.     private Connection newConnection() throws SQLException   
  113.     {  
  114.         Connection conn = DriverManager.getConnection(dbUrl, dbUsername, dbPassword);  
  115.         if (connections.size() == 0)  
  116.         {  
  117.             DatabaseMetaData metaData = conn.getMetaData();  
  118.             int driverMaxConnections = metaData.getMaxConnections();  
  119.               
  120.             if (driverMaxConnections > 0 && this.maxConnections > driverMaxConnections)  
  121.             {  
  122.                 this.maxConnections = driverMaxConnections;  
  123.             }  
  124.         }  
  125.         return conn;  
  126.     }  
  127.       
  128.     public synchronized Connection getConnection() throws SQLException  
  129.     {  
  130.         if (connections == null)  
  131.         {  
  132.             return null;  
  133.         }  
  134.           
  135.         Connection conn = getFreeConnection();  
  136.           
  137.         while (conn == null)  
  138.         {  
  139.             wait(250);  
  140.             conn = getFreeConnection();  
  141.         }  
  142.         return conn;  
  143.     }  
  144.       
  145.     private Connection getFreeConnection() throws SQLException  
  146.     {  
  147.         Connection conn = findFreeConnection();  
  148.         if (conn == null)  
  149.         {  
  150.             createConnections(incrementalConnections);  
  151.             conn = findFreeConnection();  
  152.             if (conn == null)  
  153.             {  
  154.                 return null;  
  155.             }  
  156.         }  
  157.         return conn;  
  158.     }  
  159.       
  160.     private Connection findFreeConnection() throws SQLException  
  161.     {  
  162.         Connection conn = null;  
  163.         PooledConnection pConn = null;  
  164.           
  165.         Iterator<PooledConnection> iter = connections.iterator();  
  166.         while (iter.hasNext())  
  167.         {  
  168.             pConn = (PooledConnection)iter.next();  
  169.             if (!pConn.isBusy())  
  170.             {  
  171.                 conn = pConn.getConnection();  
  172.                 pConn.setBusy(true);  
  173.                   
  174.                 if (!testConnection(conn))  
  175.                 {  
  176.                     try  
  177.                     {  
  178.                         conn = newConnection();  
  179.                     }  
  180.                     catch(SQLException e)  
  181.                     {  
  182.                         logger.error("create connection failed:", e);  
  183.                         return null;  
  184.                     }  
  185.                     pConn.setConnection(conn);  
  186.                 }  
  187.                 break;  
  188.             }  
  189.         }  
  190.         return conn;  
  191.     }  
  192.       
  193.     private boolean testConnection(Connection conn)  
  194.     {  
  195.         Statement stmt = null;  
  196.         ResultSet rset = null;  
  197.         try  
  198.         {  
  199.             stmt = conn.createStatement();  
  200.             rset = stmt.executeQuery(this.pingSql);  
  201.         }  
  202.         catch (SQLException ex)  
  203.         {  
  204.             closeConnection(conn);  
  205.             return false;  
  206.         }  
  207.         finally  
  208.         {  
  209.             try  
  210.             {  
  211.                 if (rset!= null) rset.close();  
  212.             }  
  213.             catch (SQLException ex) {}  
  214.             try  
  215.             {  
  216.                 if (stmt!= null) stmt.close();  
  217.             }  
  218.             catch (SQLException ex) {}  
  219.         }  
  220.         return true;  
  221.     }  
  222.       
  223.     public void returnConnection(Connection conn)  
  224.     {  
  225.         if (connections == null)  
  226.         {  
  227.             logger.warn("connection pool not exists.");  
  228.             return;  
  229.         }  
  230.         PooledConnection pConn = null;  
  231.         Enumeration enumerate = connections.elements();  
  232.         while (enumerate.hasMoreElements())  
  233.         {  
  234.             pConn = (PooledConnection)enumerate.nextElement();  
  235.             if (conn == pConn.getConnection())  
  236.             {  
  237.                 pConn.setBusy(false);  
  238.                 break;  
  239.             }  
  240.         }  
  241.     }  
  242.       
  243.     public synchronized void refreshConnections() throws SQLException  
  244.     {  
  245.         if (connections == null)  
  246.         {  
  247.             logger.warn("connection pool not exists, can't refresh...");  
  248.             return;  
  249.         }  
  250.           
  251.         PooledConnection pConn = null;  
  252.         Enumeration enumerate = connections.elements();  
  253.         while (enumerate.hasMoreElements())  
  254.         {  
  255.             pConn = (PooledConnection)enumerate.nextElement();  
  256.             if (pConn.isBusy())  
  257.             {  
  258.                 wait(5000);  
  259.             }  
  260.             closeConnection(pConn.getConnection());  
  261.             pConn.setConnection(newConnection());  
  262.             pConn.setBusy(false);  
  263.         }  
  264.     }  
  265.       
  266.     public synchronized void closeConnectionPool() throws SQLException  
  267.     {  
  268.         if (connections == null)  
  269.         {  
  270.             logger.warn("conneciton pool not exists, can't close..");  
  271.             return;  
  272.         }  
  273.           
  274.         PooledConnection pConn = null;  
  275.         Enumeration enumerate = connections.elements();  
  276.         while (enumerate.hasMoreElements())  
  277.         {  
  278.             pConn = (PooledConnection)enumerate.nextElement();  
  279.             if (pConn.isBusy())  
  280.             {  
  281.                 wait(5000);  
  282.             }  
  283.             closeConnection(pConn.getConnection());  
  284.             connections.removeElement(pConn);  
  285.         }  
  286.         connections = null;  
  287.     }  
  288.       
  289.     private void closeConnection(Connection conn)  
  290.     {  
  291.         try  
  292.         {  
  293.             conn.close();  
  294.         }  
  295.         catch (SQLException ex)  
  296.         {  
  297.             logger.warn("close connection error: ", ex);  
  298.         }  
  299.     }  
  300.       
  301.     private void wait(int mSeconds)  
  302.     {  
  303.         try  
  304.         {  
  305.             Thread.sleep(mSeconds);  
  306.         }  
  307.         catch (InterruptedException e)  
  308.         {  
  309.         }  
  310.     }  
  311. }  

使用方法:
ConnectionPool  connpool = new ConnectionPool("com.mysql.jdbc.Driver", "jdbc:mysql://localhost:3306/foo", "root", "******");
connpool.initialize();

Connection conn = connpool.getConnection();

try
{
 ......

}

catch ()
finally

{

     connpool.returnConnection(conn);

}

三个类只依赖于log4j-1.2.*.jar 和 commons-logging-*.jar,所以很容易构建一个工程。

改进思路:
1.  对PooledConnection类,增加相应的delegate方法,让用户看不到connpool.returnConnection()这类api。只需要关注connection.close(),不是真正的close()
2. 对pingConnection操作,提供一个工作线程,使其能确保连接池中所有的connection都是活连接。现实中,有些数据库如果空闲时间超时,比如1个小时,它是自动断开客户端连接的,这样会对应用产生负责影响。
3. 最好能自动收缩,如果连接长期不使用,可以进行物理释放,让真正打开的连接处于[min, max]之间。而不是达到max以后,物理连接就始终占用max个资源,这不尽合理。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值