Java jdbc数据库连接池完整实现R2的原理

 

Java jdbc数据库连接池完整实现

北京师范大学 计算机系

张人杰

 

前言:

网上流传着一些jdbc数据库连接池的实现,纵观所有,其操作方式、流程均对原有jdbc的操作方式、流程做了改变,也即在用完一个connection后,不能调用close方法对其关闭,而必须调用连接池对象的pool.free(connection)方法放回连接池pool。这在单个连接池时不显麻烦。但在多个连接池或连接池对象无法传入调用的方法时,释放连接就会显得非常麻烦。前段时间,出于兴趣,本人开发了一套jdbc的数据库连接池R2,在此与大家分享。

 

设计思想:

1、     基本思想为通过代理的方式,对原数据库真实连接conn对象进行包装,改写close方法,在Connection对象调用此方法时将真实连接conn放回连接池;

2、     在通过getConnection方法获得一个真实连接时,对其进行包装,返回R2Connection对象;

3、     R2Connection实现java.sql.Connection接口,完全参照jdbc标准,除close方法外,所有方法均直接调用真实连接conn进行操作。

4、     R2Pool为产生数据库连接的工厂类,处理:控制连接池中的连接数,定时清理空闲连接,保持连接等操作。

 

( 下载地址:http://download.csdn.net/user/ranjio_z )

 

代码讲解:

R2Connection类,对原真实Connection的包装

/**

 * R2 jdbc connection class, to replace the default jdbc connection and replace the close function

 * R2连接池,链接包装类,处理close方法

 * @author 张人杰 北京师范大学 计算机系

 * alex.zhangrj@hotmail.com

 * alex.zhangrj Beijing Normal University

 * 2011/11/26

 */

public class R2Connection implements Connection {

 

       private Connection conn=null;

       private R2Pool pool=null;

      

       public void setConnection(Connection jdbcConn){//真实连接对象

              this.conn=jdbcConn;

       }

       public void setPool(R2Pool pool){//连接来源(连接池)的引用

              this.pool=pool;

       }

      

       @Override

       public void clearWarnings() throws SQLException {

              if(conn!=null)conn.clearWarnings();

       }

 

       @Override

       public void close() throws SQLException {//关闭链接时,不实际关闭Connection,而把其放入连接池中

              synchronized(this){

                     if(conn!=null)pool.releaseConnection(conn);

                     conn=null;

              }

       }

       /**

        * 关闭jdbc真实链接

        * @throws SQLException

        */

       public void closeJdbcConnection() throws SQLException {//关闭jdbc真实链接

              if(conn!=null)conn.close();

              conn=null;

       }

 

       @Override

       public void commit() throws SQLException {

              if(conn!=null)conn.commit();

       }

 

       @Override

       public Array createArrayOf(String typeName, Object[] elements)

                     throws SQLException {

              if(conn!=null)return conn.createArrayOf(typeName, elements);

              return null;

       }

 

       @Override

       public Blob createBlob() throws SQLException {

              if(conn!=null)return conn.createBlob();

              return null;

       }

 

       @Override

       public Clob createClob() throws SQLException {

              if(conn!=null)return conn.createClob();

              return null;

       }

 

       @Override

       public NClob createNClob() throws SQLException {

              if(conn!=null)return conn.createNClob();

              return null;

       }

 

       @Override

       public SQLXML createSQLXML() throws SQLException {

              if(conn!=null)return conn.createSQLXML();

              return null;

       }

 

       @Override

       public Statement createStatement() throws SQLException {

              if(conn!=null)return conn.createStatement();

              return null;

       }

 

       @Override

       public Statement createStatement(int resultSetType, int resultSetConcurrency)

                     throws SQLException {

              if(conn!=null)return conn.createStatement(resultSetType,resultSetConcurrency);

              return null;

       }

 

       @Override

       public Statement createStatement(int resultSetType,

                     int resultSetConcurrency, int resultSetHoldability)

                     throws SQLException {

              if(conn!=null)return conn.createStatement(resultSetType,resultSetConcurrency,resultSetHoldability);

              return null;

       }

 

       @Override

       public Struct createStruct(String typeName, Object[] attributes)

                     throws SQLException {

              if(conn!=null)return conn.createStruct(typeName,attributes);

              return null;

       }

 

       @Override

       public boolean getAutoCommit() throws SQLException {

              if(conn!=null)return conn.getAutoCommit();

              return false;

       }

 

       @Override

       public String getCatalog() throws SQLException {

              if(conn!=null)return conn.getCatalog();

              return null;

       }

 

       @Override

       public Properties getClientInfo() throws SQLException {

              if(conn!=null)return conn.getClientInfo();

              return null;

       }

 

       @Override

       public String getClientInfo(String name) throws SQLException {

              if(conn!=null)return conn.getClientInfo(name);

              return null;

       }

 

       @Override

       public int getHoldability() throws SQLException {

              if(conn!=null)return conn.getHoldability();

              return 0;

       }

 

       @Override

       public DatabaseMetaData getMetaData() throws SQLException {

              if(conn!=null)return conn.getMetaData();

              return null;

       }

 

       @Override

       public int getTransactionIsolation() throws SQLException {

              if(conn!=null)return conn.getTransactionIsolation();

              return 0;

       }

 

       @Override

       public Map<String, Class<?>> getTypeMap() throws SQLException {

              if(conn!=null)return conn.getTypeMap();

              return null;

       }

 

       @Override

       public SQLWarning getWarnings() throws SQLException {

              if(conn!=null)return conn.getWarnings();

              return null;

       }

 

       @Override

       public boolean isClosed() throws SQLException {

              if(conn!=null)return conn.isClosed();

              return false;

       }

 

       @Override

       public boolean isReadOnly() throws SQLException {

              if(conn!=null)return conn.isReadOnly();

              return false;

       }

 

       @Override

       public boolean isValid(int timeout) throws SQLException {

              if(conn!=null)return conn.isValid(timeout);

              return false;

       }

 

       @Override

       public String nativeSQL(String sql) throws SQLException {

              if(conn!=null)return conn.nativeSQL(sql);

              return null;

       }

 

       @Override

       public CallableStatement prepareCall(String sql) throws SQLException {

              if(conn!=null)return conn.prepareCall(sql);

              return null;

       }

 

       @Override

       public CallableStatement prepareCall(String sql, int resultSetType,

                     int resultSetConcurrency) throws SQLException {

              if(conn!=null)return conn.prepareCall(sql,resultSetType,resultSetConcurrency);

              return null;

       }

 

       @Override

       public CallableStatement prepareCall(String sql, int resultSetType,

                     int resultSetConcurrency, int resultSetHoldability)

                     throws SQLException {

              if(conn!=null)return conn.prepareCall(sql,resultSetType,resultSetConcurrency,resultSetHoldability);

              return null;

       }

 

       @Override

       public PreparedStatement prepareStatement(String sql) throws SQLException {

              if(conn!=null)return conn.prepareStatement(sql);

              return null;

       }

 

       @Override

       public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)

                     throws SQLException {

              if(conn!=null)return conn.prepareStatement(sql, autoGeneratedKeys);

              return null;

       }

 

       @Override

       public PreparedStatement prepareStatement(String sql, int[] columnIndexes)

                     throws SQLException {

              if(conn!=null)return conn.prepareStatement(sql, columnIndexes);

              return null;

       }

 

       @Override

       public PreparedStatement prepareStatement(String sql, String[] columnNames)

                     throws SQLException {

              if(conn!=null)return conn.prepareStatement(sql, columnNames);

              return null;

       }

 

       @Override

       public PreparedStatement prepareStatement(String sql, int resultSetType,

                     int resultSetConcurrency) throws SQLException {

              if(conn!=null)return conn.prepareStatement(sql, resultSetType, resultSetConcurrency);

              return null;

       }

 

       @Override

       public PreparedStatement prepareStatement(String sql, int resultSetType,

                     int resultSetConcurrency, int resultSetHoldability)

                     throws SQLException {

              if(conn!=null)return conn.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability);

              return null;

       }

 

       @Override

       public void releaseSavepoint(Savepoint savepoint) throws SQLException {

              if(conn!=null)conn.releaseSavepoint(savepoint);

       }

 

       @Override

       public void rollback() throws SQLException {

              if(conn!=null)conn.rollback();

       }

 

       @Override

       public void rollback(Savepoint savepoint) throws SQLException {

              if(conn!=null)conn.rollback(savepoint);

       }

 

       @Override

       public void setAutoCommit(boolean autoCommit) throws SQLException {

              if(conn!=null)conn.setAutoCommit(autoCommit);

       }

 

       @Override

       public void setCatalog(String catalog) throws SQLException {

              if(conn!=null)conn.setCatalog(catalog);

       }

 

       @Override

       public void setClientInfo(Properties properties)

                     throws SQLClientInfoException {

              if(conn!=null)conn.setClientInfo(properties);

       }

 

       @Override

       public void setClientInfo(String name, String value)

                     throws SQLClientInfoException {

              if(conn!=null)conn.setClientInfo(name, value);

       }

 

       @Override

       public void setHoldability(int holdability) throws SQLException {

              if(conn!=null)conn.setHoldability(holdability);

       }

 

       @Override

       public void setReadOnly(boolean readOnly) throws SQLException {

              if(conn!=null)conn.setReadOnly(readOnly);

       }

 

       @Override

       public Savepoint setSavepoint() throws SQLException {

              if(conn!=null)return conn.setSavepoint();

              return null;

       }

 

       @Override

       public Savepoint setSavepoint(String name) throws SQLException {

              if(conn!=null)return conn.setSavepoint(name);

              return null;

       }

 

       @Override

       public void setTransactionIsolation(int level) throws SQLException {

              if(conn!=null)conn.setTransactionIsolation(level);

       }

 

       @Override

       public void setTypeMap(Map<String, Class<?>> map) throws SQLException {

              if(conn!=null)conn.setTypeMap(map);

       }

 

       @Override

       public boolean isWrapperFor(Class<?> iface) throws SQLException {

              if(conn!=null)return conn.isWrapperFor(iface);

              return false;

       }

 

       @Override

       public <T> T unwrap(Class<T> iface) throws SQLException {

              if(conn!=null)return conn.unwrap(iface);

              return null;

       }

       @Override

       protected void finalize() throws Throwable {

              super.finalize();

              close();

       }

}

 

