DBCP数据库链接池的简单使用

0、DBCP简介java

     DBCP(DataBase connection pool)数据库链接池是 apache 上的一个Java链接池项目。DBCP经过链接池预先同数据库创建一些链接放在内存中(即链接池中),应用程序须要创建数据库链接时直接到从接池中申请一个链接使用,用完后由链接池回收该链接,从而达到链接复用,减小资源消耗的目的。mysql

一、DBCP所依赖的jar包(如下例子基于以下jar包版本)sql

   commons-dbcp2-2.1.1.jar       commons-logging-1.2.jar       commons-pool2-2.4.2.jar数据库

二、DBCP使用示例apache

  下图是在Eclipse中建立的Java工程,使用了DBCP相关的jar包,mysql的jdbc驱动jar包,junit4 。测试

   并在src同级目录下建立了config目录,用于存放DBCP的配置文件。url

      【注】类DBCPUtil.java在下面的例子中未用到。spa

  

  1) DBCP配置文件dbcp.propertiescode

########DBCP配置文件##########

#驱动名
driverClassName=com.mysql.jdbc.Driver
#url
url=jdbc:mysql://127.0.0.1:3306/mydb
#用户名
username=sa
#密码
password=123456
#初试链接数
initialSize=30
#最大活跃数
maxTotal=30
#最大idle数
maxIdle=10
#最小idle数
minIdle=5
#最长等待时间(毫秒)
maxWaitMillis=1000
#程序中的链接不使用后是否被链接池回收(该版本要使用removeAbandonedOnMaintenance和removeAbandonedOnBorrow)
#removeAbandoned=true
removeAbandonedOnMaintenance=true
removeAbandonedOnBorrow=true
#链接在所指定的秒数内未使用才会被删除(秒)(为配合测试程序才配置为1秒)
removeAbandonedTimeout=1blog

   

  2) 建立初始化DBCP的类KCYDBCPUtil.java  

  package dbcp;
  
  import java.io.FileInputStream;
  import java.io.IOException;
  import java.sql.Connection;
  import java.sql.SQLException;
  import java.util.Properties;
  
  import javax.sql.DataSource;
 
 import org.apache.commons.dbcp2.BasicDataSourceFactory;
 
 /**
  * DBCP配置类
  * @author SUN
  */
 public class KCYDBCPUtil {
     
     private static Properties properties = new Properties();
     private static DataSource dataSource;
     //加载DBCP配置文件
     static{
         try{
             FileInputStream is = new FileInputStream("config/dbcp.properties");  
             properties.load(is);
         }catch(IOException e){
             e.printStackTrace();
         }
         
         try{
             dataSource = BasicDataSourceFactory.createDataSource(properties);
         }catch(Exception e){
             e.printStackTrace();
         }
     }
     
     //从链接池中获取一个链接
     public static Connection getConnection(){
         Connection connection = null;
         try{
             connection = dataSource.getConnection();
         }catch(SQLException e){
             e.printStackTrace();
         }
         try {
             connection.setAutoCommit(false);
         } catch (SQLException e) {
             e.printStackTrace();
         }
         return connection;
     }
     
     public static void main(String[] args) {
         getConnection();
     }
 }

 

   3) 建立使用JDBC获取数据库链接的类DBConn.java(用于和DBCP链接池对比)

  
  import java.sql.Connection;
  import java.sql.DriverManager;
  
  public class DBConn {
      private static Connection conn = null;
      
      //获取一个数据库链接
     public static Connection getConnection() {
         try {
             Class.forName("com.mysql.jdbc.Driver");
             DriverManager.registerDriver(new com.mysql.jdbc.Driver());
            String dbUrl = "jdbc:mysql://127.0.0.1:3306/mydb";
             conn = DriverManager.getConnection(dbUrl, "sa", "123456");
 //            System.out.println("========数据库链接成功========");
         } catch (Exception e) {
             e.printStackTrace();
 //            System.out.println("========数据库链接失败========");
             return null;
         }
         return conn;
     }
 }

 

   4) 建立测试类DBCPTest.java

    测试类中采用3中方法将2000个数据插入数据库同一张表中,每次插入数据以前,先清空表,并对结果进行了对比。

    3中插入数据方法以下:

    (1) 每次插入一条数据前,就建立一个链接,该条数据插入完成后,关闭该链接;

      (2) 使用DBCP链接池,每次插入一条数据前,从DBCP链接池中获取一条链接,该条数据插入完成后,该链接交由DBCP链接池管理;

    (3) 在插入数据以前建立一条链接,2000个数据所有使用该链接,2000个数据插入完毕后,关闭该链接。

  package dbcp;
  
  import java.sql.Connection;
  import java.sql.SQLException;
  import java.sql.Statement;
  
  import org.junit.Test;
 
  public class DBCPTest {
     
     //测试,每写一条数据前,就新建一个链接
     @Test
     public void testWriteDBByEveryConn() throws Exception{
         for(int i = 0; i < 2000; i++){
             writeDBByEveryConn(i);
         }
         System.out.println("DONE");
         
     }
     
     //测试,使用链接池,每写一条数据前,从链接池中获取一个链接
    @Test
     public void testWriteDBByDBCP() throws Exception{
         for(int i = 0; i < 2000; i++){
             writeDBByDBCP(i);
         }
         System.out.println("DONE");
     }
     
     //测试,只建一条链接,写入全部数据
     @Test
     public void testWriteDBByOneConn() throws Exception{
         Connection conn = DBConn.getConnection();
         Statement stat = conn.createStatement();
         for(int i = 0; i < 2000; i++){
             writeDBByOneConn(i, stat);
        }
         conn.close();
         System.out.println("DONE");
     }
     
     //不使用链接池写数据库,每写一条数据建立一个链接
     public void writeDBByEveryConn(int data){
         String sql = "insert into dbcp values (" + data + ")"; 
         Connection conn = DBConn.getConnection();
         try{    
             Statement stat = conn.createStatement();
             stat.executeUpdate(sql);            
         }catch(Exception e){
             e.printStackTrace() ;
         }finally{
             try {    
                 conn.close();    
             } catch (SQLException e) {
                 e.printStackTrace();
             }
             
         }
     }
     
     //不使用链接池写数据库,只用一个链接,写全部数据
     public void writeDBByOneConn(int data, Statement stat){
         String sql = "insert into dbcp values (" + data + ")"; 
         try{    
             stat.executeUpdate(sql);            
         }catch(Exception e){
             e.printStackTrace() ;
         }
     }
     
     //经过DBCP链接池写数据库
     public void writeDBByDBCP(int data){ 
         String sql = "insert into dbcp values (" + data + ")"; 
         try {
             Connection conn = KCYDBCPUtil.getConnection();  
             Statement stat = conn.createStatement();
             stat.executeUpdate(sql);
             conn.commit();
             conn.close();
         } catch (SQLException e) {   
             e.printStackTrace();  
         }
     }
 
 } 

  测试结果以下:

  (1) 每次插入一条数据前,就建立一个链接,该条数据插入完成后,关闭该链接。耗时158.318秒

   

  (2) 使用DBCP链接池,每次插入一条数据前,从DBCP链接池中获取一条链接,该条数据插入完成后,该链接交由DBCP链接池管理。耗时122.404秒

    

  (3) 在插入数据以前建立一条链接,2000个数据所有使用该链接,2000个数据插入完毕后,关闭该链接。耗时117.87秒

    

   经过对比结果看出,向同一个表中插入2000条数据,每插入一条数据前建立一个新链接,会很是耗时,而使用DBCP链接池和使用同一个链接操做,耗时比较接近。

 三、相关问题

  1) 应用程序中,使用完一个数据库链接后,DBCP链接池如何管理该链接。

    分两种状况:

    (1) 应用程序中主动关闭该链接,即DBCPTest.java中第79行  conn.close();

     这种状况并非手动将该链接关闭,而是将该链接交回给DBCP链接池,由链接池管理该链接。即用完链接后显示的将数据库链接提交至DBCP链接池。

    (2) 应用程序中不关闭该链接,即将DBCPTest.java中第79行  conn.close()注释掉

     这种状况DBCP配置文件dbcp.properties中的配置项(注意jar包版本,低版本中使用removeAbandoned=true配置项) 

     removeAbandonedOnMaintenance=true
        removeAbandonedOnBorrow=true

      removeAbandonedTimeout=1

      会起做用,removeAbandonedOnMaintenance=true和removeAbandonedOnBorrow=true表示DBCP链接池自动管理应程序中使用完毕的链接,removeAbandonedTimeout=1表示一个链接在程序中使用完毕后,若在1秒以内没有再次使用,则DBCP链接池回收该链接(一般removeAbandonedTimeout不会配置1,此处为了测试使用)。

    (3) 验证removeAbandonedOnMaintenance=true、removeAbandonedOnBorrow=trueremoveAbandonedTimeout=1配置项的做用

       将测试类DBCPTest.java的writeDBByDBCP(int data)方法修改成以下:

      //经过DBCP链接池写数据库
      public void writeDBByDBCP(int data){ 
          String sql = "insert into dbcp values (" + data + ")"; 
          try {
              Connection conn = KCYDBCPUtil.getConnection();  
              Statement stat = conn.createStatement();
              stat.executeUpdate(sql);
              conn.commit();
  //            conn.close();
         } catch (SQLException e) {   
             e.printStackTrace();  
         }
     }

 

    从新执行testWriteDBByDBCP()方法,结果以下:

     

    可见writeDBByDBCP(int data)方法修改后和修改前做用相同,说明链接使用完后,由DBCP链接池管理。

    而若是将修改配置项removeAbandonedTimeout=180,即一个链接用完后会等待180秒,超过180秒后才由DBCP链接池回收,从新执行testWriteDBByDBCP()方法,执行一段时间后报错(Cannot get a connection, pool error Timeout waiting for idle object),以下:

    

    此时,查询数据表,发现正好插入了30条数据,以下:

    

    这说明在插入第31条数据的时候报错,错误缘由是链接池中没有可用的链接了。这是由于DBCP链接池初始化链接数为30,removeAbandonedTimeout设为180秒,因此30个链接用完后,程序运行还未 到180秒,程序中用完的链接都尚未被DBCP链接池回收,因此DBCP链接池中没有可用的链接了,才会在插入第31条数据时报错。

相关文章
相关标签/搜索