⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 connectionpool.java

📁 cmpp的开发的短信端口实例
💻 JAVA
字号:
package com.zhanghao.common.database;

import java.sql.*;
import java.util.*;

/**
 * <p>
 * 数据库连接池。
 * 池里每个连接的最大活跃期是24小时,过期则自动重新连接数据库(以保证性能稳定)。
 * 如果连接池中的某个连接因为某个原因被数据库断开连接,则系统把该连接从连接池移出。
 * 客户端请求连接时,如果当前没有空闲连接,而连接总数小于连接池最大容量,则新建
 * 连接加入到连接池
 * </p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2004</p>
 * <p>Company: </p>
 * @author hmy
 * @version 2004-06-11
 */

import com.zhanghao.common.util.*;

public class ConnectionPool {
  public static final long SECOND = 1000;  //秒
  public static final long MINUTE = SECOND*60;   //分
  public static final long HOUR = MINUTE*60;   //小时

  //一个数据库连接的有效期。超过有效其的连接应该被释放,连接池重新建立一个连
  // 接来代替它,默认为24(单位:小时)
  //一个连接建立后,使用一定时间后,应该释放,重新建立一个连接代替它,否则
  //一个连接时间长了会出现性能和可靠性不稳定
  private final static int CONNECTION_ALIVE_TIME = 24;

  private int poolSize;    //连接池大小
  private long timeout;   //连接时的time out设置(单位:分钟)。必须大于0,默认为30

  private String driver = null; //驱动程序类
  private String dbURL = null;  //数据库服务器URL地址
  private String username = null;   //访问数据库的用户名
  private String password = null;   //访问数据库的密码

  /**
   * 用来测试的数据库连接的有效性的数据表的表名。要求数据
   * 库里存在这个表,而且这个表属于这个用户;至于这个表有
   * 些什么字段,有多少记录,都不重要(记录条数最好不要超过10条,可以没有记录)。
   * 这个属性的值可以为null,如果为null,则对某些数据库,这个连接池可能失去自动重连
   * 的功能(即在数据库服务器重新启动或网络连接短暂断开后,
   * 该连接池无法自动重新连接数据库)。
   */
  private String testTableName = null;

  /**
   * 物理连接池(包含所有物理数据库连接,不论是否被用户占用)。
   * 本集合的大小即代表与数据库的总连接数。
   * 集合中每个元素都是一个java.sql.Connection对象。
   */
  private Vector  allConns;

//  /**
//   * 所有连接(包装)的池子
//   */
//  private Vector allWrappers;

  /**
   * 空闲连接(包装)池(只包含未被用户占用的连接)。
   * 本集合的大小即代表当前空闲数据库连接(未被用户占用)的数目。
   * 里面的每个元素都是一个ConnectionWrapper对象。用户请求连接时,
   * 系统从本集合中取得ConnectionWrapper对象,同时把该对象从本集合中移除。
   */
  private Vector freeWrappers;

  private static ConnectionPool pool = null;

  /**
   *
   * @param driver  驱动程序类名
   * @param dbURL      数据库服务器的URL地址
   * @param username    数据库用户名
   * @param password    数据库用户的密码
   * @param test_table_name   参见属性testTableName的说明
   * @param poolSize    连接池大小
   * @param timeout     timeout设置,单位:分
   */
  private ConnectionPool(
      String driver,
      String dbURL,
      String username,
      String password,
      int poolSize,
      int timeout) throws Exception{
    this.driver = driver.trim();
    this.dbURL = dbURL.trim();
    this.username = username.trim();
    this.password = password.trim();
    this.timeout = timeout;
    this.poolSize = poolSize;

//    if(testTableName!=null && testTableName.trim().length()==0){
//      testTableName = null;
//    }

    allConns = new Vector(poolSize);
    freeWrappers = new Vector(poolSize);

    try{
      Class.forName(driver);

      for(int i=0;i<poolSize;i++){//根据池的大小,建立若干个连接,放入池中
        addConnection();
      }
    }catch(Exception e){
      for(int i=0;i<allConns.size();i++){
        try{
          //关闭所有已经取得的物理连接
          ((Connection)allConns.get(i)).close();
        }catch(Exception eee){}
      }//end for i
      allConns.clear();
      freeWrappers.clear();
      throw e;
    }//end try

    //启动检查线程,定期对过期连接进行释放、重新连接的工作
    (new ReCreateThread()).start();
    (new ReConnectThread()).start();

  }//end of method

