文章目录
- 原理
- 作用
- 实现方式
- 主要参数
- 工作步骤
- 自定义连接池
-
- 基本思想
- 代码实现
-
- 1. 普通连接池
- 2. 规范实现连接池
- DBCP连接池
-
- 需要的jar包
- 代码实现
- C3P0连接池
-
- 需要的jar包
- 实现方式
- 代码实现
- ☆Druid连接池
-
- 特点
-
- 1. 亚秒级查询
- 2. 实时数据注入
- 3. 可扩展的PB级存储(存储量大)
- 4. 多环境部署
- 5. 丰富的社区
- 需要的jar包
- 代码实现
JDBC操作中,连接数据库是必备的,可以通过连接池提前创建数据库连接,方便后续调用
原理
数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个
第一次访问时需要建立连接,但是之后的访问均会复用之前创建的连接,直接执行SQL语句
作用
- 较少了网络开销
- 实现了系统性能实质的提升
- 无TIME_WAIT状态
实现方式
- 自定义连接池
- DBCP连接池
- C3P0连接池
- Druid连接池
tips:
我们可以通过自定义的方式实现连接池,分析连接池类应该包含特定的属性和方法
DBCP连接池、C3P0连接池、Druid连接池可以直接使用
主要参数
-
初始化连接数
连接池启动时创建的初始化数据库连接数量
-
最小连接数
连接池一直保持的数据库连接
如果应用程序对数据库连接的使用量不大,将会大量浪费数据库连接资源
-
最大连接数
连接池能申请的最大连接数
如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作
-
最大空闲时间/最大等待时间
当没有可用连接时,连接池等待连接被归还的最大时间
超过时间抛出异常,可设置参数为0或者负数使得无限等待(根据不同连接池配置)
- 获取连接超时时间
- 超时重试连接次数
已经定义好的连接池的主要参数:
DBCP | C3P0 | Druid | |
---|---|---|---|
初始化连接数 | initialSize(0) | initialPoolSize(3) | initialSize(0) |
最小连接数 | minldle(0) | minPoolSize(3) | minldle(0) |
最大连接数 | maxTotal(8) | maxPoolSize(15) | maxActive(8) |
最大等待时间 | maxWaitMillis(毫秒) | maxIdelTime(0秒) | maxWait(毫秒) |
-
tips
DBCP连接池的配置中,有一个maxIdle属性,表示最大空闲连接数,超过的空闲连接将被释放,默认值为8
maxIdle属性在Druid连接池已不再使用,配置了也没有效果
c3p0连接池没有maxIdle属性
工作步骤
- 连接池的建立
- 连接池中连接的使用管理
- 连接池的关闭
-
tips
1、数据库连接池在初始化的时候会创建initialSize个连接,当有数据库操作时,会从池中取出一个连接
2、当前池中正在使用的连接数等于maxActive,则会等待一段时间,等待其他操作释放掉某一个连接,如果这个等待时间超过了maxWait,则会报错
3、如果当前正在使用的连接数没有达到maxActive,则判断当前是否空闲连接,如果有则直接使用空闲连接,如果没有则新建立一个连接
4、连接使用完毕后,不是将其物理连接关闭,而是将其放入池中等待其他操作复用
自定义连接池
了解思路,代码无需自己实现
基本思想
系统初始化时,将数据库连接作为对象存储在内存中,当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象
使用完毕后,用户并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用
连接的建立、断开都由连接池自身来管理
↓↓↓↓↓↓
可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等,也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等
代码实现
将连接数据库的信息放到集合中
属性: 集合 放置Connection
方法:获取连接方法、回收连接方法
1. 普通连接池
- 定义集合,通过list链式存储使得数据有序
- 静态代码块,初始化10个连接
static{
for (int i = 0; i < 10; i++) {
//JDBCUtils类是自定义类,封装了连接数据库的信息代码
Connection connection = JDBCUtils.newInstance().getConnection();
list.add(connection);
}
}
- 从连接池子中获取连接的方式
public static Connection getConnection(){
if (list.isEmpty()) {
//JDBCUtils类是自定义类,封装了连接数据库的信息代码
Connection connection = JDBCUtils.newInstance().getConnection();
list.addLast(connection);
}
Connection conn = list.removeFirst();//取出第一个
return conn;
}
- 返回到连接池子中
public static void addBack(Connection conn){
if (list.size() >= 10) {//超过连接数
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
list.addLast(conn); //添加
}
}
- 获取连接池子中连接数量
public static int getSize(){
return list.size();
}
2. 规范实现连接池
Java为连接池实现提供了一个规范(接口):DataSource接口
实现DataSource接口没有提供回收链接方法,因此使用装饰者模式
- 将被装饰者导入
public class MyConnection implements Connection {
private Connection conn;
private LinkedList<Connection> list;
public MyConnection(Connection conn, LinkedList<Connection> list) {
super();
this.conn = conn;
this.list = list;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return conn.unwrap(iface);
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return conn.isWrapperFor(iface);
}
@Override
public Statement createStatement() throws SQLException {
return conn.createStatement();
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
return conn.prepareStatement(sql);
}
@Override
public CallableStatement prepareCall(String sql) throws SQLException {
return null;
}
@Override
public String nativeSQL(String sql) throws SQLException {
return null;
}
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
}
@Override
public boolean getAutoCommit() throws SQLException {
return false;
}
@Override
public void commit() throws SQLException {
conn.commit();
}
@Override
public void rollback() throws SQLException {
conn.rollback();
}
@Override
public void close() throws SQLException {
list.addLast(conn);
}
}
- 创建基于规范实现的连接池
public class DataSourcePool implements DataSource {
static LinkedList<Connection> list = new LinkedList<Connection>();
static{
for (int i = 0; i < 10; i++) {
Connection connection = JDBCUtils.newInstance().getConnection();
list.add(connection);
}
}
public static int getSize(){
return list.size();
}
@Override
public Connection getConnection() throws SQLException {
Connection conn = list.removeFirst();
MyConnection conn1 = new MyConnection(conn, list);
return conn1;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
}
DBCP连接池
需要的jar包
mysql-jdbc.jar
commons-dbcp.jar
commons-pool.jar
DBCP是一个依赖Jakarta commons-pool对象池机制的数据库连接池
DBCP可以直接的在应用程序中使用,Tomcat的数据源使用的为DBCP
代码实现
以DBUtil类中封装的方法为例:
//定义需要的工具类对象(变量定义)
protected Connection connection = null;
protected PreparedStatement pps = null;//后续都是用预状态通道来实现
protected ResultSet rs = null;//结果集
protected int count = 0;//受影响的行数
//登录的用户名和密码
private static String username;
private static String password;
private static String url;
private static String driverName;
private static BasicDataSource basicDataSource = new BasicDataSource();
//加载驱动
static {
/*
try {
ResourceBundle bundle = ResourceBundle.getBundle("db");//参数只写属性文件名,不需要写后缀
driverName = bundle.getString("driver");
url = bundle.getString("url");
username = bundle.getString("username");
password = bundle.getString("password");
Class.forName(driverName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
*/
//DBCP
ResourceBundle bundle = ResourceBundle.getBundle("db");//参数只写属性文件名,不需要写后缀
driverName = bundle.getString("driver");
url = bundle.getString("url");
username = bundle.getString("username");
password = bundle.getString("password");
basicDataSource.setUsername(username);
basicDataSource.setPassword(password);
basicDataSource.setUrl(url);
basicDataSource.setDriverClassName(driverName);
//设置初始大小,初始化20个连接
basicDataSource.setInitialSize(20);
}
//获得连接
protected Connection getConnection() {
try {
/*
connection = DriverManager.getConnection(url, username, password);
*/
connection = basicDataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
C3P0连接池
c3p0是开源的JDBC连接池,在lib目录中与Hibernate一起发布,包括了实现jdbc3和jdbc2扩展规范说明的Connection和Statement池的DataSources对象
c3p0与dbcp区别:
c3p0 | dbcp |
---|---|
有自动回收空闲连接功能 | 没有自动回收空闲连接的功能 |
不需要手动设置 | 需要手动设置配置文件 |
需要的jar包
c3p0-0.9.1.2.jar
mysql-connector-java-5.0.8.jar
实现方式
1、手动设置工具类ComboPooledDataSource
2、加载配置文件
在src下创建文件c3p0-config.xml
<?xml version="1.0" encoding="utf-8"?>
<c3p0-config>
<!-- 默认配置,如果没有指定则使用这个配置 -->
<default-config>
<!-- 基本配置 -->
<property name="driverClass">com.mysql.cj.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT</property>
<property name="user">root</property>
<property name="password">123456</property>
<!--扩展配置-->
<!-- 连接超过30秒报错-->
<property name="checkoutTimeout">30000</property>
<!--30秒检查空闲连接 -->
<property name="idleConnectionTestPeriod">30</property>
<property name="initialPoolSize">10</property>
<!-- 30秒不适用丢弃-->
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
</default-config>
</c3p0-config>
代码实现
- 创建对象
//定义需要的工具类对象(变量定义)
protected Connection connection = null;
protected PreparedStatement pps = null;//后续都是用预状态通道来实现
protected ResultSet rs = null;//结果集
protected int count = 0;//受影响的行数
//登录的用户名和密码
private static String username;
private static String password;
private static String url;
private static String driverName;
//C3PO
private static ComboPooledDataSource comboPooledDataSource =new ComboPooledDataSource();
- 获得连接
protected Connection getConnection() {
try {
//connection = DriverManager.getConnection(url, username, password);
//connection = basicDataSource.getConnection();
connection = comboPooledDataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
☆Druid连接池
德鲁伊连接池,是目前比较流行的高性能的、分布式列存储的OLAP框架(具体来说MOLAP)
阿里出品,淘宝和支付宝专用数据库连接池
Druid不仅仅是数据库连接池,还包含一个ProxyDriver(代理驱动)、一系列内置的JDBC组件库、一个SQL Parser(sql解析器),支持所有JDBC兼容的数据库,包括Oracle、MySql、Derby、Postgresql、SQL Server、H2等等
通过Druid提供的SQL Parser可以在JDBC层拦截SQL做相应处理,比如分库分表、审计等,Druid防御SQL注入攻击的WallFilter就是通过Druid的SQL Parser分析语义实现的。
特点
1. 亚秒级查询
Druid提供了快速的聚合能力以及亚秒级的OLAP查询能力,多租户的设计,是面向用户分析应用的理想方式
2. 实时数据注入
Druid支持流数据的注入,并提供了数据的事件驱动,保证在实时和离线环境下事件的实效性和统一性
3. 可扩展的PB级存储(存储量大)
Druid集群可以很方便的扩容到PB的数据量,每秒百万级别的数据注入,即使在加大数据规模的情况下,也能保证时其效性
4. 多环境部署
Druid既可以运行在商业的硬件上,也可以运行在云上,可以从多种数据系统中注入数据,包括hadoop、spark、kafka、storm、samza等
5. 丰富的社区
druid拥有丰富的社区,供交流学习
需要的jar包
druid-1.0.9.jar
代码实现
tips:
在Druid连接池的配置中,driverClassName可配可不配,如果不配置会根据url自动识别dbType(数据库类型),然后选择相应的driverClassName
创建对象
protected Connection connection = null;
protected PreparedStatement pps = null;//后续都是用预状态通道来实现
protected ResultSet rs = null;//结果集
protected int count = 0;//受影响的行数
//登录的用户名和密码
private static String username;
private static String password;
private static String url;
private static String driverName;
//DBCP
//private static BasicDataSource basicDataSource = new BasicDataSource();
//C3PO
//private static ComboPooledDataSource comboPooledDataSource =new ComboPooledDataSource();
//Druid
private static DruidDataSource druidDataSource = new DruidDataSource();
加载驱动
static {
/*
try {
// 优化方法一:
// InputStream inputStream = DBUtils.class.getClassLoader().getResourceAsStream("db.properties");
// Properties properties = new Properties();
// properties.load(inputStream);
// driverName = properties.getProperty("driver");
// url = properties.getProperty("url");
// username = properties.getProperty("username");
// password = properties.getProperty("password");
ResourceBundle bundle = ResourceBundle.getBundle("db");//参数只写属性文件名,不需要写后缀
driverName = bundle.getString("driver");
url = bundle.getString("url");
username = bundle.getString("username");
password = bundle.getString("password");
Class.forName(driverName);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
*/
//DBCP
/*
ResourceBundle bundle = ResourceBundle.getBundle("db");//参数只写属性文件名,不需要写后缀
driverName = bundle.getString("driver");
url = bundle.getString("url");
username = bundle.getString("username");
password = bundle.getString("password");
basicDataSource.setUsername(username);
basicDataSource.setPassword(password);
basicDataSource.setUrl(url);
basicDataSource.setDriverClassName(driverName);
//设置初始大小,初始化20个连接
basicDataSource.setInitialSize(20);
*/
//C3P0
//Druid
ResourceBundle bundle = ResourceBundle.getBundle("db");//参数只写属性文件名,不需要写后缀
//加载属性文件
driverName = bundle.getString("driver");
url = bundle.getString("url");
username = bundle.getString("username");
password = bundle.getString("password");
druidDataSource.setUsername(username);
druidDataSource.setPassword(password);
druidDataSource.setUrl(url);
druidDataSource.setDriverClassName(driverName);
}
获得连接
protected Connection getConnection() {
try {
// connection = DriverManager.getConnection(url, username, password);
// connection = basicDataSource.getConnection();
// connection = comboPooledDataSource.getConnection();
connection = druidDataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}