• DBCP数据库连接池的简单使用


    0、DBCP简介

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

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

       commons-dbcp2-2.1.1.jar       commons-logging-1.2.jar       commons-pool2-2.4.2.jar

    2、DBCP使用示例

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

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

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

      

      1) DBCP配置文件dbcp.properties

    ########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=1

       

      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连接池和使用同一个连接操作,耗时比较接近。

     3、相关问题

      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条数据时报错。

  • 相关阅读:
    PHP-配置方法
    正则表达式-基础使用整理
    SEO-长尾词与分词技术
    SEO-站外优化规范
    SEO-站内优化规范
    SEO-友情链接注意事项
    java netty之ServerBootstrap的启动
    [curator] Netflix Curator 使用
    Netty5.x中新增和值得注意的点
    带连接池的netty客户端核心功能实现剖解
  • 原文地址:https://www.cnblogs.com/zmwf/p/9108725.html
Copyright © 2020-2023  润新知