为什么需要数据库连接池?
在理解如何使用之前,我们先明白为什么要使用它。

- 性能提升:数据库连接的创建和销毁是一个非常耗时的过程(涉及网络通信、身份验证、资源分配等),连接池通过预先创建并维护一组可复用的连接,避免了每次请求都创建新连接的开销,从而显著提高响应速度。
- 资源控制:如果应用不使用连接池,并发量高时可能会无限制地创建数据库连接,导致数据库服务器资源耗尽(连接数耗尽),甚至整个应用崩溃,连接池可以设置最大连接数,有效管理数据库资源。
- 稳定性增强:连接池提供了连接的有效性检查、自动回收失效连接等功能,使得数据库连接管理更加健壮。
一个形象的比喻:
没有连接池,就像每次去餐厅吃饭都要先去农场买地、种麦子、磨面、烤面包,效率极低。 有了连接池,就像餐厅有一个面包房(连接池),预先烤好了一批面包(数据库连接),客人来了直接拿一个用,用完放回,大大提高了服务效率。
主流的 Java 连接池技术
市面上有许多优秀的 Java 连接池实现,其中最主流的有以下几个:
| 连接池 | 优点 | 缺点 | 官方推荐 |
|---|---|---|---|
| HikariCP | 性能极高,代码简洁,稳定可靠,是目前公认的性能最好的连接池。 | 功能相对其他几款较少。 | Spring Boot 2.x 及更高版本的默认首选。 |
| Druid | 功能强大,性能优秀,内置了强大的监控功能(SQL监控、Web Stat Filter等),在生产环境中非常受欢迎。 | 配置项相对较多,代码比 HikariCP 复杂。 | 阿里巴巴开源,国内广泛使用。 |
| C3P0 | 老牌连接池,功能稳定,使用广泛。 | 性能不如 HikariCP 和 Druid,且有一些已知的 Bug。 | 早期项目常见,新项目不推荐首选。 |
| DBCP | Apache 旗下产品,也是一款老牌连接池。 | 性能一般,且在高并发下稳定性不如 HikariCP。 | Spring Framework 曾作为默认,现已不再推荐。 |
对于新项目,HikariCP 是首选,其次是 Druid(如果需要强大的监控功能)。

实践演示:使用 HikariCP 连接 MySQL
我们将以最流行的 HikariCP 为例,演示如何在 Java 项目中配置和使用它。
准备工作
-
创建 MySQL 数据库和表
CREATE DATABASE mytestdb; USE mytestdb; CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL, password VARCHAR(50) NOT NULL, email VARCHAR(100) ); INSERT INTO users (username, password, email) VALUES ('zhangsan', '123456', 'zhangsan@example.com'); INSERT INTO users (username, password, email) VALUES ('lisi', '654321', 'lisi@example.com'); -
创建 Maven 项目 在
pom.xml文件中添加必要的依赖:mysql-connector-java:MySQL 驱动。com.zaxxer:HikariCP:HikariCP 连接池本身。slf4j-api和logback-classic:日志框架,HikariCP 依赖 SLF4J 输出日志。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>java-mysql-pool-demo</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <!-- MySQL 8.0+ 驱动 --> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.0.33</version> </dependency> <!-- HikariCP 连接池 --> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> <version>5.0.1</version> </dependency> <!-- 日志框架 SLF4J + Logback --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.7</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.4.8</version> </dependency> </dependencies> </project>
代码实现
硬编码配置(不推荐,仅用于演示)
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class HikariCPHardcodedDemo {
public static void main(String[] args) {
// 1. 创建 HikariConfig 配置对象
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mytestdb?useSSL=false&serverTimezone=UTC");
config.setUsername("root"); // 你的 MySQL 用户名
config.setPassword("your_password"); // 你的 MySQL 密码
config.setDriverClassName("com.mysql.cj.jdbc.Driver");
// 2. 可选的优化配置
config.setMaximumPoolSize(10); // 最大连接数
config.setMinimumIdle(5); // 最小空闲连接数
config.setConnectionTimeout(30000); // 连接超时时间 (毫秒)
config.setIdleTimeout(600000); // 空闲连接存活时间 (毫秒)
config.setMaxLifetime(1800000); // 连接最大存活时间 (毫秒)
// 3. 创建 HikariDataSource 数据源对象
HikariDataSource dataSource = new HikariDataSource(config);
// 4. 使用连接池获取连接并执行操作
try (Connection connection = dataSource.getConnection()) {
System.out.println("成功获取连接: " + connection);
// 查询示例
String sql = "SELECT id, username, email FROM users WHERE id = ?";
try (PreparedStatement pstmt = connection.prepareStatement(sql)) {
pstmt.setInt(1, 1);
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
System.out.println("查询到用户: ID=" + rs.getInt("id")
+ ", Username=" + rs.getString("username")
+ ", Email=" + rs.getString("email"));
}
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 5. 关闭数据源(在应用关闭时执行)
if (dataSource != null && !dataSource.isClosed()) {
dataSource.close();
System.out.println("HikariCP 连接池已关闭。");
}
}
}
}
使用 DataSource 接口和配置文件(推荐最佳实践)
将数据库配置信息与代码分离,是更专业、更灵活的做法。