R2Pool类,池对象类

/**

 * R2 jdbc connection pool

 * R2连接池,连接池类

 * @author 张人杰 北京师范大学 计算机系

 * alex.zhangrj@hotmail.com

 * alex.zhangrj Beijing Normal University

 * 2011/11/26

 */

public class R2Pool {

       private ConcurrentLinkedQueue<R2ConnectionWrap> pool=null;

       private String driver="com.mysql.jdbc.Driver";

       private String url="jdbc:mysql://localhost:3306/movie_info?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false";

       private String user="root";

       private String password="1234";

       private int    maxSize=50;

       private int minSize=2;

       private int initSize=2;

       private int increment=2;

       private long timeout=120000;

       private long idleTestPeriod=120000;

       private String keepAliveSql="select 1;";

       private boolean closeIdleConnection=true;

       private long maxIdleTime=300000;

       private int retryTimesWhileGetNullConnection=-1;

       private long retryDurationDuringGetNullConnection=1000;

       private int retryTimesWhileCanNotConnectServer=-1;

       private long retryDurationDuringConnectingServer=1000;

      

       private AtomNumber currentSize=new AtomNumber(0);

       private Thread cleanPoolThread;

       private boolean keepClean=true;

       private boolean closed=false;

       private Logger logger=Logger.getLogger(R2Pool.class);

      

      

