开源数据库连接池之C3P0

  本篇介绍几种开源数据库连接池,同时重点讲述如何使用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 }
View Code

  在上面的代码中,在该工具类一加载进内存时就利用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 }
View Code

在控制台上显示的效果如下:

  

  红字信息是因为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>
View Code

  在官方给出的配置文件例子中,有默认配置和自定义配置两种:

  

  ⑴ 如果想用默认配置,则在程序中只要在创建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>
View Code

  在这个配置文件中,默认配置是使用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 }
View Code

  在这个代码中,通过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 }
View Code

  在控制台效果如下,我们照样从C3P0连接池中获取到了连接:

  

  以上就是我们对开源数据库连接池C3P0的整个学习和使用的过程。如果想对C3P0有更深入的理解,上面说过的文档可以是很好的学习方式。

 

参考博客:

      http://weifly.iteye.com/blog/1227182

 

 

 

 

                  

posted @ 2016-03-13 20:58  fjdingsd  阅读(2691)  评论(1编辑  收藏  举报