创建 db.properties 配置文件
在 src/main/resources 目录下创建 db.properties 文件:
# db.properties # MySQL 8.0+ 驱动类名 jdbc.driverClassName=com.mysql.cj.jdbc.Driver # JDBC URL jdbc.url=jdbc:mysql://localhost:3306/mytestdb?useSSL=false&serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8 # 用户名和密码 jdbc.username=root jdbc.password=your_password # HikariCP 连接池配置 pool.maximumPoolSize=10 pool.minimumIdle=5 pool.connectionTimeout=30000 pool.idleTimeout=600000 pool.maxLifetime=1800000
创建工具类 DataSourceUtil.java
这个类负责加载配置文件并创建数据源实例。
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class DataSourceUtil {
private static HikariDataSource dataSource;
static {
try (InputStream input = DataSourceUtil.class.getClassLoader().getResourceAsStream("db.properties")) {
Properties props = new Properties();
if (input == null) {
throw new RuntimeException("无法找到 db.properties 文件");
}
props.load(input);
// 创建 HikariConfig
HikariConfig config = new HikariConfig();
config.setJdbcUrl(props.getProperty("jdbc.url"));
config.setUsername(props.getProperty("jdbc.username"));
config.setPassword(props.getProperty("jdbc.password"));
config.setDriverClassName(props.getProperty("jdbc.driverClassName"));
// 从配置文件中读取 HikariCP 特定配置
config.setMaximumPoolSize(Integer.parseInt(props.getProperty("pool.maximumPoolSize")));
config.setMinimumIdle(Integer.parseInt(props.getProperty("pool.minimumIdle")));
config.setConnectionTimeout(Long.parseLong(props.getProperty("pool.connectionTimeout")));
config.setIdleTimeout(Long.parseLong(props.getProperty("pool.idleTimeout")));
config.setMaxLifetime(Long.parseLong(props.getProperty("pool.maxLifetime")));
// 创建数据源
dataSource = new HikariDataSource(config);
} catch (IOException e) {
throw new RuntimeException("加载数据库配置文件失败", e);
}
}
// 获取连接
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
// 关闭数据源 (通常在应用关闭时调用)
public static void closeDataSource() {
if (dataSource != null && !dataSource.isClosed()) {
dataSource.close();
}
}
}
在业务代码中使用工具类 你的业务代码可以非常简洁,完全不需要关心连接池的细节。
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class UserService {
public void findUserById(int id) {
// 使用 try-with-resources 自动关闭连接
try (Connection connection = DataSourceUtil.getConnection();
PreparedStatement pstmt = connection.prepareStatement("SELECT * FROM users WHERE id = ?")) {
pstmt.setInt(1, id);
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
System.out.println("用户信息: ID=" + rs.getInt("id")
+ ", 用户名=" + rs.getString("username")
+ ", 邮箱=" + rs.getString("email"));
} else {
System.out.println("未找到 ID 为 " + id + " 的用户。");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
UserService userService = new UserService();
userService.findUserById(2); // 查询 lisi
}
}
Druid 连接池简介
如果你选择 Druid,其使用方式与 HikariCP 非常相似,只是在配置和功能上有所不同。
添加 Druid 依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.18</version>
</dependency>
使用 Druid 创建数据源
import com.alibaba.druid.pool.DruidDataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class DruidDemo {
public static void main(String[] args) {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/mytestdb?useSSL=false&serverTimezone=UTC");
dataSource.setUsername("root");
dataSource.setPassword("your_password");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
// Druid 特有的配置,如监控
dataSource.setFilters("stat"); // 开启监控统计功能
try (Connection conn = dataSource.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT 'Hello Druid'")) {
if (rs.next()) {
System.out.println(rs.getString(1));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
dataSource.close();
}
}
}
Druid 的最大优势在于其强大的监控页面,你只需配置一个 Servlet,就可以在浏览器中访问一个页面,实时查看 SQL 执行情况、活跃连接数、慢查询等信息,对于线上问题排查非常有帮助。
总结与最佳实践
- 选择连接池:优先选择 HikariCP,对监控有强需求时选择 Druid。
- 配置分离:永远不要在代码中硬编码数据库连接信息,使用
.properties或.yml文件进行管理。 - 生命周期管理:
DataSource(数据源)的创建和销毁成本很高,应该在应用启动时创建一次,在应用关闭时销毁一次,不要为每个请求都创建一个新的DataSource。 - 连接的获取与释放:遵循 “用即取,用即还” 的原则,使用
try-with-resources语句块可以确保Connection,Statement,ResultSet等资源在使用后被自动关闭,并将其归还给连接池,这是防止连接泄漏的关键。 - 理解配置参数:花时间理解
maximumPoolSize,connectionTimeout等核心参数的含义,并根据你的应用负载和数据库服务器的性能进行合理调优。
