连接池
学习目标
- 能够理解连接池解决现状问题的原理(理解)
- 能够理解代理模式(不需要重点掌握)
- 能够使用动态代理(最好能搞懂)
- 能够使用C3P0连接池(必须掌握)
- 能够编写C3P0连接池工具类(必须掌握)
- 能够使用DRUID连接池(必须掌握)
第1章 自定义连接池
1.1 连接池概念
我们现实生活中每日三餐。我们并不会吃一餐饭就将碗丢掉,而是吃完饭后将碗放到碗柜中,下一餐接着使用。目的是重复利用碗,我们的数据库连接也可以重复使用,可以减少数据库连接的创建次数。提高数据库连接对象的使用率。
连接池的概念: 连接池是创建和管理数据库连接的缓冲池技术。连接池就是一个容器,连接池中保存了一些数据库连接,这些连接是可以重复使用的。
1.2 没有连接池的现状之前JDBC访问数据库的步骤:
-
创建数据库连接 →运行SQL语句→关闭连接
每次数据库访问执行这样重复的动作
-
每次创建数据库连接的问题
- 获取数据库连接需要消耗比较多的资源,而每次操作都要重新获取新的连接对象,执行一次操作就把连接关闭,而数据库创建连接通常需要消耗相对较多的资源,创建时间也较长。这样数据库连接对象的使用率低。
- 假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出
1.3 连接池解决现状问题的原理
-
连接池在初始化的时候会默认存放n个连接对象并存放到一个容器(LinkedList)中
-
当需要连接的时候,如果连接池中有连接就直接从连接池中获取,如果连接池中没有则新创建连接,如果连接数大于最大连接数时,如果再有请求需要获取连接则将其添加到等待队列
-
对于新创建的连接,一般不会直接销毁,它将被放到连接池中等待重复使用,只有当空闲超时后被释放。
-
对于原本就在连接池中的连接对象,用完之后直接放回连接池中
-
连接池中的连接数在空闲时会逐渐趋近于最小连接数(核心连接数),连接池在满载时的连接数会接近最大连接数。
数据库连接池相关API
Java为数据库连接池提供了公共的接口: javax.sql.DataSource
,各个厂商需要让自己的连接池实现这个接口。这样应用程序可以方便的切换不同厂商的连接池。
数据源(连接池)接口:javax.sql.DataSource
中的方法
Connection getConnection()
从连接池中取出一个连接。
常见的连接池: C3P0
、Druid
、DBCP
等
1.4自定义连接池的要求
- 在连接池初始化的时候默认存放n个连接对象
- 当需要连接时,如果连接池中有连接则直接从连接池中获取,没有则新创建
- 原本就在连接池中的连接,用完之后直接还回连接池中。从连接池的头部获取连接,从连接池的尾部归还连接
- 对于新创建的连接,用完之后直接销毁。
1.4.1自定义连接池第一个版本
- 1.定义一个MyThreadPool类,该类有一个成员变量pool是LinkedList类型,用于存放连接对象的容器。
- 2.该类有两个方法,分别用于获取连接对象和归还连接对象
- 2.1. 获取连接的方法:当pool的长度大于零时从pool中获取(调用removeFirst()方法),当pool的长度小于等于零的时候,创建一个Connection对象
- 2.2. 归还连接的方法:调用pool的addLast()方法
-
3.该版本的缺点:如果连接不是原本就在连接池中的连接,也会归还到连接池中去,造成了连接池的体积越来越庞大,内存占用越来越多。
-
4.解决方法:是原本就在连接池中的连接就归还到连接池中,是新创建的连接就直接销毁。
public class MyConnectionPool {
private LinkedList<Connection> pool = new LinkedList<>();
private String password = "123";
private String user = "root";
private String url = "jdbc:mysql://localhost:3306/day09_1";
static{
//注册驱动
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public MyConnectionPool() throws SQLException{
//初始化的时候,往容器中存放五个连接对象
for(int i=0;i<5;i++){
Connection conn = DriverManager.getConnection(url, user, password);
pool.add(conn);
}
}
public Connection getConnectionFromPool() throws SQLException{
//从pool的头部拿连接
//如果池子中有连接,就直接从池子中取
Connection connection = null;
if (pool.size() > 0) {
connection = pool.removeFirst();
}else {
//池子中没有连接了
//先等待,等待超时之后,就新创建连接
connection = DriverManager.getConnection(url, user, password);
}
return connection;
}
public int getCount(){
return pool.size();
}
public void addBack(Connection conn){
//如果是原本就在池子中的,就归还到池子中
//如果是新创建的连接,就直接销毁
pool.addLast(conn);
}
}
1.4.2自定义连接池第二个版本
-
- 自定义一个MyConnection类实现Connection接口,初始化连接池对象的时候往容器中存入n个MyConnection的对象。而新创建Connection的时候,则创建MySQL驱动中实现类的对象,这样就能区分拿个连接是新创建的或者是原本就在连接池中的了。
-
- 在归还连接的时候,使用instance of 判断如果该对象是MyConnection类型则归还到连接池中,否则直接销毁。
-
- 该版本缺点:初始化存放在连接池中MyConnection对象的方法都是空实现
-
- 解决方法:将MySQL驱动中实现类的对象传入到MyConnection类中,去帮助MyConnection类实现方法
/**
* 优化:区分原本就在池子中的连接和新创建的连接
*
*/
public class MyConnectionPool2 {
private LinkedList<Connection> pool = new LinkedList<>();
private String password = "123";
private String user = "root";
private String url = "jdbc:mysql://localhost:3306/day09_1";
static{
//注册驱动
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public MyConnectionPool2() throws SQLException{
//初始化的时候,往容器中存放五个连接对象------>我们自定义的MyConnection的对象
for(int i=0;i<5;i++){
Connection connection = DriverManager.getConnection(url, user, password);
Connection conn = new MyConnection(connection);
pool.add(conn);
}
}
public Connection getConnectionFromPool() throws SQLException{
//从pool的头部拿连接
//如果池子中有连接,就直接从池子中取
Connection connection = null;
if (pool.size() > 0) {
connection = pool.removeFirst();
}else {
//池子中没有连接了
//先等待,等待超时之后,就新创建连接
connection = DriverManager.getConnection(url, user, password);
}
return connection;
}
public int getCount(){
return pool.size();
}
public void addBack(Connection conn) throws SQLException{
//如果是原本就在池子中的,就归还到池子中
//如果是新创建的连接,就直接销毁
//判断是原本就在池子中的还是新创建的
if (conn instanceof MyConnection) {
//原本就在池子中的
pool.addLast(conn);
}else {
//新创建的,就直接销毁
conn.close();
}
}
}
public class MyConnection implements Connection{
@Override
public void close() throws SQLException {
}
public MyConnection()