池化思想的特点:是一个容器,可以减少创建连接和销毁连接的频次,提高效率
一、为什么要用数据库连接池?
在没有连接池的时代,我们每次操作数据库都要:
-
加载驱动
-
创建连接
-
执行 SQL
-
销毁连接
而创建和销毁连接是非常耗时的操作,涉及网络通信、TCP 握手、权限校验等步骤,频繁创建会带来严重的性能开销,甚至导致数据库连接耗尽。
连接池的核心思想,就是「池化复用」:
-
程序启动时,预先创建一批数据库连接,放在 “池子” 里统一管理
-
业务代码需要连接时,直接从池子里取,用完再归还
-
避免频繁创建和销毁连接,大幅提升性能,同时控制最大并发连接数,防止数据库被压垮
1.1 连接池概述
1.总结
-
原来:需要自己创建连接和销毁连接,这样是比较消耗时间,资源等。
-
现在:有一些连接池,已经创建好了一些连接,现在可以从连接池中获取到,这样就节省创建连接时间,直接使
用这些连接,归还到连接池中。 -
节省创建连接与释放连接,性能消耗
-
连接池中连接起到复用的作用,提升程序性能
2.连接池(池参数,如果不指定,有默认值)
-
初始大小:10个
-
最小空闲连接数:3个
-
增量:一次创建的最小单位(5个)
-
最大空闲连接数:12个
-
最大连接数:20个
-
最大的等待时间:1000毫秒
3.4个参数:任何的开源的连接池,4大参数都需要自己来设置
-
驱动的名称 ‐‐ com.mysql.jdbc.Driver
-
连接 ‐‐ jdbc:mysql:///day14
-
用户名 ‐‐ root
-
密码 ‐‐ root
二、连接池的核心概念与参数
- 标准接口:DataSource
-
JDBC 规范定义了 javax.sql.DataSource 接口,所有开源连接池(包括 Druid、DBCP、C3P0)都必须实现这个接口。
-
核心方法:getConnection(),业务代码通过这个方法从池子里获取连接
-
连接池对 Connection 的 close() 方法做了增强:调用 conn.close() 时,并不是真正销毁连接,而是把连接归还到池子里,等待复用。
- 连接池的核心配置参数
不同连接池的参数略有差异,但核心配置基本一致:
| 参数 | 作用 | 示例 |
|---|---|---|
| driverClassName | 数据库驱动类全限定名 | com.mysql.cj.jdbc.Driver |
| url | 数据库连接地址 | jdbc:mysql://localhost:3306/jdbcdemo?useSSL=false&serverTimezone=UTC |
| username | 数据库用户名 | root |
| password | 数据库密码 | root |
| initialSize | 初始化连接数,启动时创建的连接数量 | 5 |
| minIdle | 最小空闲连接数,池子里始终保持的空闲连接数 | 3 |
| maxActive | 最大连接数,池子最多同时存在的连接数 | 10 |
| maxWait | 获取连接的最大等待时间(毫秒),超时会抛出异常 | 3000 |
三、主流连接池对比
目前 Java 生态中主流的连接池有 3 种:
-
DBCP:Apache 开源,老牌连接池,性能一般,功能简单
-
C3P0:老牌连接池,支持 JDBC3 标准,但并发性能较差
-
Druid:阿里巴巴开源,目前业界公认的最佳连接池
-
高性能,并发场景下表现远超其他连接池
-
内置强大的 SQL 监控、性能统计功能
-
支持密码加密、SQL 日志、JDBC 扩展等高级特性
-
已在阿里数百个生产环境大规模验证,稳定性极强
-
四、Druid 连接池实战(两种实现方式)
下面是 Druid 连接池的完整实现,分为硬编码和配置文件两种方式。
方式 1:硬编码实现(快速上手)
package cn.tx.demo1;import com.alibaba.druid.pool.DruidDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;public class JdbcTest6 {public static void main(String[] args) {// 1. 创建 Druid 连接池对象DruidDataSource dataSource = new DruidDataSource();// 2. 设置数据库连接的 4 大核心参数dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");dataSource.setUrl("jdbc:mysql://localhost:3306/jdbcdemo?useSSL=false&serverTimezone=UTC");dataSource.setUsername("root");dataSource.setPassword("root");// 3. 设置连接池核心参数dataSource.setInitialSize(5); // 初始化连接数dataSource.setMaxActive(10); // 最大连接数dataSource.setMaxWait(2000); // 获取连接超时时间(毫秒)Connection conn = null;PreparedStatement stmt = null;try {// 4. 从连接池获取连接conn = dataSource.getConnection();// 5. 编写 SQL 并执行String sql = "insert into t_user values (null, ?, ?, ?)";stmt = conn.prepareStatement(sql);stmt.setString(1, "eee");stmt.setString(2, "eee");stmt.setString(3, "eee");stmt.executeUpdate();} catch (SQLException e) {e.printStackTrace();} finally {// 6. 释放资源:归还连接到池子if (stmt != null) {try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}if (conn != null) {try {// 这里的 close() 不是销毁连接,而是归还到连接池conn.close();} catch (SQLException e) {e.printStackTrace();}}}}
}
方式 2:配置文件实现(生产环境推荐)
步骤 1:创建 druid.properties 配置文件
在 src/main/resources 目录下创建配置文件:
# 数据库连接配置
driverClassName=com.mysql.jdbc.Driver //5.0版本的mysqljar包
//8.0版本的jar包用:driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcdemo?useSSL=false&serverTimezone=UTC
username=root
password=root# 连接池配置
initialSize=5
maxActive=10
maxWait=3000
maxIdle=6
minIdle=3
步骤 2:编写 JDBC 工具类
package cn.tx.utils;import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;/*** 基于 Druid 连接池的 JDBC 工具类*/
public class JdbcUtils2 {// 连接池对象private static final DataSource DATA_SOURCE;static {Properties pro = new Properties();try {// 加载配置文件InputStream inputStream = JdbcUtils2.class.getResourceAsStream("/druid.properties");pro.load(inputStream);// 创建 Druid 连接池DATA_SOURCE = DruidDataSourceFactory.createDataSource(pro);} catch (Exception e) {throw new RuntimeException("初始化 Druid 连接池失败", e);}}/*** 从连接池获取连接* @return 数据库连接*/public static Connection getConnection() {try {return DATA_SOURCE.getConnection();} catch (SQLException e) {throw new RuntimeException("获取数据库连接失败", e);}}/*** 关闭资源(归还连接到池子)* @param conn 连接对象* @param stmt Statement 对象* @param rs 结果集*/public static void close(Connection conn, Statement stmt, ResultSet rs) {if (rs != null) {try {rs.close();} catch (SQLException e) {e.printStackTrace();}}if (stmt != null) {try {stmt.close();} catch (SQLException e) {e.printStackTrace();}}if (conn != null) {try {// 归还连接到连接池conn.close();} catch (SQLException e) {e.printStackTrace();}}}/*** 重载方法:关闭无结果集的资源*/public static void close(Connection conn, Statement stmt) {close(conn, stmt, null);}
}
五、核心原理:为什么 conn.close() 不会真的销毁连接?
很多初学者会疑惑:调用 conn.close() 后,连接不是被关闭了吗?为什么连接池还能复用?
核心答案是:连接池对 Connection 对象做了增强(代理 / 装饰模式):
- 业务代码拿到的 Connection 不是原生的数据库连接,而是连接池包装后的代理对象
- 当调用 conn.close() 时,执行的是连接池重写后的逻辑:
- 重置连接状态(回滚未提交事务、恢复自动提交等)
- 把连接归还到池子里,标记为空闲状态
- 只有当连接池关闭、连接超时被清理时,才会真正调用原生连接的 close() 方法销毁连接
六、Druid 连接池的高级特性
-
SQL 性能监控:内置 StatFilter 插件,可统计每条 SQL 的执行耗时、并发次数,方便定位慢查询
-
数据库密码加密:支持 PasswordCallback,避免明文密码泄露,提升安全性
-
SQL 日志记录:支持多种日志框架,可记录所有执行的 SQL,方便排查问题
-
JDBC 扩展:通过 Filter 机制,可自定义 JDBC 层的扩展逻辑,比如 SQL 拦截、数据脱敏等