       class AtomNumber{

              int num=0;

              public AtomNumber(){

                     num=0;

              }

              public AtomNumber(int n){

                     num=n;

              }

              public void inc(int n){

                     synchronized (this) {

                            num+=n;

                     }

              }

              public void dec(int n){

                     synchronized (this) {

                            num-=n;

                     }

              }

              public int getValue(){

                     return num;

              }

              public void setValue(int n){

                     synchronized (this) {

                            num=n;

                     }

              }

       }

       /**

        * 得到当前池中的连接数量

        * @return

        */

       public int getCurrentSize(){

              return currentSize.getValue();

       }

      

       /**

        * 初始化R2连接池,增加池中链接,设置清理线程。

        * 注意:

        * 1、 此方法只可调用一次,若意外被调用多次,则原有jdbc连接会在Connection对象被gc时释放,释放时间较长。

        * 2、在开始使用连接池后调用此方法,则可能造成连接数超过配置项最大连接数maxSize

        */

       public void init(){

              pool=new ConcurrentLinkedQueue<R2ConnectionWrap>();

              closed=false;

              currentSize.setValue(0);

              increasePool(pool, initSize);

              if(cleanPoolThread!=null){//防止重复调用,造成多个清理线程

                     keepClean=false;

                     cleanPoolThread.interrupt();

                     try {

                            Thread.sleep(idleTestPeriod+1000);

                     } catch (InterruptedException e) {

                            logger.error("reinitial pool error,interrupted while waiting the old clean thread to close.", e);

                     }

                     logger.error("the old cleanPoolThread is closed.");

                     keepClean=true;

              }

              cleanPoolThread=new Thread(){

                     @Override

                     public void run() {

                            while(keepClean){

                                   try {

                                          Thread.sleep(idleTestPeriod);

                                   } catch (InterruptedException e) {

                                          logger.error("clean pool thread sleep error", e);

                                   }

                                   int availableNum=pool.size();//目前池中拥有的连接数

                                   R2ConnectionWrap wrap=pool.poll();

                                   for(int i=0;i<availableNum;i++){

                                          if(wrap==null)break;//池中已无可用连接

                                          long now = Calendar.getInstance().getTimeInMillis();

                                          if(closeIdleConnection&&(wrap.getLastUsingTime()+maxIdleTime<now)&&currentSize.getValue()>minSize){//空闲时间过长则关闭连接

                                                 try {

                                                        wrap.getConnection().closeJdbcConnection();

                                                 } catch (SQLException e) {

                                                        logger.error("close the real jdbc connection error", e);

                                                 }

                                                 currentSize.dec(1);

                                          }else{

                                                 Connection conn = wrap.getConnection();

                                                 PreparedStatement pstmt;

                                                 try {

                                                        pstmt = conn.prepareStatement(keepAliveSql);

                                                        pstmt.execute();

                                                        pstmt.close();

                                                 } catch (SQLException e) {

                                                        logger.error("execute keepAliveSql error", e);

                                                 }

                                                 pool.add(wrap);

                                          }

                                          wrap=pool.poll();

                                   }

                            }

                     }

              };

              cleanPoolThread.start();

       }

 