  private void addConnection() {
    try{
      Connection conn = DriverManager.getConnection(dbURL, username, password); //建立连接
      ConnectionWrapper wrapper = new ConnectionWrapper(conn, freeWrappers);

      allConns.addElement(conn); //把物理连接放入数据库连接池中
      //把物理连接放入包装器后,放入空闲连接池中
      freeWrappers.addElement(wrapper);
    }catch (SQLException e) {
      System.out.println("ConnectionPool,addConnection(),新建数据库链接失败,原因:"+e);
    }
  }

  /**
   * 初始化数据库连接池
   * @param driver
   * @param url
   * @param user
   * @param password
   * @param  testTableName  用来测试的数据库连接的有效性的数据表的表名。要求数据
   *                      库里存在这个表,而且这个表属于这个用户;至于这个表有
   *                      些什么字段,有多少记录,都不重要(记录条数最好不要超过10条,
   *                      可以没有记录)。这个参数可以为null,
   *                      如果为null,则对某些数据库,这个连接池可能失去自动重连
   *                      的功能(即在数据库服务器重新启动或网络连接短暂断开后,
   *                      该连接池无法自动重新连接数据库)。
   * 连接池将用这个表
   * @param size   连接池大小
   * @param timeout     连接超时设置(单位:秒)
   */
  public static void initConnectionPool(String driver,String url,String user,
          String password, int size,int timeout) throws SQLException {
    if(pool!=null){ //连接池已经建立
      return;
    }

    try{
      pool = new ConnectionPool(driver,url,user,password,size,timeout);
    }catch(Exception e){
      throw new SQLException(e.toString());
    }
  }

  /**
   * 从空闲连接池取得连接。
   * @return 取得的连接
   * @throws   遇到SQL异常则抛出,连接超时也抛出异常。
   */
  public static Connection getConnection() throws SQLException {
    if(pool==null){
      throw new SQLException("异常,ConnectionPool,getConnection(),连接池尚未初始化");
    }

    ConnectionWrapper wrapper;
    Connection conn = null;
    long startTime = System.currentTimeMillis(); //发起请求的时间
    while(conn==null && System.currentTimeMillis()-startTime<SECOND*pool.timeout){
      synchronized(pool.freeWrappers){
        if (pool.freeWrappers.size() > 0) {
          //从空闲连接池取出连接。
          //从连接池取出连接时从队列的最前面取,把连接放回连接池时放到队列的最后
          wrapper = (ConnectionWrapper) pool.freeWrappers.remove(0);
          return wrapper;
        }else{  //如果没有空闲连接
          if(pool.allConns.size()<pool.poolSize){  //有效物理连接不够
            pool.addConnection();   //增加连接
          }
        }
      }//end  synchronized

      try{
        Thread.sleep(SECOND);   //等待1秒
      }catch(Exception e){
        //
      }
    }//end while

    throw new SQLException("ConnectionPool,getConnection,请求连接超时");

  }//end of method

  /**
   * 定期检查物理连接池里的连接,如果某个连接被数据库断开了,则重新建立连接替
   * 代它,同时要对空闲连接(包装)池做处理
   */
  private class ReConnectThread extends Thread{
    public void run(){
      while(true){
        try {
          Thread.sleep(10 * SECOND); //间隔10秒
        }
        catch (Exception e) {
          //
        }

        deleteInvalidConns();  //删除物理连接池里的无效连接
        deleteInvalidWrappers();   //删除无效的连接包装器
      }//end while
    }

    private void deleteInvalidWrappers(){
      if(allConns.size()==poolSize){
        return;
      }

      synchronized(freeWrappers){
          ConnectionWrapper wrapper = null;
          Connection conn = null;
          for(int i=freeWrappers.size()-1;i>=0;i--){
            wrapper = (ConnectionWrapper)freeWrappers.get(i);
            conn = wrapper.getPhysicalConnection();
            if(allConns.contains(conn)==false){  //说明该包装器已经无效
              freeWrappers.remove(wrapper);   //删除无效包装器
            }//end if
          }//end for i
      }//end synchronized

    }//end method

    private void deleteInvalidConns(){
      synchronized(allConns){
        Connection conn = null;
        for(int i=allConns.size()-1;i>=0;i--){
          conn = (Connection)allConns.get(i);
          if(testConnection(conn)==false){
            try{
              conn.close();  //关闭物理连接
            }catch(Exception e){}
            allConns.remove(conn);  //删除无效物理连接
          }
        }//end for i
      }//end synchronized
//      System.out.println("连接池大小:"+allConns.size());
    }

