学科分类
目录
Java Web

C3P0数据源

C3P0是目前最流行的开源数据库连接池之一,它实现了DataSource数据源接口,支持JDBC2和JDBC3的标准规范,易于扩展并且性能优越,著名的开源框架Hibernate和 Spring使用的都是该数据源。我们在使用C3P0数据源开发时,需要了解C3P0中DataSource接口的实现类ComboPooledDataSource,它是C3P0的核心类,提供了数据源对象的相关方法,具体如表1所示。

表1 ComboPooledDataSource类的常用方法

方法名称 功能描述
void setDriverClass() 设置连接数据库的驱动名称
void setJdbcUrl() 设置连接数据库的路径
void setUser() 设置数据库的登陆账号
void setPassword() 设置数据库的登录密码
void setMaxPoolSize() 设置数据库连接池最大的连接数目
void setMinPoolSize() 设置数据库连接池最小的连接数目
void setInitialPoolSize() 设置数据库连接池初始化的连接数目
Connection getConnection() 从数据库连接池中获取一个连接

通过表1发现,发现C3P0和DBCP数据源所提供的方法大部分功能相同,都包含了设置数据库连接信息的方法和数据库连接池初始化的方法,以及DataSource接口中的getConnection()方法。

当使用C3P0数据源时,首先得创建数据源对象,创建数据源对象可以使用ComboPooledDataSource类,该类有两个构造方法,分别是ComboPooledDataSource()和ComboPooledDataSource(String configName)。接下来,通过两个案例分别讲解上述构造方法是如何创建数据源对象的,具体如下:

1、通过ComboPooledDataSource**类直接创建数据源对象**

使用ComboPooledDataSource类直接创建一个数据源对象,手动给数据源对象设置属性值,然后获取数据库连接对象,具体步骤如下:

(1)在工程chapter02中导入mysql-connector-java-5.0.8-bin.jar、c3p0-0.9.1.2.jar两个jar包,然后在cn.itcast.example包下创建一个Example04类,如例1所示。

例1 Example04.java

 1  package cn.itcast.example;

 2  import java.sql.SQLException;

 3  import javax.sql.DataSource;

 4  import com.mchange.v2.c3p0.ComboPooledDataSource;

 5  public class Example04 {

 6    public static DataSource ds = null;

 7    // 初始化C3P0数据源

 8    static {

 9      ComboPooledDataSource cpds = new ComboPooledDataSource();

 10     // 设置连接数据库需要的配置信息

 11     try {

 12       cpds.setDriverClass("com.mysql.jdbc.Driver");

 13       cpds.setJdbcUrl("jdbc:mysql://localhost:3306/chapter02");

 14       cpds.setUser("root");

 15       cpds.setPassword("itcast");

 16       // 设置连接池的参数

 17       cpds.setInitialPoolSize(5);

 18       cpds.setMaxPoolSize(15);

 19       ds = cpds;

 20     } catch (Exception e) {

 21       throw new ExceptionInInitializerError(e);

 22     }

 23   }

 24   public static void main(String[] args) throws SQLException {

 25     // 获取数据库连接对象

 26     System.out.println(ds.getConnection());

 27   }

 28 }

程序的运行结果如图1所示。

图1 运行结果

从图1中可以看出,C3P0数据源对象成功获取到了数据库连接对象。

2、通过读取配置文件创建数据源对象

通过ComboPooledDataSource(String configName)构造方法读取c3p0-config.xml配置文件,创建数据源对象,然后获取数据库连接对象,具体步骤如下:

(1) 在src根目录下创建一个c3p0-config.xml文件,如例2所示。

例2 c3p0-config.xml

<?xml version="1.0" encoding="UTF-8"?>

<c3p0-config>

 <default-config>

   <property name="user">root</property>

   <property name="password">itcast</property>

   <property name="driverClass">com.mysql.jdbc.Driver</property>

   <property name="jdbcUrl">

jdbc:mysql://localhost:3306/chapter02</property>

   <property name="checkoutTimeout">30000</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>

 </default-config> 

 <named-config name="itcast">

   <property name="initialPoolSize">5</property>

   <property name="maxPoolSize">15</property>

   <property name="driverClass">com.mysql.jdbc.Driver</property>

   <property name="jdbcUrl">

jdbc:mysql://localhost:3306/chapter02</property>

   <property name="user">root</property>

   <property name="password">itcast</property>

 </named-config>

</c3p0-config>

在例2中,c3p0-config.xml配置了两套数据源,<default-config>…< /default-config >中的信息是默认配置,在没有指定配置时默认使用该配置创建c3p0数据源对象;<named-config>…</ named-config >中的信息是自定义配置,一个配置文件中可以有零个或多个的自定义配置,当用户需要使用自定义配置时,调用ComboPooledDataSource(String configName)方法,传入<named-config>节点中name属性的值即可创建C3P0数据源对象。这种设置的好处是,当程序在后期更换数据源配置时,只需要修改构造方法中对应的name值即可。

(2) 在cn.itcast.example包下创建一个Example05类,如例3所示。

例3 Example05.java

 1  package cn.itcast.example;

 2  import java.sql.SQLException;

 3  import javax.sql.DataSource;

 4  import com.mchange.v2.c3p0.ComboPooledDataSource;

 5  public class Example05 {

 6    public static DataSource ds = null;

 7    // 初始化C3P0数据源

 8    static {

 9      // 使用c3p0-config.xml配置文件中的named-config节点中name属性的值

 10     ComboPooledDataSource cpds = new ComboPooledDataSource("itcast");

 11     ds = cpds;

 12   }

 13   public static void main(String[] args) throws SQLException {

 14     System.out.println(ds.getConnection());

 15   }

 16 }

程序的运行结果如图2所示。

图2 运行结果

从图2中可以看出,C3P0数据源对象成功获取到了数据库连接对象。需要注意的是,在使用ComboPooledDataSource(String configName)方法创建对象时必须遵循以下两点:

● 配置文件名称必须为c3p0-config.xml,并且位于该项目的src根目录下。

● 当传入的configName值为空或者不存在时,则会使用默认的配置方式创建数据源。

通过前两个小节的学习,熟悉了两种常用数据源的配置方法,并从各自的API中可以看出,数据源的实现类会增加一些设置数据库连接池对象属性的方法,这也是不同数据源实现类的本质区别。

多学一招:自定义一个类实现数据库连接池

通过DBCP和C3P0的学习,相信大家对数据源有了一定的了解,为了让大家更好地掌握数据源的工作原理,接下来,通过自定义一个类模拟实现数据库连接池,具体步骤如下:

(1)在chapter02工程中,创建cn.itcast.utils包,并新建一个JDBCUtils工具类,它的实现方式和第一章中提到的JDBCUtils工具类完全相同,这里就不再列出代码。接着创建cn.itcast.mydatasource包,并在该包下创建一个JdbcPool类,如例4所示。

例4 JdbcPool.java

 1  package cn.itcast.mydatasource;

 2  import java.io.PrintWriter;

 3  import java.sql.Connection;

 4  import java.sql.SQLException;

 5  import java.sql.SQLFeatureNotSupportedException;

 6  import java.util.LinkedList;

 7  import java.util.logging.Logger;

 8  import javax.sql.DataSource;

 9  import cn.itcast.utils.JDBCUtils;

 10 //自定义数据库连接池类需要实现DataSource接口,重写DataSource的方法

 11 public class JdbcPool implements DataSource {  

 12    // 自定义连接池

 13    private static LinkedList<Connection> pool = 

 14         new LinkedList<Connection>();

 15   // 初始化连接池

 16   static {

 17     try {

 18       for (int i = 0; i < 10; i++) {

 19         pool.add(getNewCon());// 初始化在连接池中添加10个连接对象

 20       }

 21     } catch (Exception e) {

 22       throw new ExceptionInInitializerError(e);// 抛出初始化错误对象

 23     }

 24   }

 25   //封装获取连接方法

 26   public static Connection getNewCon() throws SQLException{

 27      //传统创建数据库连接对象的方法

 28     return JDBCUtils.getConnection();

 29   }

 30   //从连接池中获取一个连接对象  

 31   @Override

 32   public Connection getConnection() throws SQLException {

 33     // 返回删除的LinkedList集合中的第一个连接对象

 34     Connection connection = pool.removeFirst();

 35      // 包装设计模式,包装Connection对象,重写close()方法

 36     MyConnection con = new MyConnection(connection, pool); 

 37     return con;

 38   }

 39    //获取数据库连接池

 40   public static LinkedList<Connection> getPool() {

 41     return pool;

 42   }

 43   ...

 44   .. //省略DataSource的其他重写方法

 45   .

 46 }

在例4中,定义了一个连接池pool用于保存Connection对象(连接池本质是一个集合),由于频繁的增删操作特性,所以选用LinkedList比较好。在类第一次加载的时候,自动创建10个连接对象存放在LinkedList中。当调用getConnection()方法时,会在LinkedList集合中获取一个对象并返回。这里除了使用LinkedList的removeFirst()方法每次获取第一个被删除对象以外,还可以使用JDK1.6新特性中LinkedList推出的pop()方法,比removeFirst()更加高效。

​ (2)在例4中第36行,MyConnection是自定义类,它实现了Connection接口,采用了包装设计模式,重写了Connection对象的close()方法,让数据库连接对象调用close()方式时,不是释放资源、关闭连接,而是把对象返回到连接池中。下面我们实现MyConnection类,在cn.itcast.mydatasource包下创建MyConnection类,如例5所示:

例5 MyConnection.java

 1  package cn.itcast.mydatasource;

 2  import java.sql.*;

 3  import java.util.Map;

 4  import java.util.Properties;

 5  import java.util.LinkedList;

 6  import java.util.concurrent.Executor;

 7  //自定义一个Connection对象,其他方法都调用原本Connection的方法,重写close方法

 8  public class MyConnection implements Connection{

 9      private Connection con;

 10     private LinkedList<Connection> pool; 

 11     //MyConnection的构造函数的参数包括连接对象和连接池对象

 12     public MyConnection (Connection con,LinkedList<Connection> pool){

 13       this.con = con ;

 14       this.pool = pool;

 15     }

 16     //不能真的关闭连接,而是把连接重新放回连接池中

 17     @Override

 18     public void close() throws SQLException {

 19       this.pool.addFirst(this.con); 

 20     }

 21   ...

 22   .. //省略Connection的其他重写方法

 23   .

 24 }

经过MyConnection包装后的Connection对象,调用close()方法释放资源时,就会执行close()方法,把连接对象返回到LinkedList集合中。

(3)在cn.itcast.example包下创建JdbcPoolTest类,用来测试自定义数据源是否创建成功,如例6所示:

例6 JdbcPoolTest.java

 1  package cn.itcast.example;

 2  import java.sql.Connection;

 3  import java.sql.SQLException;

 4  import java.util.LinkedList;

 5  import javax.sql.DataSource;

 6  import cn.itcast.mydatasource.JdbcPool;

 7  public class JdbcPoolTest {

 8    public static void main(String[] args) throws SQLException {

 9      // 获取数据源对象

 10     DataSource ds = new JdbcPool();

 11     // 获取连接池对象

 12     LinkedList<Connection> pool = JdbcPool.getPool();

 13     // 输出连接池中连接的个数

 14     System.out.println("初始化时连接池中的连接对象个数是:" + pool.size());

 15     // 获取一个数据库连接对象

 16     Connection conn = ds.getConnection();

 17     // 输出连接池中连接的个数

 18     System.out.println("获取一个连接对象时,连接池中的连接对象个数是:" 

 19             + pool.size());

 20     // 返还数据库连接对象

 21     conn.close();

 22     // 输出连接池中连接的个数

 23     System.out.println("返还数据库连接后,连接池中的连接对象个数是:" 

 24             + pool.size());

 25   }

 26 }

程序运行结果如图3所示:

图3 运行结果

从图3中可以看出,当数据源初始化时,连接池中连接对象个数是10;当从数据源中获取一个连接对象后,连接池中连接对象个数变成了9;当conn对象执行close()方法后,连接池中连接对象个数又变成了10。由此可见,自定义类JdbcPool实现了数据库连接池,具备管理连接池中对象的功能。

点击此处
隐藏目录