       /**

        * 增加R2连接池中连接的数量(由调用此方法的方法控制同步锁)

        * @param pool

        * @param size

        */

       private void increasePool(ConcurrentLinkedQueue<R2ConnectionWrap> pool,int size){

              try {

                     Class.forName(driver);

                     for(int i=0;currentSize.getValue()<maxSize&&i<size;i++){

                            DriverManager.setLoginTimeout((int)(timeout/1000));

                            try {

                                   Connection jdbcConn = DriverManager.getConnection(url, user, password);

                                   releaseConnection(jdbcConn);

                                   currentSize.inc(1);

                            } catch (SQLException e) {

                                   logger.error("getConnection error,maybe user or password or url wrong,or the db is not available (timeout) at this time.", e);

                            }

                     }

              } catch (ClassNotFoundException e) {

                     logger.error("jdbc driver error", e);

              }

       }

       /**

        * 释放连接(将jdbc连接放回R2连接池中)

        * @param jdbcConn

        */

       public void releaseConnection(Connection jdbcConn){

              R2Connection connection = new R2Connection();

              connection.setConnection(jdbcConn);

              connection.setPool(this);

              R2ConnectionWrap wrap=new R2ConnectionWrap();

              wrap.setConnection(connection);

              wrap.setLastUsingTime(Calendar.getInstance().getTimeInMillis());

              pool.add(wrap);

       }

      