    /**
     * 检查一个数据库连接是否有效
     * @param conn
     * @return   有效则返回true
     */
    private boolean testConnection(Connection conn){
      Statement stmt = null;
      ResultSet rs = null;
      boolean flag = false;
      try{
        stmt = conn.createStatement();  //测试是否有效
        //if(testTableName!=null){
          //String sql = "select getdate()";            //测试SQL,根据不同的数据库更改
          //rs = stmt.executeQuery(sql);
        //}

        flag = true;   //没出异常,说明连接有效
//       System.out.println("------testConnection(),连接有效");
      }catch(SQLException e){
//        System.out.println("------testConnection(), table name: "+testTableName);
//        System.out.println("------testConnection(), 异常:");
//        e.printStackTrace();
        flag = false;  //认定连接无效
      }finally{
        if(rs!=null)
          try{
            rs.close();
          }catch(Exception ee){}

        if(stmt!=null)
          try{
            stmt.close();
          }catch(Exception ee){}
      }
      return flag;
    }//end of method testConnection

  }

  /**
   * 负责释放过期的数据库连接、重新建立连接替代它的线程
   * <p>Title: </p>
   * <p>Description: </p>
   * <p>Copyright: Copyright (c) 2004</p>
   * <p>Company: </p>
   * @author hmy
   * @version 1.0
   */
  private class ReCreateThread extends Thread{
    public void run(){
      while(true){
        try{
          Thread.sleep(HOUR);   //等待一个小时
        }catch(Exception e){}

        recreateConns();  //检查连接池中的连接,对于已经过期的,释放,用新的连接代替
      }//end for while
    }

    //在空闲连接池中检查已经过期的连接,将它释放,新建一个连接代替它
    private void recreateConns(){
      //Debug.outInfo("ConnectionPool,ReCreateThread,巡视,检查过期连接");
      //用来存储需要被释放、重新连接的ConnectionWrapper对象
      //已经超过有效期的、当前正处于空闲状态的ConnectionWrapper对象需要被释放、重连
      Vector timeouts = new Vector();

      //从空闲连接池取出所有过期连接,放入timeouts集合中
      long currTime = System.currentTimeMillis(); //当前时间
      ConnectionWrapper wrapper = null;

      synchronized(freeWrappers){
        //当前长度。本次操作期间,被用户返回连接池的连接放到了队列的最后,本次不操作它
        int currSize = freeWrappers.size();

        //遍历空闲连接池中的连接(因为有删除操作,所以从后面开始),把该释放的对象取出
        for (int i = currSize-1; i>=0; i--) {
          wrapper = (ConnectionWrapper) freeWrappers.get(i);

          //如果连接已经过了有效期:
          if (currTime - wrapper.getCreatedTime() > HOUR * CONNECTION_ALIVE_TIME) {//已经超时
            timeouts.addElement(freeWrappers.remove(i));
          }//end if
        }//end for i
      }//end synchronized


      //把需要重新连接的对象释放,并新建连接替代它
      Connection timeoutConn;   //过期的物理连接
      Connection newConn;   //新的物理连接
      for(int i=0;i<timeouts.size();i++){
        wrapper = (ConnectionWrapper)timeouts.get(i);
        timeoutConn = wrapper.getPhysicalConnection();  //取得包装器所包装的物理连接
        try{
          allConns.remove(timeoutConn);   //把过期物理连接从物理连接池移除
          timeoutConn.close();   //释放过期物理连接

          newConn = DriverManager.getConnection(dbURL,username,password);//新建一个物理连接
          allConns.addElement(newConn);  //放入物理连接池
          //包装后放入空闲连接池:
          freeWrappers.addElement(new ConnectionWrapper(newConn,freeWrappers));
        }catch(Exception e){
          //
        }
      }//end for i

    }//end of method

  }//end class ReCreateThread


  //测试
  public static void main(String[] args){
    Connection conn = null;

    int m = 1;

    while(true){
      try{
        //取得连接,持有1秒再释放
        conn = ConnectionPool.getConnection();
        Debug.outInfo("得到连接:   "+conn);
        Debug.outInfo("conn.getAutoCommit(): "+conn.getAutoCommit());

        Thread.sleep(1000);  //等待1秒

        conn.close();
        Debug.outInfo("conn.getAutoCommit(): "+conn.getAutoCommit());

        Debug.outInfo("------------------------------");
      }catch(Exception e){
        e.printStackTrace();
        //Debug.outInfo("main(), e: "+e);
      }

    }//end  while


  }//end of method


}//end file


⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -