Java-C3P0连接池

概述

分析

实际开发中“获得连接”或“释放资源”是非常消耗系统资源的两个过程,为了解决此类性能问题,通常情况我们采用连接池技术,来共享连接Connection

概念

用池来管理Connection,这样可以重复使用Connection。有了池,所以我们就不用自己来创建 Connection’而是通过池来获取Connection对象。当使用完Connection后,调用Connection的close() 方法也不会真的关闭Connection,而是把Connection “归还”给池。池就可以再利用这个Connection 对象了。

规范

Java为数据库连接池提供了公共的接口:javax.sqI.DataSource,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池!

常见的连接池:DBCP、C3POO

自定义连接池

分析

根据我们对连接池简单的理解,如果我们要编写自定义连接池,需要完成以下步骤

  • 创建连接池实现(数据源),并实现接口 javax.sqI.DataSource 。因为我们只使用该接口中getConnection()方法,简化本案例,我们将自己提供方法,而没有实现接口
  • 提供一个集合,用于存放连接,因为移除/添加操作过多,所以选择LinkedList
  • 本案例在静态代码块中,为连接池初始化3个连接
  • 之后程序如果需要连接,调用实现类的getConnection(),本方法将从连接池(容器List)获得连接。为了保证当前连接只能提供给一个线程使用,所以我们需要将连接先从连接池中移除
  • 当用户使用完连接,释放资源时,不执行close()方法,而是将连接添加到连接池中

准备

  • 写好配置文件后,通过静态代码块加载配置文件信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
public class JDBCUtils_V3 {
private static String driver;
private static String url;
private static String username;
private static String password;

static {
try {
// 1.通过当前类获取类加载器
ClassLoader classLoader = JDBCUtils_V3.class.getClassLoader();
// 2.通过类加载器的方法获得一个输入流
InputStream is = classLoader.getResourceAsStream("db.properties");
// 3.创建一个properties对象
Properties props = new Properties();
// 4.加载输入流
props.load(is);
// 5.获取相关参数的值
driver = props.getProperty("driver");
url = props.getProperty("url");
username = props.getProperty("username");
password = props.getProperty("password");
} catch (IOException e) {
e.printStackTrace();
}

}

public static Connection getConnection() {
Connection conn = null;
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, username, password);
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}

public static void release(Connection conn, PreparedStatement pstmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (pstmt != null){
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class MyDataSource implements DataSource{

//1.创建1个容器用于存储Connection对象
private static LinkedList<Connection> pool = new LinkedList<Connection>();

//2.创建5个连接放到容器中去
static{
for (int i = 0; i < 5; i++) {
Connection conn = JDBCUtils.getConnection();
pool.add(conn);
}
}

/**
* 重写获取连接的方法
*/
@Override
public Connection getConnection() throws SQLException {
Connection conn = null;
//3.使用前先判断
if(pool.size()==0){
//4.池子里面没有,我们再创建一些
for (int i = 0; i < 5; i++) {
conn = JDBCUtils.getConnection();
pool.add(conn);
}
}
//5.从池子里面获取一个连接对象Connection
conn = pool.remove(0);
return conn;
}

/**
* 归还连接对象到连接池中去
*/
public void backConnection(Connection conn){
pool.add(conn);
}
}

自定义连接池:方法增强

自定义连接池中存在严重问题,用户调用getConnection()获得连接后,必须使用release()方法进行连接的归还,如果用户调用conn.close()将连接真正的释放,连接池中将出现无连接可用。

此时我们希望,即使用户调用了close()方法,连接仍归还给连接池。close()方法原有功能时释放资源,期望功能:将当前连接归还连接池。说明se()方法没有我们希望的功能,我们将对close() 方法进行增强,从而实现将连接归还给连接池的功能

方法增强总结

  • 继承:子类继承父类,将父类的方法进行复写,从而进行增强。
    • 使用前提:必须有父类,且存在继承关系
  • 装饰者设计模式:此设计模式专门用于增强方法。
    • 使用前提:必须有接口
    • 缺点:需要将接口的所有方法都实现
  • 动态代理:在运行时动态的创建代理类,完成增强操作。
    • 与装饰者相似使用前提:必须有接口
    • 难点:需要反射技术
  • 字节码增强:运行时创建目标类子类,从而进行增强常见
    • 第三方框架:cglib、javassist等

装饰者设计模式

设计模式:专门为解决某一类问题,而编写的固定格式的代码。

装饰者固定结构:接口A,己知实现类c,需要装饰者创建代理类B

  • 创建类B,并实现接口A
  • 提供类B的构造方法,参数类型为A,用于接收A接口的其他实现类(c)
  • 给类B添加类型为A成员变量,用于存放A接口的其他实现类
  • 增强需要的方法
  • 实现不需要增强的方法,方法体重调用成员变量存放的其他实现类对应的方法

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//1.实现同一个接口Connection
public class MyConnection implements Connection {
//3.定义一个变量
private Connection conn;

private LinkedList<Connection> pool;

// 2.编写一个构造方法(参数使用了面相对象的多态特性)
public MyConnection(Connection conn,LinkedList<Connection> pool) {
this.conn=conn;
this.pool=pool;
}

//4.书写需要增强的方法
@Override
public void close() throws SQLException {
pool.add(conn);
}

/**
* 此方法必须覆盖!否则会出现空指针异常!!!
*/
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return conn.prepareStatement(sql);
}

/*下面为不需要增强的方法*/

}

使用装饰类

C3P0连接池

导入Jar包

C3P0-0.9.1.2.jar

mchange-commons-java-0.2.3.4.jar

配置文件

  • 配置文件名称:c3p0-config.xml (固定)
  • 配置文件位置:src(类路径)
  • 配置文件内容:命名配置

c3p0-config.xml-基本配置

常见配置项

编写工具类

  • 未抽取工具类之前
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class TestC3P0 {
@Test
public void testAddUser(){
Connection conn = null;
PreparedStatement pstmt = null;
ComboPooledDataSource dataSource = new ComboPooledDataSource();
try {
conn = dataSource.getConnection();
String sql = "insert into tbl_user value(null,?,?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "xiaomingming");
pstmt.setString(2, "小白");
int rows = pstmt.executeUpdate();
if(rows>0){
System.out.println("添加成功");
}else {
System.out.println("添加失败");
}

}catch (Exception e){
throw new RuntimeException(e);
}finally {
JDBCUtils_V3.release(conn, pstmt, null);
}
}
}
  • 抽取工具类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class C3P0Utils {
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();

public static DataSource getDataSource(){
return dataSource;
}

public static Connection getConnection(){
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
  • 抽取工具类后的测试类可以这样写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class TestC3P0 {
@Test
public void testAddUser1(){
Connection conn = null;
PreparedStatement pstmt = null;
try {
conn = C3P0Utils.getConnection();
String sql = "insert into tbl_user value(null,?,?)";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, "xiaowang");
pstmt.setString(2, "小白");
int rows = pstmt.executeUpdate();
if(rows>0){
System.out.println("添加成功");
}else {
System.out.println("添加失败");
}

}catch (Exception e){
throw new RuntimeException(e);
}finally {
JDBCUtils_V3.release(conn, pstmt, null);
}
}
}