C3P0的使用
一、什么是C3P0连接池
开源的JDBC连接池,C3P0连接池是在程序操作数据库之前预先根据配置文件创建一定数量的连接,当线程需要时直接取走,缩短了创建连接的时间,当使用完毕后,释放连接后放回连接池,以此类推,如果连接池中的连接使用完后,程序会根据配置文件配置的数据再次创建一批,使用完后放回连接池,并不是真正的关闭连接。
二、使用C3P0(数据库连接池)的必要性
当我们在进行基于数据库的web程序开发时,我们可以先在主程序(如Servlet、Bean)中通过JDBC中的DriverManager建立数据库连接,然后将要对数据库进行操作的sql语句封装到Statement中,最后在返回结果集后断开数据库连接。以上是较为传统的开发模式,然而用这种模式开发会埋下严重的安全隐患
三、数据库连接池的详细说明
数据库连接池的基本原理就是为数据库建立一个缓冲池。在缓冲池中先创建指定数量的数据库连接,当有连接请求时就从缓冲池中取出处于“空闲”状态的连接,并将此连接标记为“忙碌”,直到该请求进程结束后,它所使用的连接才会重新回到“空闲”状态,并等待下一次请求调用。
数据库连接池的主要作用就是负责分配、管理和释放数据库连接,它允许程序重复使用同一个现有的数据库连接,大大缩短了运行时间,提高了执行效率。
这里需要强调一点的是,数据库连接池中的连接数是在其初始化时根据c3p0-config.xml中的最小连接数来确定的。当然,无论连接池的连接数是否有被使用,它都至少会保持最小连接数,如果请求连接数超过最小连接数也会根据c3p0-config.xml中指定的自增长数增加连接数直到达到最大连接数,这时如果请求连接数量还是大于连接池中的连接数的话,剩下的请求将会被放入等待队列直到有空闲连接出现。
这样一来,数据库连接池相较于传统JDBC模式等到请求发出才创建连接的做法有着显而易见的优势。
四、C3P0实操
1.导入jar包
项目下创建一个lib目录,选择jar右击选择Add as Library
2.配置xml文件
配置文件要求:
(1) 文件名称:必须叫c3p0-config.xml
(2) 文件位置:必须在src下
3.c3p0-config.xml模板
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<!-- 默认配置 -->
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/test01?useSSL=false</property>
<property name="user">root</property>
<property name="password">root</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="mysql">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/test01?useSSL=false</property>
<property name="user">root</property>
<property name="password">root</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>
</named-config>
<named-config name="oracle">
</named-config>
<named-config name="SqlServer">
</named-config>
</c3p0-config>
4.创建JDBCUtil类
public class JdbcUtil {
// 创建c3p0连接池对象
private static ComboPooledDataSource poll = new ComboPooledDataSource("mysql");
// 提供线程局部变量,每个线程(通过其get或set方法)都有自己独立初始化的变量副本
// ThreadLocal是根据当前线程对象取值,所以多线程各自的Connection对象不会受到影响
// cons:仅仅保存事务相关的Connection
private static ThreadLocal<Connection> cons = new ThreadLocal<>();
public static DataSource getDataSource(){
return poll;
}
/* 连接数据库 */
public static Connection getConnection(){
Connection con = cons.get();
// 1.非事务Connection
if(con == null){
// 目前没有涉及事务
try {
return poll.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
}
// 2.涉及事务Connection
return cons.get();
}
/* 开启事务 */
public static void beginTransaction() throws Throwable {
Connection con = cons.get();
if(con != null){
// 事务已经开启过
throw new Throwable("事务已经开启过,不能重复开启");
}
con = poll.getConnection();
// 开启事务
con.setAutoCommit(false);
cons.set(con);
}
/* 提交事务 */
public static void commitTransaction() throws Throwable {
Connection con = cons.get();
if(con == null){
throw new Throwable("事务还未开启,不能提交事务");
}
con.commit();
cons.remove();
}
/* 回滚事务 */
public static void rollBackTransaction() throws Throwable {
Connection con = cons.get();
if(con == null){
throw new Throwable("事务还未开启,不能回滚事务");
}
con.rollback();
cons.remove();
}
/* 释放资源 */
public static void release(Connection con) throws SQLException {
if(con == null)
return;
// 当前线程无事务
if(cons.get() == null)
con.close();
// 非事务的Connection
if(con != cons.get())
con.close();
}
总结
1.相较于JDBC,使用C3P0能够更加高效地建立与数据库的连接,尤其是在高并发随机访问数据库的时候;
2.C3P0通过dataSource.getConnection()从线程池中获取“空闲”连接,真正的数据库连接创建与释放则是由C3P0在后台自行完成的,我们只花费了获取和释放连接占用权的时间;
3.使用c3p0-config.xml代替原来JDBC硬编码的形式,提高了代码复用性。