一、基本定义
C3P0是一个开源的JDBC连接池,它实现了数据源与JNDI绑定,支持JDBC3规范和实现了JDBC2的标准扩展说明的Connection和Statement池的DataSources对象。
即将用于连接数据库的连接整合在一起形成一个随取随用的数据库连接池(Connection pool)。
二、使用C3P0(数据库连接池)的必要性
当我们在进行基于数据库的web程序开发时,我们可以先在主程序(如Servlet、Bean)中通过JDBC中的DriverManager建立数据库连接,然后将要对数据库进行操作的sql语句封装到Statement中,最后在返回结果集后断开数据库连接。以上是较为传统的开发模式,然而用这种模式开发会埋下严重的安全隐患。
1.JDBC传统模式开发存在的主要问题
1.1>时间和内存资源消耗巨大
普通的JDBC数据库连接使用DriverManager来获取,每次向数据库建立连接的时候都要将Connection加载到内存中,再根据JDBC代码(或配置文件)中的用户名和密码进行验证其正确性。这一过程一般会花费0.05~1s,一旦需要数据库连接的时候就必须向数据库请求一个,执行完后再断开连接。显然,如果同一个数据库在同一时间有数十人甚至上百人请求连接势必会占用大量的系统资源,严重的会导致服务器崩溃。
1.2>有内存泄漏的风险
因为每一次数据库连接使用完后都需要断开连接,但如果程序出现异常致使连接未能及时关闭,这样就可能导致内存泄漏,最终只能以重启数据库的方法来解决;
另外使用传统JDBC模式开发不能控制需要创建的连接数,系统一般会将资源大量分出给连接以防止资源不够用,如果连接数超出一定数量也会有极大的可能导致内存泄漏。
三、数据库连接池的详细说明
为了解决由使用传统开发模式创建连接导致的一系列问题,我们可以采用数据库连接池技术。
数据库连接池的基本原理就是为数据库建立一个缓冲池。在缓冲池中先创建指定数量的数据库连接,当有连接请求时就从缓冲池中取出处于“空闲”状态的连接,并将此连接标记为“忙碌”,直到该请求进程结束后,它所使用的连接才会重新回到“空闲”状态,并等待下一次请求调用。
从上面不难看出数据库连接池的主要作用就是负责分配、管理和释放数据库连接,它允许程序重复使用同一个现有的数据库连接,大大缩短了运行时间,提高了执行效率。
这里需要强调一点的是,数据库连接池中的连接数是在其初始化时根据c3p0-config.xml中的最小连接数来确定的,关于c3p0-config.xml我会在后文提供模板以供大家参考。当然,无论连接池的连接数是否有被使用,它都至少会保持最小连接数,如果请求连接数超过最小连接数也会根据c3p0-config.xml中指定的自增长数增加连接数直到达到最大连接数,这时如果请求连接数量还是大于连接池中的连接数的话,剩下的请求将会被放入等待队列直到有空闲连接出现。
这样一来,数据库连接池相较于传统JDBC模式等到请求发出才创建连接的做法有着显而易见的优势。
四、使用连接池的明显优势
1.资源的高效利用
由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销,减小了系统资源消耗的同时也提高了系统运行环境的平稳性。
2.更快的系统反应速度
数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接可以避免数据库在连接初始化和释放过程所需的时间开销,从而减少了系统的响应时间,提高了系统的反应速度。
3.减少了资源独占的风险
新的资源分配手段对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置实现对某一应用最大可用数据库连接数的限制,避免了应用独占所有数据库资源的风险。
4.统一的连接管理,避免数据库连接泄露
在实现较为完善的数据库连接池时,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露。
——————————————我是一条优雅的分割线——————————————
虽然数据库连接池(Connection pool)种类很多,并不仅限于c3p0这一个,像DBCP、BoneCP、Proxool、SmartPool、MiniConnectionPoolManager等等都是较为常用的,c3p0只是其中较为优秀且使用人数较多的一款,因为标题的原因这里只说c3p0。
五、C3P0实操
1.导入jar包
主要是c3p0和mysql,其他根据需求添加
2.配置xml文件
下面是我配置的c3p0-config.xml,可以作为模板以供大家参考:
3. 一般c3p0-config.xml模板
当然,除了以上这几种常用的参数设置以外,这里还有一份有关c3p0-config.xml参数的详细清单,如有需要可自行增加。
4.c3p0-config.xml参数清单
5.创建C3P0Util类
package com.c3p0.utils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class C3P0Util {
//使用ComboPooledDataSource来生成DataSource的实例
private static DataSource dataSource = new ComboPooledDataSource();
//从连接池中获取连接
public static Connection getConnection() {
try {
return dataSource.getConnection();
} catch (SQLException e) {
// TODO Auto-generated catch block
throw new RuntimeException();
}
}
//释放连接回连接池
public static void release(Connection conn, Statement stmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (Exception e) {
e.printStackTrace();
}
rs = null;
}
if (stmt != null) {
try {
stmt.close();
} catch (Exception e) {
e.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
conn = null;
}
}
}
6.创建user表和类
package com.c3p0.utils;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable {
private int id;
private String username;
private String password;
private String email;
private Date birthday;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User [id=" + id + ", username=" + username + ", password="
+ password + ", email=" + email + ", birthday=" + birthday
+ "]";
}
}
(注:后面的测试程序也是调用此表,我们就对表中的数据进行了封装,后台数据库也是此类结构,这里不再展示)
7.测试程序
package com.c3p0.utils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
public class TestCRUD {
@Test
public void testInsert() {
Connection conn = null;
PreparedStatement ps = null;
conn = C3P0Util.getConnection();
try {
ps = conn.prepareStatement("INSERT INTO users (username,PASSWORD,email,birthday)VALUES('SUN99','123','123456@qq.com','2020-01-01')");
ps.executeUpdate();
System.out.println("添加操作执行成功!");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("添加操作执行失败!");
} finally {
C3P0Util.release(conn, ps, null);
}
}
@Test
public void testSelect() {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
conn = C3P0Util.getConnection();
try {
ps = conn.prepareStatement("Select * from users");
rs = ps.executeQuery();
List
while (rs.next()) {
User u = new User();
u.setId(rs.getInt(1));
u.setUsername(rs.getString(2));
u.setPassword(rs.getString(3));
u.setEmail(rs.getString(4));
u.setBirthday(rs.getDate(5));
list.add(u);
}
for (User user : list) {
System.out.println(user.toString());
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
C3P0Util.release(conn, ps, rs);
}
}
@Test
public void testDelete() {
Connection conn = null;
PreparedStatement ps = null;
conn = C3P0Util.getConnection();
try {
ps = conn.prepareStatement("delete from users where username='SUN99'");
ps.executeUpdate();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
C3P0Util.release(conn, ps, null);
}
}
@Test
public void testUpdate() {
Connection conn = null;
PreparedStatement ps = null;
conn = C3P0Util.getConnection();
try {
ps = conn.prepareStatement("UPDATE users SET username='SUN100',PASSWORD='456'WHERE id='1'");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
C3P0Util.release(conn, ps, null);
}
}
}
六、学习总结
1.相较于JDBC,使用C3P0能够更加高效地建立与数据库的连接,尤其是在高并发随机访问数据库的时候;
2.C3P0通过dataSource.getConnection()从线程池中获取“空闲”连接,真正的数据库连接创建与释放则是由C3P0在后台自行完成的,我们只花费了获取和释放连接占用权的时间;
3.使用c3p0-config.xml代替原来JDBC硬编码的形式,提高了代码复用性。