       /**

        * 从R2连接池中获得连接

        * @return 经过包装处理close方法后的Connection对象

        */

       public Connection getConnection(){

              if(closed){//连接池已关闭

                     return null;

              }

              synchronized(this){//若连接池未初始化,则初始化

                     if(pool==null){

                            init();

                     }

              }

              R2ConnectionWrap wrap = pool.poll();

              int i=0;

              while(wrap==null&&(i<retryTimesWhileGetNullConnection||retryTimesWhileGetNullConnection<=0)){//当retryTimes为-1时,保持重试

                     synchronized(this){//若连接池中连接已用完(返回的wrap为null),则增加连接池中连接数量

                            if(currentSize.getValue()<maxSize){

                                   increasePool(pool, increment);

                            }

                     }

                     wrap = pool.poll();

                     if(wrap!=null)break;

                     try {

                            Thread.sleep(retryDurationDuringGetNullConnection);

                     } catch (InterruptedException e) {

                            logger.error("retry sleep error",e);

                     }

                     ++i;

              }

              if(wrap!=null){

                     R2Connection connection = wrap.getConnection();

                     int j=0;

                     while(j<retryTimesWhileCanNotConnectServer||retryTimesWhileCanNotConnectServer<=0){

                            PreparedStatement pstmt;

                            try {//检查连接是否正常打开

                                   pstmt = connection.prepareStatement(keepAliveSql);

                                   pstmt.execute();

                                   pstmt.close();

                                   break;

                            } catch (SQLException e) {

                                   logger.error("execute keepAliveSql error", e);

                            }//若未正常打开,则重新链接数据库,以保证返回的数据库连接可用

                            DriverManager.setLoginTimeout((int)(timeout/1000));

                            try {

                                   Connection jdbcConn = DriverManager.getConnection(url, user, password);

                                   connection.setConnection(jdbcConn);

                            } catch (SQLException e) {

                                   logger.error("connecting server error,maybe user or password or url wrong,or the db is not available (timeout) at this time.", e);

                            }

                            try {

                                   Thread.sleep(retryDurationDuringConnectingServer);

                            } catch (InterruptedException e) {

                                   logger.error("retry sleep error while connectiing server.", e);

                            }

                            ++j;

                     }

                     return connection;

              }else{

                     return null;

              }

       }

       public void close(){

              keepClean=false;

              closed=true;

       }

 

       //以下为set,get方法

       public String getDriver() {

              return driver;

       }

 

       public void setDriver(String driver) {

              this.driver = driver;

       }

 

       public String getUrl() {

              return url;

       }

 

       public void setUrl(String url) {

              this.url = url;

       }

 

       public String getUser() {

              return user;

       }

 

       public void setUser(String user) {

              this.user = user;

       }

 

       public String getPassword() {

              return password;

       }

 

       public void setPassword(String password) {

              this.password = password;

       }

 

       public int getMaxSize() {

              return maxSize;

       }

 

       public void setMaxSize(int maxSize) {

              this.maxSize = maxSize;

       }

 

       public int getMinSize() {

              return minSize;

       }

 

       public void setMinSize(int minSize) {

              this.minSize = minSize;

       }

 

       public int getInitSize() {

              return initSize;

       }

 

       public void setInitSize(int initSize) {

              this.initSize = initSize;

       }

 

       public int getIncrement() {

              return increment;

       }

 

       public void setIncrement(int increment) {

              this.increment = increment;

       }

 

       public long getTimeout() {

              return timeout;

       }

 

       public void setTimeout(long timeout) {

              this.timeout = timeout;

       }

 

       public long getIdleTestPeriod() {

              return idleTestPeriod;

       }

 

       public void setIdleTestPeriod(long idleTestPeriod) {

              this.idleTestPeriod = idleTestPeriod;

       }

 

       public String getKeepAliveSql() {

              return keepAliveSql;

       }

 

       public void setKeepAliveSql(String keepAliveSql) {

              this.keepAliveSql = keepAliveSql;

       }

 

