每次都 getConnection 新建连接太浪费(TCP 握手 + 账号验证,用完就销毁)——连接池 = 连接的租车行:程序启动时预建好一池子连接,用的时候取、用完 close 还回去循环使用。JDK 定了标准接口 DataSource,最常用实现是阿里的 Druid。
直连的账,算一下就心疼
11.3 的 Demo13 每跑一次都要 DriverManager.getConnection(...)——这一行背后发生了什么?
- 和 MySQL 服务器 TCP 三次握手建立网络连接
- 传输账号密码,验证身份
- 服务器分配资源,才算"接通了"
一次几百毫秒,看着不多。但真实程序里数据库操作每秒成百上千次——每个请求都现造一辆车、用完报废,服务器自己先被自己累死。
连接池:租车行的生意经
程序启动时:预建 N 条连接,停在"车位"上待命
│
请求来了 → getConnection() 取一条 ← 车位空了
│
用完了 → conn.close() ← 注意:不是销毁,是开回车位!
│
下个请求 → 取的还是这条(或另一条闲置的)
三大好处:
- 快:省掉每次握手的开销,取连接是纯内存操作,微秒级
- 省:连接数有上限(比如最多 20 条),再猛的请求也只会排队,数据库不会被挤爆
- 好管理:url、账号、密码、池子大小集中在一处配置,不用散落在每个方法里
连接池版代码里,conn.close() 的意思变了:不是"关闭连接",是"把连接还回池子"。忘写 close = 只借不还,池子很快被借空,后面的请求全部卡死等车。规矩反而更严了:用完必须还。
DataSource:JDK 又一份"总合同"
连接池这么好用,各大厂商都造——JDK 照 11.1 的思路又定了一个标准接口:
javax.sql.DataSource(JDK 自带,java.sql的补充包)——合同就一个核心方法:getConnection()- 实现类(第三方 jar):
- Druid(阿里巴巴)——国内主流,自带监控页面,本节用它
- HikariCP——Spring Boot 默认,号称最快
- C3P0——上古时期的活化石,老项目里能看到
又是 4.3 接口 + 4.4 多态的老戏码:你面向 DataSource 接口编程,哪天想换 HikariCP,只改一行 new 的地方。
Demo14:Druid 版查询(和 11.3 只差一行)
准备:下载 druid(GitHub 阿里 Druid releases,拿 druid-1.2.x.jar),按 11.3 保姆四步导入(libs → 粘贴 → Add as Library)。
import com.alibaba.druid.pool.DruidDataSource;
import javax.sql.DataSource;
import java.sql.*;
public class Demo14 {
public static void main(String[] args) throws Exception {
// ① 造一辆"租车行",配上参数
DataSource ds = new DruidDataSource();
((DruidDataSource) ds).setUrl("jdbc:mysql://localhost:3306/javaweb?serverTimezone=Asia/Shanghai");
((DruidDataSource) ds).setUsername("root");
((DruidDataSource) ds).setPassword("你的密码");
((DruidDataSource) ds).setInitialSize(5); // 启动先备 5 条连接
// ② 取连接(从池里拿,不再现造)
try (Connection conn = ds.getConnection();
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("SELECT * FROM student")) {
while (rs.next()) {
System.out.println(rs.getInt("id") + " "
+ rs.getString("name") + " " + rs.getInt("age"));
}
} // close = 还车回池,连接没死,下个请求接着用
}
}
对比 Demo13:只有"取连接"那一行换了写法(DriverManager.getConnection → ds.getConnection),Statement、ResultSet、try-with-resources 全套照旧——11.2 的方法地图一个字不用重学。
真实项目里,这个"租车行"会在工具类里造一次(static),全程序共用——这就是后面要学的 JDBCUtils 工具类套路。
System.out.println(ds.getConnection() != null); 连取三次都成功;把三个连接都 close 再取——照样成功。车是循环用的,不还才怪。
把 setInitialSize(2)、setMaxWait(1000)(等车最多等 1 秒)设上,取两条连接不 close,再取第三条 → 超时报错拿不到连接。池子是有限的,借了要还。
如果项目里同时有 HikariCP 的 jar,把 new DruidDataSource() 换成 new HikariDataSource(),配同样的 url 账号密码——其余代码一行不动照样跑。这就是面向接口编程的含金量。
常见误区
- 「连接池的 close 真的关连接」——是还回池子,不是销毁。这也是为什么池子版代码更不能漏 close:只借不还,池子空了全卡死。
- 「用了连接池就不用学 JDBC 了」——恰恰相反,池子只负责"发连接",拿到 Connection 之后 Statement、ResultSet 那套原封不动。池子是 11.2 五步套路的第一步的升级版。
- 「池子越大越好」——错。每条连接在 MySQL 那边都占资源,设几百条反而拖垮数据库。一般 10~50 条按业务定。
- 「Druid 是 JDK 自带的」——不是。JDK 自带的只有
DataSource接口;Druid、HikariCP 都要另导 jar(11.3 保姆步骤)。 - 「每次 getConnection 都是同一条连接」——不一定,池子给你的是任意一条闲置的。所以千万别把连接存到成员变量里全局共用——多线程会打架(多线程是后面的大章)。
自测一下
TCP 三次握手建立网络连接 → 传输账号密码做身份验证 → 服务器分配资源。每次几百毫秒,高频请求下服务器吃不消。
把连接还回池子(开回车位),不是销毁。下个请求可复用。忘 close = 只借不还,池子被借空后所有请求卡死。
DataSource 是 JDK 自带的标准接口(合同);Druid 是阿里写的实现类(第三方 jar)。面向接口编程,Druid 换 HikariCP 只改 new 的那一行(4.3 + 4.4 的思想)。
成立。只有第一步"获取连接"从 DriverManager 换成 ds.getConnection(),注册驱动由池子代劳;createStatement、执行、取结果、释放资源全套照旧。
改动:①导 druid jar;②造一个 static 的 DruidDataSource 并配置四参(url/user/password/initialSize),全程序共用这一家租车行;③每处 DriverManager.getConnection(url, user, pass) 换成 ds.getConnection()。不动:Class.forName 可留可删(池子代劳);Statement/ResultSet/try-with-resources/CRUD 语句全部原样。核心认知:close 从"销毁"变"归还"。
下一步学什么
- ☑ 11.4 数据库连接池 ✅ —— 点此复习:第十一章 JDBC/11.4 数据库连接池
- ☑ 11.5 DbUtils ✅ 已完成——点此复习:JDBCUtils 的现成成品——QueryRunner 管执行、ResultSetHandler 管形状,拼对象的底层是 9.1 的反射。
相关笔记
- 第十一章 JDBC/11.1 什么是JDBC:合同思想,DataSource 是第二份合同
- 第十一章 JDBC/11.3 JDBC编程:直连版 CRUD,本节只换"取连接"一行
- 第四章 面向对象(下)/4.4 多态:面向接口编程,换实现不动代码
- 第十章 I-O/10.2 字节流:close 的语义升级——从关流到还车