• 向 DriverManager 类注册驱动数据库驱动程序

  • 调用 DriverManager.getConnection 方法, 通过 JDBC URL,用户名,密码取得数据库连接的 Connection 对象。

  • 获取 Connection 后, 便可以通过 createStatement 创建 Statement 用以执行 SQL 语句。

  • 有时候会得到查询结果,比如 select,得到查询结果,查询(SELECT)的结果存放于结果集(ResultSet)中。

  • 关闭数据库语句,关闭数据库连接。

五步流程背下来只要十秒,但每一步背后都有一个”为什么这样设计”:驱动为什么要注册、Statement 三种形态的区别、资源为什么要按序关闭。把这些补齐,JDBC 就从流程变成机制。

完整代码与五步对应

// 1. 注册驱动(JDBC4 之后 SPI 自动加载,此行可省略)
Class.forName("com.mysql.cj.jdbc.Driver");

// 2. 获取连接
try (Connection conn = DriverManager.getConnection(
"jdbc:mysql://host:3306/db?useSSL=false", "user", "pwd");
// 3. 创建语句(预编译)
PreparedStatement ps = conn.prepareStatement(
"SELECT id, name FROM user WHERE age > ?")) {

ps.setInt(1, 18); // 参数绑定
// 4. 执行并处理结果集
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
System.out.println(rs.getLong("id") + ":" + rs.getString("name"));
}
}
} // 5. try-with-resources 自动按逆序关闭

每一步的设计意图

驱动注册:Class.forName 触发 Driver 类静态块调用 DriverManager.registerDriver。JDBC4 起靠 SPI(META-INF/services)自动发现,手写注册已是历史习惯。DriverManager 作为”驱动注册表”,按 URL 协议(jdbc:mysql:)路由到对应驱动——JDBC 用接口隔离了 Java 与具体数据库。

获取连接:getConnection 是重操作(TCP 握手 + 认证 + 会话初始化,毫秒到几十毫秒),这正是连接池存在的理由:池化复用 Connection,把”每次建连”变成”借还连接”。

Statement 三兄弟:

  • Statement:SQL 字符串直接执行,拼接参数有注入风险,且每次执行都编译
  • PreparedStatement:预编译 + ? 占位符绑定参数,防注入、可复用执行计划,默认选择
  • CallableStatement:调用存储过程

ResultSet 处理:游标式向前遍历(rs.next()),按列名/列序取值。注意 ResultSet 默认不可回滚滚动、连接关闭即失效——需要离线处理时应先映射为对象列表(这正是 MyBatis 做的事)。

关闭顺序:ResultSet → Statement → Connection,逆创建序关闭。漏关连接在池化环境下等于连接泄漏:池被占满后所有请求阻塞,是生产事故高频原因。try-with-resources 是唯一的可靠写法。

从 JDBC 到连接池与 ORM

裸 JDBC 的痛点(建连贵、手工映射 ResultSet、样板代码多)催生了两层基础设施:

  • 连接池(HikariCP/Druid):维护最小/最大连接数、借还超时、泄漏检测;配置要点是 maxPool 不必过大(数据库并发能力有限,公式参考 核心数*2+磁盘数)
  • ORM/模板(MyBatis/JdbcTemplate):把”参数绑定-执行-结果映射”自动化;MyBatis 保留 SQL 控制权,JPA 走向全映射

理解 JDBC 流程的价值在于:用任何框架时都知道底层在做什么——MyBatis 的 #{} 就是 PreparedStatement 绑定,${} 就是字符串拼接(注入风险来源);连接池监控里的 active/idle 指标就是 Connection 的借还状态。

流程是表,机制是里:接口隔离数据库差异、预编译隔离注入风险、池化隔离建连成本、映射隔离样板代码——JDBC 及其生态就是这四层隔离的展开。