       public boolean isCloseIdleConnection() {

              return closeIdleConnection;

       }

 

       public void setCloseIdleConnection(boolean closeIdleConnection) {

              this.closeIdleConnection = closeIdleConnection;

       }

 

       public long getMaxIdleTime() {

              return maxIdleTime;

       }

 

       public void setMaxIdleTime(long maxIdleTime) {

              this.maxIdleTime = maxIdleTime;

       }

 

       public int getRetryTimesWhileGetNullConnection() {

              return retryTimesWhileGetNullConnection;

       }

 

       public void setRetryTimesWhileGetNullConnection(

                     int retryTimesWhileGetNullConnection) {

              this.retryTimesWhileGetNullConnection = retryTimesWhileGetNullConnection;

       }

 

       public long getRetryDurationDuringGetNullConnection() {

              return retryDurationDuringGetNullConnection;

       }

 

       public void setRetryDurationDuringGetNullConnection(

                     long retryDurationDuringGetNullConnection) {

              this.retryDurationDuringGetNullConnection = retryDurationDuringGetNullConnection;

       }

 

       public int getRetryTimesWhileCanNotConnectServer() {

              return retryTimesWhileCanNotConnectServer;

       }

 

       public void setRetryTimesWhileCanNotConnectServer(

                     int retryTimesWhileCanNotConnectServer) {

              this.retryTimesWhileCanNotConnectServer = retryTimesWhileCanNotConnectServer;

       }

 

       public long getRetryDurationDuringConnectingServer() {

              return retryDurationDuringConnectingServer;

       }

 

       public void setRetryDurationDuringConnectingServer(

                     long retryDurationDuringConnectingServer) {

              this.retryDurationDuringConnectingServer = retryDurationDuringConnectingServer;

       }

 

}

 

R2ConnectionWrap类,对R2Connection的包装,用于连接池中定时任务的处理(即,在原R2Connection类的基础上,加入时间属性)

/**

 * R2 jdbc connection Wrap class, to wrap connection and lastUsingTime

 * R2连接池,链接包装类,用于处理定时清理链接

 * @author 张人杰 北京师范大学 计算机系

 * alex.zhangrj@hotmail.com

 * alex.zhangrj Beijing Normal University

 * 2011/11/26

 */

public class R2ConnectionWrap {

       private long lastUsingTime=0;//开始使用此链接的时间

       private R2Connection connection=null;

 

       public long getLastUsingTime() {

              return lastUsingTime;

       }

       public void setLastUsingTime(long lastUsingTime) {

              this.lastUsingTime = lastUsingTime;

       }

       public R2Connection getConnection() {

              return connection;

       }

       public void setConnection(R2Connection connection) {

              this.connection = connection;

       }

}

 

配置文件样本:

#driver of jdbc

driver=com.mysql.jdbc.Driver

url=jdbc:mysql://localhost:3306/movie_info?useUnicode=true&characterEncoding=utf8&autoReconnect=true&failOverReadOnly=false

user=root

password=1234

#max size of connection pool

maxSize=50

#min size of connection pool

minSize=2

#initial size of connection pool

initSize=2

#increment of connections while all connections are busy and hasn't reached the max size of connection pool

increment=2

#timeout of connection(millisecond)

timeout=120000

#period of testing idle connect(millisecond, must positive)

idleTestPeriod=120000

#sql to execute for keeping connection alive

keepAliveSql=select 1;

#close idle connections while reaching threshold of max idle time if it's true

closeIdleConnection=true

#max idle time before close a idle connection(millisecond)

maxIdleTime=300000

#retry times while get null connection(keep retry while 0 or negative)

retryTimesWhileGetNullConnection=-1

#retry duration during get null connection(millisecond,immediately retry while 0)

retryDurationDuringGetNullConnection=1000

#retry times while can not connect server(keep retry while 0 or negative)

retryTimesWhileCanNotConnectServer=-1

#retry duration during connecting server(millisecond,immediately retry while 0)

retryDurationDuringConnectingServer=1000

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值