JDBC数据库连接池
学习目标
- 了解数据库连接池的原理
- 了解C3P0数据库连接池
- 了解DBCP数据库连接池
- 掌握Druid数据库连接池
数据库连接池
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用现有的N个(自定义)数据库连接,不用每次都再重新建立一个新的连接,以提高对数据库操作的性能。
常用开源的数据库连接池
C3P0
:是一个开放源代码的JDBC连接池,它在lib目录中与Hibernate [2] 一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection 和Statement 池的DataSources 对象。Proxool
:是一个Java SQDriver驱动程序,提供了对选择的其它类型的驱动程序的连接池封装。可以非常简单的移植到现存的代码中,完全可配置,快速、成熟、健壮。可以透明地为现存的JDBC驱动程序增加连接池功能。Jakarta DBCP
:DBCP是一个依赖Jakartacommons-pool对象池机制的数据库连接池。DBCP可以直接的在应用程序中使用。
-DBPool
:是一个高效、易配置的数据库连接池。它除了支持连接池应有的功能之外,还包括了一个对象池,使用户能够开发一个满足自己需求的数据库连接池。SmartPool
:是一个连接池组件,它模仿应用服务器对象池的特性。SmartPool能够解决一些临界问题如连接泄漏(connection leaks)、连接阻塞、打开的JDBC对象(如Statements、PreparedStatements)等。Druid
连接池是阿里巴巴开源的数据库连接池项目。Druid连接池为监控而生,内置强大的监控功能,监控特性不影响性能。功能强大,能防SQL注入,内置Loging能诊断Hack应用行为。
数据库连接池原理
简单来说,数据库连接池就是在一开始先创建N多个数据库连接connection对象,存储在一个集合内,每次使用一个就从集合内取出一个,用完之后又添加到集合内,一直反复使用。
第一步
创建带有是否使用标记的Connection对象。自定义一个带有是否使用标记的Connection对象。
package com.pool;
import java.sql.Connection;
public class PooledConnection {
// 数据库连接
private Connection conn;
// 用于标识当前数据库连接的状态 true:执行 false:空闲
private boolean busy;
public PooledConnection(Connection conn) {
this.conn = conn;
}
public Connection getConn() {
return conn;
}
public void setConn(Connection conn) {
this.conn = conn;
}
public boolean isBusy() {
return busy;
}
public void setBusy(boolean busy) {
this.busy = busy;
}
}
第二步
创建数据库线程池对象
数据库线程池对象包含默认连接数、最大连接数、每次新增个数、超时时间和连接对象集合。
package com.star;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.*;
/**
* 连接池
*/
public class Poo{
// 初始化连接数
private static int initSize = 2;
// 池中最大连接数
private static int maxSize = 10;
// 每次创建的连接数
private static int stepSize = 2;
// 超时时间
private static int timeout = 2000;
// 用来保存创建的数据库连接
private static List<PoolCollection> poo= Collections.synchronizedList(new ArrayList<>());
//单例对象
private static Pooinstance;
/**
* 单利模式:无论什么时候只会有一个Pool对象
*/
private Pool() {
resizePool(initSize);
}
/**
* 单利模式:无论什么时候只会有一个Pool对象
*/
private Pool() {
resizePool(initSize);
}
/**
* 获取单例对象
*
* @return
*/
public static synchronized PoogetInstance() {
if (instance == null) {
instance = new Pool();
}
return instance;
}
/**
* 初始化连接池,循环使用池子里的连接对象,如果不够再创建num个,不超过最大数maxSize
*
* @param size 初始时按照initSize给池中添加连接,其他时候按照stepSize给池中加
*/
private void resizePool(int size) {
int current = pool.size();
if (current + size > maxSize) {
size = maxSize - current;
}
for (int i = 0; i < size; i++) {
pool.add(new PoolCollection(createConnection()));
}
}
}
第三步
获取连接
/**
* 创建连接
*
* @return
*/
public Connection createConnection() {
try {
//加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//加载配置文件
Properties p = new Properties();
p.load(new FileInputStream("jdbc.properties"));
return DriverManager.getConnection(p.getProperty("url"), p.getProperty("user"), p.getProperty("password"));
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 获取连接
*
* @return
*/
public Connection getConnection() {
Connection connection = getAddConnection();
while (connection == null) {
System.out.println(Thread.currentThread().getName() + "开始等待获取连接");
try {
Thread.sleep(timeout);
connection = getAddConnection();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "拿到连接");
return connection;
}
/**
* 可以实现递增获取的机制
*
* @return
*/
private Connection getAddConnection() {
Connection connection = getUsableConnection();
if (connection == null) {
resizePool(stepSize);
connection = getUsableConnection();
}
return connection;
}
/**
* 获取可用的连接
*
* @return
*/
private Connection getUsableConnection() {
Connection connection = null;
if (pool.size() > 0) {
for (PoolCollection poolCollection : pool) {
if (poolCollection.isUsed() == false) {
poolCollection.setUsed(true);
connection = poolCollection.getConnection();
//connection有课能在上次使用过程中产生异常被中断
try {
// connection.isValid如果连接未关闭且有效,则返回true
if (connection.isValid(2000) == false) {
connection = createConnection();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
break;
}
}
}
return connection;
}
第四步
归还
/**
* 关闭
* @param connection
*/
public void close(Connection connection){
for (PoolCollection poolCollection : pool) {
if(connection == poolCollection.getConnection()){
poolCollection.setUsed(false);
}
}
}
第五步
测试
package com.star;
import java.sql.Connection;
public class Main {
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "获取连接");
Poopool1 = Pool.getInstance();
Connection connection = pool1.getConnection();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
pool1.close(connection);
System.out.println(Thread.currentThread().getName() + "释放连接");
}
}.start();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
DBCP
DBCP参数
参数 | 默认值 | 说明 |
---|---|---|
username |
传递给JDBC驱动的用于建立连接的用户名 | |
password |
传递给JDBC驱动的用于建立连接的密码 | |
url |
传递给JDBC驱动的用于建立连接的URL | |
driverClassName |
使用的JDBC驱动的完整有效的Java 类名 | |
initialSize |
0 | 初始化连接:连接池启动时创建的初始化连接数量,1.2版本后支持 |
maxActive | 8 | 最大活动连接:连接池在同一时间能够分配的最大活动连接的数量, 如果设置为非正数则表示不限制 |
maxIdle | 8 | 最大空闲连接:连接池中容许保持空闲状态的最大连接数量,超过的空闲连接将被释放, 如果设置为负数表示不限制 |
minIdle | 0 | 最小空闲连接:连接池中容许保持空闲状态的最小连接数量,低于这个数量将创建新的连接, 如果设置为0则不创建 |
maxWait | 无限 | 最大等待时间:当没有可用连接时,连接池等待连接被归还的最大时间(以毫秒计数)超过时间则抛出异常,如果设置为-1表示无限等待 |
testOnReturn | false | 是否在归还到池中前进行检验 |
testWhileIdle | false | 连接是否被空闲连接回收器(如果有)进行检验.如果检测失败, 则连接将被从池中去除.设置为true后如果要生效,validationQuery参数必须设置为非空字符串 |
minEvictableIdleTimeMillis | 1000 * 60 * 30 |
连接在池中保持空闲而不被空闲连接回收器线程 (如果有)回收的最小时间值,单位毫秒 |
numTestsPerEvictionRun | 3 | 在每次空闲连接回收器线程(如果有)运行时检查的连接数量 |
timeBetweenEvictionRunsMillis | -1 | 在空闲连接回收器线程运行期间休眠的时间值,以毫秒为单位. 如果设置为非正数,则不运行空闲连接回收器线程 |
validationQuery | null | SQL查询,用来验证从连接池取出的连接,在将连接返回给调用者之前.如果指定, 则查询必须是一个SQSELECT并且必须返回至少一行记录 |
testOnBorrow | true | 是否在从池中取出连接前进行检验,如果检验失败, 则从池中去除连接并尝试取出另一个. |
方法1
package com.dbcp;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
public class DBCPUtils {
private String driver = "com.mysql.cj.jdbc.Driver";
private String ur= "jdbc:mysql://localhost/mydbcp?serverTimezone=GMT";
private String username = "root";
private String password = "";
private static BasicDataSource basicDataSource = new BasicDataSource();
public DBCPUtils() {
basicDataSource.setUrl(url);
basicDataSource.setDriverClassName(driver);
basicDataSource.setUsername(username);
basicDataSource.setPassword(password);
basicDataSource