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  

 1 package dbcp;
 2 
 3 import java.io.FileInputStream;
 4 import java.io.IOException;
 5 import java.sql.Connection;
 6 import java.sql.SQLException;
 7 import java.util.Properties;
 8 
 9 import javax.sql.DataSource;
10 
11 import org.apache.commons.dbcp2.BasicDataSourceFactory;
12 
13 /**
14  * DBCP配置類
15  * @author SUN
16  */
17 public class KCYDBCPUtil {
18     
19     private static Properties properties = new Properties();
20     private static DataSource dataSource;
21     //加載DBCP配置文件
22     static{
23         try{
24             FileInputStream is = new FileInputStream("config/dbcp.properties");  
25             properties.load(is);
26         }catch(IOException e){
27             e.printStackTrace();
28         }
29         
30         try{
31             dataSource = BasicDataSourceFactory.createDataSource(properties);
32         }catch(Exception e){
33             e.printStackTrace();
34         }
35     }
36     
37     //從連接池中獲取一個連接
38     public static Connection getConnection(){
39         Connection connection = null;
40         try{
41             connection = dataSource.getConnection();
42         }catch(SQLException e){
43             e.printStackTrace();
44         }
45         try {
46             connection.setAutoCommit(false);
47         } catch (SQLException e) {
48             e.printStackTrace();
49         }
50         return connection;
51     }
52     
53     public static void main(String[] args) {
54         getConnection();
55     }
56 }

 

   3) 創建使用JDBC獲取數據庫連接的類DBConn.java(用於和DBCP連接池對比)

 1 package dbcp;
 2 
 3 import java.sql.Connection;
 4 import java.sql.DriverManager;
 5 
 6 public class DBConn {
 7     private static Connection conn = null;
 8     
 9     //獲取一個數據庫連接
10     public static Connection getConnection() {
11         try {
12             Class.forName("com.mysql.jdbc.Driver");
13             DriverManager.registerDriver(new com.mysql.jdbc.Driver());
14             String dbUrl = "jdbc:mysql://127.0.0.1:3306/mydb";
15             conn = DriverManager.getConnection(dbUrl, "sa", "123456");
16 //            System.out.println("========數據庫連接成功========");
17         } catch (Exception e) {
18             e.printStackTrace();
19 //            System.out.println("========數據庫連接失敗========");
20             return null;
21         }
22         return conn;
23     }
24 }

 

   4) 創建測試類DBCPTest.java

    測試類中采用3中方法將2000個數據插入數據庫同一張表中,每次插入數據之前,先清空表,並對結果進行了對比。

    3中插入數據方法如下:

    (1) 每次插入一條數據前,就創建一個連接,該條數據插入完成后,關閉該連接;

      (2) 使用DBCP連接池,每次插入一條數據前,從DBCP連接池中獲取一條連接,該條數據插入完成后,該連接交由DBCP連接池管理;

    (3) 在插入數據之前創建一條連接,2000個數據全部使用該連接,2000個數據插入完畢后,關閉該連接。

 1 package dbcp;
 2 
 3 import java.sql.Connection;
 4 import java.sql.SQLException;
 5 import java.sql.Statement;
 6 
 7 import org.junit.Test;
 8 
 9 public class DBCPTest {
10     
11     //測試,每寫一條數據前,就新建一個連接
12     @Test
13     public void testWriteDBByEveryConn() throws Exception{
14         for(int i = 0; i < 2000; i++){
15             writeDBByEveryConn(i);
16         }
17         System.out.println("DONE");
18         
19     }
20     
21     //測試,使用連接池,每寫一條數據前,從連接池中獲取一個連接
22     @Test
23     public void testWriteDBByDBCP() throws Exception{
24         for(int i = 0; i < 2000; i++){
25             writeDBByDBCP(i);
26         }
27         System.out.println("DONE");
28     }
29     
30     //測試,只建一條連接,寫入所有數據
31     @Test
32     public void testWriteDBByOneConn() throws Exception{
33         Connection conn = DBConn.getConnection();
34         Statement stat = conn.createStatement();
35         for(int i = 0; i < 2000; i++){
36             writeDBByOneConn(i, stat);
37         }
38         conn.close();
39         System.out.println("DONE");
40     }
41     
42     //不使用連接池寫數據庫,每寫一條數據創建一個連接
43     public void writeDBByEveryConn(int data){
44         String sql = "insert into dbcp values (" + data + ")"; 
45         Connection conn = DBConn.getConnection();
46         try{    
47             Statement stat = conn.createStatement();
48             stat.executeUpdate(sql);            
49         }catch(Exception e){
50             e.printStackTrace() ;
51         }finally{
52             try {    
53                 conn.close();    
54             } catch (SQLException e) {
55                 e.printStackTrace();
56             }
57             
58         }
59     }
60     
61     //不使用連接池寫數據庫,只用一個連接,寫所有數據
62     public void writeDBByOneConn(int data, Statement stat){
63         String sql = "insert into dbcp values (" + data + ")"; 
64         try{    
65             stat.executeUpdate(sql);            
66         }catch(Exception e){
67             e.printStackTrace() ;
68         }
69     }
70     
71     //通過DBCP連接池寫數據庫
72     public void writeDBByDBCP(int data){ 
73         String sql = "insert into dbcp values (" + data + ")"; 
74         try {
75             Connection conn = KCYDBCPUtil.getConnection();  
76             Statement stat = conn.createStatement();
77             stat.executeUpdate(sql);
78             conn.commit();
79             conn.close();
80         } catch (SQLException e) {   
81             e.printStackTrace();  
82         }
83     }
84 
85 } 

  測試結果如下:

  (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)方法修改為如下:

 1     //通過DBCP連接池寫數據庫
 2     public void writeDBByDBCP(int data){ 
 3         String sql = "insert into dbcp values (" + data + ")"; 
 4         try {
 5             Connection conn = KCYDBCPUtil.getConnection();  
 6             Statement stat = conn.createStatement();
 7             stat.executeUpdate(sql);
 8             conn.commit();
 9 //            conn.close();
10         } catch (SQLException e) {   
11             e.printStackTrace();  
12         }
13     }

 

    重新執行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條數據時報錯。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM