本篇介紹幾種開源數據庫連接池,同時重點講述如何使用C3P0數據庫連接池。
之前的博客已經重點講述了使用數據庫連接池的好處,即是將多次創建連接轉變為一次創建而使用長連接模式。這樣能減少數據庫創建連接的消耗。正是由於數據庫連接池的思想非常重要,所以市面上也有很多開源的數據庫連接池供我們使用。主要有以下三個:
DBCP數據庫連接池
C3P0 數據庫連接池
Tomcat內置的數據庫連接池(DBCP)
本篇主要講述C3P0數據庫連接池的使用,關於另外兩個數據庫連接池的用法請看《開源數據庫連接池之DBCP》 、《開源數據庫連接池之Tomcat內置連接池》 。如果我們使用這些開源的數據庫連接池,我們就可以省略像前一篇博客中自己創建數據庫連接池的步驟,這樣會省略我們很多事。
C3P0的官網是http://sourceforge.net/projects/c3p0/?source=navbar ,比較不好找。
C3P0實現了連接池和JNDI的綁定,支持JDBC3規范和JDBC2的標准擴展。C3P0與DBCP的區別在於DBCP沒有自動回收空閑連接的功能,而C3P0卻有。但是C3P0在從連接池中獲取和返回連接對象的時候,采用了異步處理方式(即非線程安全,關於異步可以看這篇很好的文章http://www.cnblogs.com/xiohao/p/4385508.html )。
要想了解更多關於C3P0概念的信息,可以通過下載的C3P0的包中的【doc】目錄下的index.html來查看關於C3P0的一些信息:

這里面有一些很有幫助的文檔,例如快速入門(QuickStart)等等,這里就簡單介紹一下:

要使用C3P0同樣需要下載其jar包,在C3P0的jar包中共有三個jar包,如下圖所示:

如果使用的是非Oracle數據庫,則只需導入c3p0-0.9.5.2jar包和mechange-commons-java-0.2.11.jar包即可,如果是使用Oracle數據庫的話,還需要導入c3p0-oracle-thin-extras-0.9.5.2.jar包。
C3P0可以有兩種使用方法,一種是直接在程序中以調用ComboPooledDataSource對象的一系列方法配置各種數據庫和連接池的參數,另一種也是跟DBCP一樣使用配置文件來初始化數據庫和連接池。
例1:使用第一種方式來簡單創建C3P0數據庫連接池
在本例中我們僅使用C3P0的連接池類ComboPooledDataSource的對象來設置各個數據庫驅動和連接池的參數,這種方法無需配置文件,換句話說也就是在程序中“寫死”各個配置信息。
創建一個工程,因為我們使用的是MySQL數據庫,因此只需要導入C3P0中的兩個jar包即可,當然別忘了還有MySQL的數據庫驅動jar包:

同前一篇博客一樣,我們現在是使用數據庫連接池來獲取連接了,而不是通過數據庫直接提供的連接,因此《JDBC操作數據庫的學習(2)》中的數據庫工具類JdbcUtils的部分方法已經不適用了,現在我們重新在剛建的工程中編寫一個新的JdbcUtils工具類:
1 public class JdbcUtils { 2 private static ComboPooledDataSource ds = null; 3 static{ 4 try { 5 ds = new ComboPooledDataSource(); 6 ds.setDriverClass("com.mysql.jdbc.Driver"); //為C3P0配置MySQL數據庫驅動
7 ds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbcdemo"); //為C3P0配置MySQL數據庫URL
8 ds.setUser("root"); 9 ds.setPassword("root"); 10 ds.setMaxPoolSize(50); //設置連接池最大連接數
11 ds.setMinPoolSize(5); //設置連接池最小連接數
12 ds.setInitialPoolSize(10); //設置連接池初始化連接數
13
14 } catch (PropertyVetoException e) { 15 throw new ExceptionInInitializerError(e); 16 } 17 } 18
19 public static Connection getConnection() throws SQLException { 20 return ds.getConnection(); //使用ComboPooledDataSource對象獲取連接
21 } 22
23 public static void release(Connection conn,Statement st,ResultSet rs) { 24 if(rs!=null) { 25 try{ 26 rs.close(); 27 }catch (Exception e) { 28 e.printStackTrace(); 29 } 30 } 31 if(st!=null) { 32 try{ 33 st.close(); 34 }catch (Exception e) { 35 e.printStackTrace(); 36 } 37 } 38 if(conn!=null) { 39 try{ 40 conn.close(); 41 }catch (Exception e) { 42 e.printStackTrace(); 43 } 44 } 45 } 46 }
在上面的代碼中,在該工具類一加載進內存時就利用C3P0的連接池類ComboPooledDataSource的對象來設置各個數據庫驅動和連接池的參數,例如上面我們設置了數據庫連接驅動、數據庫URL、數據庫用戶名和密碼、連接池里的最大和最小連接數,連接池初始化時的連接數等等,當然上面的配置只是ComboPooledDataSource對象中設置方法的冰山一角,我們還可以通過ComboPooledDataSource對象的方法為我們的連接池設置更多的功能和參數。
而我們要給別的想操作數據庫的方法返回的連接即使通過ComboPooledDataSource對象的getConnection()方法取得的Connection對象,另外通過釋放資源的方法還是和以前一模一樣,尤其是調用了Connection對象的close方法就能知道,這個Connection對象必定經過C3P0進行功能增強,將數據庫直接提供的Connection對象的close方法進行了覆寫,才能使我們釋放資源時(調用Connection對象的close方法)不會將連接銷毀,而是重新放入C3P0的連接池中。
下面我們將通過一個測試代碼來看看通過C3P0連接池獲得的Connection對象:
1 public void testConnection() throws SQLException { 2 Connection conn = null; 3 PreparedStatement st = null; 4 ResultSet rs = null; 5
6 try{ 7 conn = JdbcUtils.getConnection(); 8 System.out.println(conn); 9 System.out.println(conn.getClass().getName()); 10 }finally{ 11 JdbcUtils.release(conn, st, rs); 12 } 13 }
在控制台上顯示的效果如下:

紅字信息是因為C3P0在創建數據庫連接池時會通過日志來記錄其工作的一些信息,我們也可以通過這個信息來查看C3P0創建的連接池的情況。
最后兩行是我們通過上面的測試代碼打印出來的Connection對象的信息,可以看到C3P0也將數據庫驅動直接提供的Connection對象進行了功能增強,而這種增強方式是通過動態代理方式來覆寫了原來Connection對象的close方法再返回給我們的,以使我們在釋放資源時能將連接重新返回到C3P0的連接池中。
例2:使用第二種方式來簡單創建C3P0數據庫連接池
和第一種方式不同,在本例中我們使用配置文件的方式使C3P0能配置我們的數據庫驅動和連接池所需要的參數。這種方式的好處就是不會在程序里將這些參數“寫死”。
C3P0的配置文件里使用什么參數關鍵字呢?配置文件有沒特殊的命名方式呢?配置文件需要放置在什么特別的地方嗎?這三個問題是使用C3P0連接池第二種方式必須要知道的。
先說配置文件里使用的參數關鍵字,這個可以由ComboPooledDataSource對象中的各種set方法得到,比如這個對象中的setDriverClass方法,那么使用配置文件的話配置數據庫驅動類的參數即為driverClass。另一種參看C3P0配置參數的方式就是看上面曾經說過的C3P0的包中【doc】目錄下的index.html文檔,在這個文檔找到附錄B(Appendix B:Configuration Files)有如下配置參數:

在這張表的下面還有對每一個參數的各種介紹功能和一些默認值,這里就省略不貼圖出來了。
接下來就是配置文件了,和DBCP不一樣,C3P0必須使用XML來作為配置文件,而且配置文件名和應該放置的位置都有嚴格的規定:

首先C3P0的配置文件必須要叫“c3p0-config.xml”,另外根據文檔,這個配置文件必須要直接或者以jar包的形式存放在你應用的CLASSPATH路徑或者WEB-INF/classes路徑(WEB工程)下才行。當然我們知道在MyEclipse上如果將配置文件放在【src】目錄中在IED編譯運行時會自動將【src】中的文件放置在CLASSPATH路徑中,所以我們可以直接將配置文件放在【src】目錄里。
而文檔也提供了一個配置文件中參數內容的例子:
<c3p0-config>
<default-config>
<property name="automaticTestTable">con_test</property>
<property name="checkoutTimeout">30000</property>
<property name="idleConnectionTestPeriod">30</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
<user-overrides user="test-user">
<property name="maxPoolSize">10</property>
<property name="minPoolSize">1</property>
<property name="maxStatements">0</property>
</user-overrides>
</default-config>
<!-- This app is massive! -->
<named-config name="intergalactoApp">
<property name="acquireIncrement">50</property>
<property name="initialPoolSize">100</property>
<property name="minPoolSize">50</property>
<property name="maxPoolSize">1000</property>
<!-- intergalactoApp adopts a different approach to configuring statement caching -->
<property name="maxStatements">0</property>
<property name="maxStatementsPerConnection">5</property>
<!-- he's important, but there's only one of him -->
<user-overrides user="master-of-the-universe">
<property name="acquireIncrement">1</property>
<property name="initialPoolSize">1</property>
<property name="minPoolSize">1</property>
<property name="maxPoolSize">5</property>
<property name="maxStatementsPerConnection">50</property>
</user-overrides>
</named-config>
</c3p0-config>
在官方給出的配置文件例子中,有默認配置和自定義配置兩種:

⑴ 如果想用默認配置,則在程序中只要在創建ComboPooledDataSource對象時調用其無參的構造器即可,例如ComboPooledDataSource ds = new ComboPooledDataSource()即是使用默認配置。
⑵ 如果是想使用自定義配置,則在創建ComboPooledDataSource對象時將自定義配置的<named-config>指定的名稱作為參數傳進ComboPooledDataSource的構造器即可,例如按上圖的例子來說ComboPooledDataSource ds = new ComboPooledDataSource(“intergalactoApp”)。因此這個配置文件可以配置多個自定義的參數內容,非常靈活,比如我們可以在一個C3P0配置文件中分別自定義MySQL數據庫和Oracle數據庫的配置參數。
現在我們開始真正地使用第二種方式來使用C3P0連接池,創建一個工程,因為我們使用的是MySQL數據庫,因此只需要導入C3P0中的兩個jar包即可,當然別忘了還有MySQL的數據庫驅動jar包:

這回我們在【src】目錄中放置C3P0的配置文件c3p0-config.xml,內容以文檔案例做了修改如下:
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcdemo</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">50</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
</default-config>
<!-- if you want to use Oracle database -->
<named-config name="oracle">
<property name="driverClass">oracle.jdbc.driver.OracleDriver</property>
<property name="jdbcUrl"> jdbc:oracle:thin:@localhost:1521:jdbcdemo</property>
<property name="user">root</property>
<property name="password">root</property>
<property name="initialPoolSize">10</property>
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">50</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
</named-config>
</c3p0-config>
在這個配置文件中,默認配置是使用MySQL數據庫,也設置了一個自定義配置可以使用Oracle數據庫。
同例1一樣,我們也是要改寫以前的數據庫工具類JdbcUtils,代碼如下:
1 public class JdbcUtils { 2 private static ComboPooledDataSource ds = null; 3 static{ 4 try { 5 ds = new ComboPooledDataSource(); //使用配置文件中的默認配置 6 // ds = new ComboPooledDataSource("oracle"); 如果要使用Oracle則使用配置文件中的自定義配置
7
8 } catch (Exception e) { 9 throw new ExceptionInInitializerError(e); 10 } 11 } 12
13 public static Connection getConnection() throws SQLException { 14 return ds.getConnection(); //使用ComboPooledDataSource對象獲取連接
15 } 16
17 public static void release(Connection conn,Statement st,ResultSet rs) { 18 if(rs!=null) { 19 try{ 20 rs.close(); 21 }catch (Exception e) { 22 e.printStackTrace(); 23 } 24 } 25 if(st!=null) { 26 try{ 27 st.close(); 28 }catch (Exception e) { 29 e.printStackTrace(); 30 } 31 } 32 if(conn!=null) { 33 try{ 34 conn.close(); 35 }catch (Exception e) { 36 e.printStackTrace(); 37 } 38 } 39 } 40 }
在這個代碼中,通過ComboPooledDataSource獲取C3P0的連接池對象,因為我們在創建該對象時沒有在構造器中傳入參數,因此使用的是默認配置,而我們在配置文件中的默認設置也就是使用MySQL數據庫。
當然獲取連接的getConnection方法和釋放資源的release方法都還和例1 一樣,通過連接池對象ComboPooledDataSource獲取連接,而釋放資源跟以前的代碼沒有任何區別,說明在釋放資源調用Connection對象的close方法時,其實已經是被ComboPooledDataSource返回的另一種Connection對象覆寫了close方法。
我們通過下面的代碼再來試下通過配置文件的方式使用C3P0的連接好不好使:
1 public void testConnection() throws SQLException { 2 Connection conn = null; 3 PreparedStatement st = null; 4 ResultSet rs = null; 5
6 try{ 7 conn = JdbcUtils.getConnection(); 8 System.out.println(conn); 9 System.out.println(conn.getClass().getName()); 10 }finally{ 11 JdbcUtils.release(conn, st, rs); 12 } 13 }
在控制台效果如下,我們照樣從C3P0連接池中獲取到了連接:

以上就是我們對開源數據庫連接池C3P0的整個學習和使用的過程。如果想對C3P0有更深入的理解,上面說過的文檔可以是很好的學習方式。
參考博客:
http://weifly.iteye.com/blog/1227182

