2. JDBC核心API与连接管理
JDBC(Java Database Connectivity)是Java访问关系型数据库的标准规范,它定义了一套接口,由各数据库厂商提供驱动实现。理解JDBC的核心API,是掌握后续ORM框架的基础。本节围绕DriverManager、Connection、Statement、ResultSet四个核心对象,以及连接参数配置、连接池原理与HikariCP集成展开。
一、JDBC核心对象与调用链
一次典型的JDBC查询,本质上是四个对象依次协作的过程:DriverManager负责根据URL找到合适的驱动并建立物理连接,返回Connection;Connection创建Statement(或PreparedStatement)承载SQL;Statement执行后返回ResultSet,以游标方式逐行读取结果。四者的关系与数据流向如下:
二、DriverManager与连接参数
DriverManager通过URL前缀匹配已注册的驱动。以MySQL为例,标准URL格式为:
jdbc:mysql://host:3306/dbname?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8
关键参数包括:host/port定位数据库实例,dbname指定默认库,serverTimezone避免时区导致的日期偏移,characterEncoding保证中文不乱码,useSSL控制加密连接。生产环境还应配置connectTimeout和socketTimeout,防止网络异常时线程长时间阻塞。
需要特别注意的是,每次DriverManager.getConnection()都会建立一条物理连接,涉及TCP握手、认证、会话初始化,开销通常在几十毫秒量级。高并发下频繁创建销毁连接会迅速耗尽数据库的连接数上限,这正是连接池存在的根本原因。
三、Statement与ResultSet
Statement用于执行静态SQL,而PreparedStatement是它的子接口,支持预编译和参数占位符?。使用PreparedStatement有两大收益:一是数据库可缓存执行计划,重复执行效率更高;二是参数通过setXxx()绑定,天然防止SQL注入。因此除极少数动态表名场景外,都应优先使用PreparedStatement。
String sql = "SELECT id, name, balance FROM account WHERE balance > ?";
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setBigDecimal(1, new BigDecimal("1000"));
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
long id = rs.getLong("id");
String name = rs.getString("name");
BigDecimal balance = rs.getBigDecimal("balance");
// 处理每一行
}
}
}
ResultSet是一个游标,初始位于第一行之前,必须调用next()才能移动到下一行,返回false表示无更多数据。取值时推荐使用列名而非列索引,可读性更好且不易因SQL列顺序调整而出错。对于可能为NULL的列,应使用getObject()或先wasNull()判断,避免基本类型拆箱空指针。
四、连接池原理与HikariCP集成
连接池的核心思想是空间换时间:启动时预先创建若干物理连接放入池中,应用借出使用后归还而非关闭。这样把昂贵的连接建立成本摊薄到多次复用上。连接池需要管理几个关键行为:借出时校验连接是否有效、空闲连接的超时回收、池满时的等待策略、以及连接泄漏检测。
HikariCP是目前性能与稳定性俱佳的连接池实现,Spring Boot 2.x起默认集成。其核心参数如下表:
| 参数 | 含义 | 示例值 |
|---|---|---|
| maximumPoolSize | 池中最大连接数 | 10 |
| minimumIdle | 最小空闲连接数 | 5 |
| connectionTimeout | 获取连接的最长等待毫秒数 | 30000 |
| idleTimeout | 空闲连接存活时间 | 600000 |
| maxLifetime | 连接最大生命周期,应小于数据库wait_timeout | 1800000 |
| connectionTestQuery | 连接有效性检测SQL | SELECT 1 |
一个常见的经验公式是:maximumPoolSize ≈ CPU核数 × 2 + 磁盘数,但更可靠的做法是结合压测结果调整。若maxLifetime大于数据库的wait_timeout,连接可能被数据库单方面断开,应用借出时才发现失效,因此maxLifetime通常设置为比wait_timeout小30秒左右。
connectionTimeout默认30秒,若频繁出现"Connection is not available"异常,应先排查是否存在连接泄漏(借出未归还),再考虑调大池容量。五、资源关闭与最佳实践
Connection、Statement、ResultSet都实现了AutoCloseable,应使用try-with-resources确保按ResultSet→Statement→Connection的顺序关闭。使用连接池时,conn.close()并非真正关闭物理连接,而是将连接归还池中,因此必须显式关闭,否则连接会被永久占用直至泄漏检测触发。
综上,DriverManager负责建立连接,Connection是会话载体,Statement承载SQL,ResultSet承载结果,而连接池则解决了连接复用与资源管控问题。掌握这条主线,后续学习JdbcTemplate、MyBatis等框架时,就能清楚它们究竟在封装哪一层。