当前位置: 首页 > java >正文

JDBC:数据库访问的原始接口

目录

一、JDBC 基础入门:数据库访问的原始接口

  • JDBC 是什么?它在 Java 中扮演什么角色?

  • JDBC 工作原理图解(驱动 -> 连接 -> 执行 -> 关闭)

  • 常见 JDBC 驱动类型及差异

  • 第一个 JDBC 示例程序:连接数据库 + 执行查询

二、JDBC 核心 API 详解

  • 核心接口介绍:DriverManager / Connection / Statement / PreparedStatement / ResultSet

  • PreparedStatement 与 Statement 的区别与安全性(防止 SQL 注入)

  • ResultSet 遍历与数据提取技巧

  • JDBC 操作流程六步详解

三、JDBC 与事务控制

  • JDBC 中的事务控制:自动提交与手动提交

  • commit()rollback() 的使用与实战

  • 多事务嵌套控制及注意事项

  • 如何处理事务中断异常?面试常问陷阱题解析

四、JDBC 实战:增删改查 CURD 模板

  • 通用 CURD 模板封装

  • 批量插入与批处理优化(addBatch() / executeBatch()

  • 实战演练:实现一个简易 DAO 层

  • JDBC 与用户登录/注册/分页查询等典型业务示例

五、JDBC 性能优化与连接池技术

  • JDBC 性能瓶颈分析

  • 为什么不能频繁开关连接?

  • 常见连接池技术对比:C3P0、DBCP、HikariCP、Druid

  • HikariCP 实践:配置、监控、调优

六、JDBC 与数据库安全

  • 防止 SQL 注入的最佳实践

  • 参数预编译的作用与局限

  • 数据库权限控制(从代码到数据库用户)

七、JDBC 与框架集成

  • Spring JDBC 简介:比原生 JDBC 更高效

  • Spring JDBC 模板(JdbcTemplate)使用指南

  • 与 MyBatis 的对比(JDBC 和 ORM 之间的选择)

  • 企业项目中 JDBC 的典型应用场景

八、常见问题与面试题精选

  • JDBC 与连接池的面试高频题解析

  • JDBC 如何实现事务控制?嵌套事务如何处理?

  • 如何解决 JDBC 中连接泄漏问题?

  • 面试官提问:“如果让你封装一个通用 JDBC 工具类,你怎么设计?”


一、JDBC 基础入门:数据库访问的原始接口

在 Java 开发中,无论你是否使用框架如 MyBatis、JPA、Hibernate,JDBC 始终是底层的根基。理解它,不仅能帮助你打好基本功,还能在面试中展示你对原理的掌控力。


1.1 JDBC 是什么?它在 Java 中扮演什么角色?

JDBC(Java Database Connectivity)是 Java 官方提供的一套 用于操作数据库的 API 接口规范,主要用来:

  • 连接数据库(MySQL、Oracle、PostgreSQL 等)

  • 执行 SQL(查询、插入、更新、删除)

  • 获取执行结果

  • 控制事务(提交 / 回滚)

📌 通俗理解:JDBC 就是 Java 和数据库之间的“翻译器”。

面试官常问:

“你写过 JDBC 吗?它和 MyBatis 的本质区别是什么?”

✅ 回答建议:

  • JDBC 是低层原生接口,MyBatis 是对 JDBC 的封装;

  • JDBC 更灵活但代码多,MyBatis 简洁但封装深;

  • 理解 JDBC 原理有助于定位 MyBatis 的性能问题。


1.2 JDBC 的工作原理(流程图)

JDBC 工作流程大致如下:

加载驱动 → 获取连接 → 创建 Statement → 执行 SQL → 处理结果 → 关闭资源

你可以类比理解为:

你(Java 程序) → 说话翻译(JDBC 驱动) → 数据库 → 给出回应 → 翻译回来 → 你处理结果

关键类与接口如下:

步骤对应类或方法说明
加载驱动Class.forName()注册 JDBC 驱动
获取连接DriverManager.getConnection()连接数据库
执行语句Statement / PreparedStatement发起 SQL
处理结果ResultSet查询结果
关闭资源close() 方法防止连接泄漏

1.3 常见 JDBC 驱动类型及区别

JDBC 驱动分为 4 种类型,了解即可,面试可重点记 Type 4。

类型简介示例是否推荐
Type 1JDBC-ODBC 桥接早期版本,几乎废弃❌ 不推荐
Type 2Java 到本地 API依赖本地库(DLL、so)❌ 移植性差
Type 3中间服务器转发稀有,复杂
Type 4纯 Java 驱动如:com.mysql.cj.jdbc.Driver✅ 最常用、性能好

1.4 第一个 JDBC 示例程序

import java.sql.*;
​
public class JdbcDemo {public static void main(String[] args) throws Exception {// 1. 加载驱动(MySQL 8 以后可省略)Class.forName("com.mysql.cj.jdbc.Driver");
​// 2. 获取连接Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testdb?serverTimezone=UTC", "root", "password");
​// 3. 创建 Statement 执行 SQLString sql = "SELECT id, name FROM users";Statement stmt = conn.createStatement();ResultSet rs = stmt.executeQuery(sql);
​// 4. 处理结果while (rs.next()) {int id = rs.getInt("id");String name = rs.getString("name");System.out.println("用户ID: " + id + ",用户名:" + name);}
​// 5. 关闭资源rs.close();stmt.close();conn.close();}
}

✅ 重点提醒:

  • 要导入 MySQL JDBC 驱动 jar 包(如:mysql-connector-j

  • 使用完连接必须关闭,避免“连接泄漏”

  • 实际项目中不直接使用 Statement,而是用 PreparedStatement


总结

  • JDBC 是数据库访问的基础技能,虽然在项目中被框架封装,但面试时经常会问底层细节

  • 记住六步流程,尤其是连接获取、预编译 SQL、防止注入;

  • JDBC 是学习 Spring JDBC、MyBatis 的前提。


好的,下面是第二章的正文内容,继续坚持“原理 + 实战 + 面试”的思路,帮助 Java 学习者全面掌握 JDBC 核心接口。


二、JDBC 核心 API 详解

学习 JDBC,最绕不开的就是这几个核心接口:DriverManagerConnectionStatementPreparedStatementResultSet。这些东西不仅写代码用得上,面试时也常被用来考察对底层机制的理解。


2.1 核心接口一览

接口/类名作用
DriverManager管理数据库驱动,创建数据库连接
Connection表示数据库连接,支持事务管理
Statement用于执行静态 SQL 语句
PreparedStatement用于执行参数化 SQL,预编译,防止 SQL 注入
ResultSet查询结果集,可遍历读取每一行数据

2.2 PreparedStatementStatement 的区别与优劣

虽然两者都能执行 SQL,但差距很大。

✅ Statement 示例(不推荐):
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users WHERE name = '" + name + "'");
问题:
  • SQL 拼接麻烦

  • 存在 SQL 注入风险

  • 无法预编译,性能差


✅ PreparedStatement 示例(推荐):
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE name = ?");
ps.setString(1, name);
ResultSet rs = ps.executeQuery();
优点:
  • 预编译,提高性能(尤其在批量处理时)

  • 防止 SQL 注入

  • 代码结构清晰,参数类型更安全

🎯 面试官常问:你能讲讲 PreparedStatement 和 Statement 的区别吗?

回答思路:

  • 安全性:PreparedStatement 能防 SQL 注入,Statement 不能;

  • 性能:PreparedStatement 会预编译 SQL,提高效率;

  • 可维护性:PreparedStatement 参数绑定清晰,SQL 拼接更安全简洁。


2.3 ResultSet 遍历与数据提取技巧

ResultSet 是 SQL 查询结果的封装对象,遍历它的方式如下:

while (rs.next()) {int id = rs.getInt("id");String name = rs.getString("name");// 还可以 rs.getDate(), rs.getDouble() 等
}
常见问题:
  • 别忘了先 rs.next() 移动指针!

  • 字段类型要和数据库字段匹配,避免类型转换错误


2.4 JDBC 操作流程六步详解

这六步是经典套路,也是每一道 JDBC 面试题的“基本功”。

步骤说明示例
1加载驱动Class.forName("com.mysql.cj.jdbc.Driver");(新版本可省略)
2获取连接DriverManager.getConnection(...)
3创建 SQL 对象PreparedStatement ps = conn.prepareStatement(sql);
4执行 SQLps.executeQuery()ps.executeUpdate()
5处理结果遍历 ResultSet
6释放资源先关 ResultSet,再关 Statement,最后关 Connection

⚠️ 注意资源释放顺序

资源释放顺序必须从“最小”向“最大”依次关闭:

try {// 使用资源
} finally {if (rs != null) rs.close();if (ps != null) ps.close();if (conn != null) conn.close();
}

或者使用 Java 7+ 的 try-with-resources:

try (Connection conn = ...;PreparedStatement ps = ...;ResultSet rs = ...
) {// 自动关闭资源
}

小结

  • JDBC 最核心的接口就是这一组:ConnectionPreparedStatementResultSet

  • PreparedStatement 是面试最喜欢考的点,务必掌握其优势与使用方式

  • 理解 JDBC 的六步流程,是之后学习 Spring JDBC / MyBatis 的基础。


好的,下面是第三章的正文内容,聚焦于 JDBC 中的事务控制机制。这部分既是 JDBC 的重点,也与数据库事务知识(ACID)高度关联,是面试的常考模块。


三、JDBC 与事务控制

在真实项目中,我们对数据库的操作往往不是“查一下”这么简单。涉及增删改操作时,数据一致性就成了必须保障的底线。JDBC 提供了最原始但也是最灵活的事务控制能力。


3.1 JDBC 的事务控制机制

默认情况下,JDBC 的连接是自动提交(auto-commit)的,也就是说:

Connection conn = DriverManager.getConnection(...);
Statement stmt = conn.createStatement();
stmt.executeUpdate("UPDATE account SET balance = balance - 100 WHERE id = 1");
// 语句执行后,立刻自动提交
但是问题来了:
  • 如果你有多个语句组成一个业务逻辑,比如“转账”,就不能每句都立刻提交。

  • 一旦其中一句失败,就无法回滚前面已提交的语句,这将导致数据不一致!


3.2 手动控制事务(推荐做法)

Connection conn = DriverManager.getConnection(...);
try {conn.setAutoCommit(false); // 开启手动事务PreparedStatement ps1 = conn.prepareStatement("...");ps1.executeUpdate();PreparedStatement ps2 = conn.prepareStatement("...");ps2.executeUpdate();conn.commit(); // 所有操作成功后提交
} catch (Exception e) {conn.rollback(); // 有异常就回滚
} finally {conn.close();
}
✅ 这就是“原子性”的实现:

要么全部成功,要么全部失败,数据始终保持一致


3.3 多事务嵌套的控制

JDBC 原生并不直接支持嵌套事务(不像 Spring 支持传播机制),但你可能会在同一个连接里“模拟嵌套行为”。

  • 建议通过 分方法 + 同一个 Connection 传参 的方式,控制多个子事务。

  • 不要在子方法中随意提交事务,统一由调用方决定 commit/rollback。

✅ 面试场景题:两个子模块都要操作数据库,一个失败另一个必须回滚,如何处理?

答:统一由上层控制事务边界,子模块只抛异常或返回状态,不主动 commit/rollback。


3.4 事务中断异常怎么处理?

一旦出错,必须 rollback(),但很多人犯以下错误:

  • 错误做法:

    conn.setAutoCommit(false);
    stmt.executeUpdate(sql1);
    // 假如这里抛出异常……
    stmt.executeUpdate(sql2);
    conn.commit(); // 永远不会执行

    上面语句会卡在“执行了一半”,数据库被“脏改”。

  • 正确做法:commit()rollback() 都写在 try-catch 块中,保证异常后能回滚:

    try {conn.setAutoCommit(false);...conn.commit();
    } catch (Exception e) {conn.rollback(); // 抓异常就回滚
    }

✅ 面试陷阱题:JDBC 默认是自动提交的吗?

是的,JDBC 默认 autoCommit = true。这意味着每条执行完的 SQL 都立即提交!

所以如果你执行一串更新语句而忘记手动关掉 autoCommit,那就会:

  • 成功一半、失败一半

  • 无法回滚

  • 数据出问题,后果极难排查


小结

  • JDBC 默认自动提交事务,一定要手动设置 autoCommit(false) 来控制事务边界

  • commit()rollback() 要配对使用,并注意异常处理。

  • 嵌套事务应避免在子方法中操作事务边界,统一由调用者负责。

  • 事务控制是面试高频项,尤其在转账/订单/扣库存场景中反复考察。


当然,这一章会从最常用的 JDBC CURD 实战出发,给 Java 后端求职者和学习者一个“能直接搬到项目里”的落地模板,并结合典型业务场景和性能优化点。以下是正文内容:


四、JDBC 实战:增删改查 CURD 模板

JDBC 本质上就是“Java 写 SQL”,它提供了最基础、最直接的数据库访问能力。这一章我们就来搞定一件事:

如何用 JDBC 写出结构清晰、可维护、性能还不错的 CURD?


4.1 通用 CURD 模板封装思路

很多初学者写 JDBC 代码是这样的:

Connection conn = DriverManager.getConnection(...);
PreparedStatement ps = conn.prepareStatement("INSERT INTO user (name) VALUES (?)");
ps.setString(1, "Tom");
ps.executeUpdate();

这没问题,但重复多了会显得啰嗦又难维护。我们应该做的是:抽出公共模板 + 封装通用工具类。

推荐封装结构:
public class JdbcUtil {public static Connection getConnection() { ... }  // 获取连接public static void close(Connection conn, Statement stmt, ResultSet rs) { ... } // 关闭连接
}

4.2 批量插入与 addBatch() 使用

处理大量数据时,如果你还在用 for 循环一个个插入,那性能就彻底拉跨了。正确方式是用批处理:

Connection conn = JdbcUtil.getConnection();
String sql = "INSERT INTO user (name, age) VALUES (?, ?)";
PreparedStatement ps = conn.prepareStatement(sql);for (int i = 0; i < 1000; i++) {ps.setString(1, "User" + i);ps.setInt(2, 20 + i % 10);ps.addBatch();  // 批量添加if (i % 200 == 0) {ps.executeBatch(); // 每200条批量提交ps.clearBatch();}
}
ps.executeBatch();  // 最后不足200条的提交

✅ 这种方式比单条插入性能高 10 倍以上。


4.3 实战:实现一个简易 DAO 层(UserDAO)

这是面试中常见的问法:“你怎么设计 DAO 层?”

先定义实体类:
public class User {private int id;private String name;private int age;// getter / setter
}
再封装 DAO:
public class UserDAO {public void insert(User user) throws SQLException {Connection conn = JdbcUtil.getConnection();String sql = "INSERT INTO user (name, age) VALUES (?, ?)";PreparedStatement ps = conn.prepareStatement(sql);ps.setString(1, user.getName());ps.setInt(2, user.getAge());ps.executeUpdate();JdbcUtil.close(conn, ps, null);}public List<User> findAll() throws SQLException {Connection conn = JdbcUtil.getConnection();String sql = "SELECT * FROM user";PreparedStatement ps = conn.prepareStatement(sql);ResultSet rs = ps.executeQuery();List<User> list = new ArrayList<>();while (rs.next()) {User u = new User();u.setId(rs.getInt("id"));u.setName(rs.getString("name"));u.setAge(rs.getInt("age"));list.add(u);}JdbcUtil.close(conn, ps, rs);return list;}
}

✅ 你可以很容易扩展出 updatedeletefindById 等方法,完全覆盖项目需求。


4.4 JDBC 与典型业务场景实战

✅ 用户登录
public boolean login(String username, String password) {String sql = "SELECT * FROM user WHERE username = ? AND password = ?";Connection conn = JdbcUtil.getConnection();PreparedStatement ps = conn.prepareStatement(sql);ps.setString(1, username);ps.setString(2, password);ResultSet rs = ps.executeQuery();return rs.next();  // 查到即登录成功
}

面试加分提示:真实系统中密码要加密(如 MD5、BCrypt),这里为简化省略。


✅ 注册校验用户名是否存在
public boolean exists(String username) {String sql = "SELECT COUNT(*) FROM user WHERE username = ?";PreparedStatement ps = JdbcUtil.getConnection().prepareStatement(sql);ps.setString(1, username);ResultSet rs = ps.executeQuery();if (rs.next()) {return rs.getInt(1) > 0;}return false;
}

✅ 分页查询
public List<User> findByPage(int page, int size) {int offset = (page - 1) * size;String sql = "SELECT * FROM user ORDER BY id DESC LIMIT ?, ?";PreparedStatement ps = JdbcUtil.getConnection().prepareStatement(sql);ps.setInt(1, offset);ps.setInt(2, size);ResultSet rs = ps.executeQuery();// 同上遍历 rs 并封装 User
}

小结

  • JDBC 虽然底层,但是真实面试中很多问题都来自它。

  • 封装通用模板、精通批处理、了解业务常见场景,是你把 JDBC 用“好”的关键。

  • 如果你能写出一个简洁、扩展性强的 DAO 层,就比大多数人强很多了。


当然,下面是这一部分的正文内容,依旧保持实用性 + 面试导向 + 易落地的风格:


五、JDBC 性能优化与连接池技术

很多人学 JDBC 时最大的误区是:以为写完 Connection conn = DriverManager.getConnection(...) 就完事了。

错!性能问题往往就藏在这句代码里。

这章核心目标:搞清楚 JDBC 的性能瓶颈在哪?连接池怎么选、怎么配、怎么调?


5.1 JDBC 性能瓶颈在哪?

打开数据库连接是非常昂贵的操作(涉及三次握手、权限认证、内存资源分配等),而且频繁开关连接还会拖垮数据库。

👇 举个例子:

for (int i = 0; i < 1000; i++) {Connection conn = DriverManager.getConnection(...); // ❌ 慢 + 占资源PreparedStatement ps = conn.prepareStatement(...);...conn.close();
}

即使你在每次操作后都关闭连接,也会因为频繁连接 + 关闭而严重拖慢性能(甚至打爆连接池、耗尽数据库连接数)。


5.2 为什么要用连接池?

连接池的核心思路就是:

提前准备好一批连接,程序要用的时候“借”,用完再“还”。

这样就避免了频繁创建/销毁连接的问题,显著提高性能。

连接池带来的好处:
  • ✅ 避免频繁创建连接(重用连接)

  • ✅ 控制连接数量,避免资源泄露

  • ✅ 提供连接监控、超时检测、慢查询告警等能力


5.3 常见连接池技术对比

名称特点是否推荐
C3P0老牌连接池,功能齐全,但性能较差❌ 过时
DBCPApache 提供,轻量但稳定性一般⚠️ 一般
HikariCPSpring Boot 默认连接池,极致性能✅ 推荐
Druid阿里出品,功能丰富、监控强大,但稍重✅ 大厂常用

面试问你连接池用哪个?——Spring Boot 默认用的是 HikariCP,Druid 常见于业务复杂系统。


5.4 HikariCP 实践:配置、监控、调优

HikariCP 是目前性能最强、最现代的连接池实现,Spring Boot 从 2.x 开始就默认采用它。

✅ HikariCP 基本配置(application.yml)
spring:datasource:url: jdbc:mysql://localhost:3306/demo?serverTimezone=UTCusername: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driverhikari:minimum-idle: 5maximum-pool-size: 15idle-timeout: 600000max-lifetime: 1800000connection-timeout: 30000
参数说明:
  • maximum-pool-size: 最大连接数,决定并发能力,可根据服务器核数适配(CPU 核数 * 2)

  • idle-timeout: 空闲连接最大存活时间,建议10分钟

  • max-lifetime: 连接最大生命周期,防止被数据库强踢

  • connection-timeout: 获取连接最大等待时间


5.5 如何监控连接池?

如果你用的是 Druid,可以直接访问内置的监控界面:

spring.datasource.druid.stat-view-servlet.enabled=true
spring.datasource.druid.stat-view-servlet.login-username=admin
spring.datasource.druid.stat-view-servlet.login-password=123456

访问 http://localhost:8080/druid 即可查看实时连接数、SQL 执行时间等信息。

而 HikariCP 可以通过 Micrometer + Actuator 监控:

<dependency><groupId>io.micrometer</groupId><artifactId>micrometer-core</artifactId>
</dependency>

加上 Spring Boot Actuator 之后,访问 /actuator/metrics/hikaricp.connections.active 等指标即可。


✅ 总结一句话:

  • 连接池是 JDBC 性能优化的第一要素

  • 项目小就用 HikariCP(默认、快),项目复杂就用 Druid(功能强)

  • 永远不要每次操作都新建一个 Connection


六、JDBC 与数据库安全

安全永远是开发中不能忽视的问题,尤其是涉及数据库访问时。很多“老掉牙”的漏洞,其实至今依然频繁出现——比如最常见的:SQL 注入。

这一章,我们就从 JDBC 层面,深入聊一聊:

  • JDBC 怎么防 SQL 注入?

  • PreparedStatement 真的是万能的吗?

  • 代码和数据库分别该怎么做好权限隔离?


6.1 防止 SQL 注入的最佳实践

什么是 SQL 注入?

SQL 注入(SQL Injection)指的是攻击者通过构造恶意输入,干扰 SQL 查询结构,从而绕过验证、窃取数据甚至破坏数据的行为。

👇 举个“死亡写法”的例子:

String username = request.getParameter("username");
String password = request.getParameter("password");String sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);

如果攻击者输入的是:

username = admin' -- 
password = 随便

最终拼接出的 SQL 会变成:

SELECT * FROM users WHERE username = 'admin' -- ' AND password = '随便'

结果:整个 password 条件被注释,直接登录成功!


6.2 如何防御:使用 PreparedStatement

正确的写法如下 👇:

String sql = "SELECT * FROM users WHERE username = ? AND password = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, username);
ps.setString(2, password);
ResultSet rs = ps.executeQuery();

PreparedStatement 会自动将输入进行参数预编译与转义,彻底避免注入漏洞

✅ 好处:
  • 自动处理特殊字符(如 '" 等)

  • 提前编译 SQL,提高性能

  • 完全阻断 SQL 注入风险


6.3 PreparedStatement 有局限吗?

是的,它并非万能,尤其在动态构建 SQL 结构时。

例如下面这种“模糊查询 + 动态字段”的写法:

String column = request.getParameter("sortBy"); // 危险!
String sql = "SELECT * FROM products ORDER BY " + column;

攻击者传入 sortBy=1 desc; DROP TABLE products;,那你基本就可以下班了。

应对策略:
  • 永远不要让用户控制 SQL 的结构(如字段名、表名、order by 等)

  • 对字段名做白名单过滤(限定只能是 idnameprice 等)


6.4 数据库权限控制(代码层 + 数据库层双保险)

除了写代码要安全,还要从数据库账号本身“限权”:

✅ 原则:最小权限原则(Least Privilege)
  • 业务用账号不能有 DROPALTER 等高权限

  • 管理账号仅供管理员使用,不放进代码里

  • 分模块设置账号:只读账号 / 读写账号 / 审计账号

👇 代码层也可通过 @Secured@PreAuthorize 做权限控制:
@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(...) {...
}

✅ 总结一句话:

  • 永远用 PreparedStatement,别拼接 SQL

  • 防注入的核心:用户输入不能影响 SQL 结构

  • 数据库和代码都要限权,不留后门


如果你准备面试,这一章很有可能就是“开胃题”,比如:

面试官:你知道 PreparedStatement 和 Statement 有什么区别吗? 你可以直接说:“PreparedStatement 是防注入的核心机制,因为……”


七、JDBC 与框架集成:从原始连接到高效实战

JDBC 是 Java 世界访问数据库的“底层接口”,但如果你真的在企业项目中还在手写一堆 try-catch-finally,那只能说——你该升级了。

所以这一章,我们就来聊聊:

  • 为什么 Spring JDBC 更“香”?

  • JdbcTemplate 是怎么帮我们简化代码的?

  • 什么时候应该用 MyBatis?JDBC 和 ORM 怎么选?

  • JDBC 在实际项目中还有哪些典型用法?


7.1 Spring JDBC 简介:让底层 JDBC 更“丝滑”

Spring Framework 为原生 JDBC 提供了一个更轻量的封装层:Spring JDBC,也称 JdbcTemplate。

它解决的核心痛点就是:

原生 JDBC 的痛点Spring JDBC 的优化
代码冗长、重复简洁模板式写法
异常处理复杂统一包装成 DataAccessException
资源释放容易写漏(如关闭)自动关闭 Connection、Statement、ResultSet
参数设置容易出错自动填充参数

简单理解就是:Spring JDBC 是对原生 JDBC 的“工具化封装”,让你更专注于业务逻辑,而不是底层流程。


7.2 JdbcTemplate 使用指南

Spring 提供的 JdbcTemplate 是 Spring JDBC 的核心工具类。

典型使用方式:
@Autowired
private JdbcTemplate jdbcTemplate;public List<User> getUsers() {String sql = "SELECT * FROM users";return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
}
参数绑定 + 插入示例:
String sql = "INSERT INTO users(name, age) VALUES (?, ?)";
jdbcTemplate.update(sql, "Tom", 25);
查询一个值(比如总数):
String sql = "SELECT COUNT(*) FROM users";
int count = jdbcTemplate.queryForObject(sql, Integer.class);

是不是比原始的 Statement + ResultSet 干净太多了?


7.3 与 MyBatis 的对比:什么时候用 JDBC,什么时候用 ORM?

对比项JdbcTemplateMyBatis
性质半自动(基于 SQL)全自动映射 ORM 框架
学习曲线较低中等(XML / 注解配置)
灵活性更贴近原生 SQL可扩展性好,支持 SQL 映射、插件机制
适合场景简单 CRUD、批处理复杂业务、多表关联、动态 SQL
与 JDBC 的关系基于 JDBC 封装底层仍然使用 JDBC
简单说:
  • 小型服务 / 简单 CRUDJdbcTemplate 非常轻量高效;

  • 中大型项目 / 动态查询多MyBatis 更灵活,也更可维护;

  • 纯 ORM 实体映射需求重:考虑 Hibernate 但要小心性能坑。


7.4 企业项目中 JDBC 的典型应用场景

JDBC 本质是“数据库访问协议”,即使用了框架,底层还是 JDBC。

常见使用方式:
  • 批处理任务(如 Spring Batch 中的数据库写入)

  • 后台脚本 / 任务系统 / 数据导出

  • 与传统系统对接时(只支持 JDBC 驱动)

  • 需要极致性能优化的场景(手动控制连接与执行)

在这些“贴底层”的场景里,JdbcTemplate + 手动 SQL 仍然是非常可靠的选择。


✅ 小结:框架是帮你提效,不是让你忘了底层

  • 原生 JDBC 太冗长,Spring JDBC 提供高效封装;

  • JdbcTemplate 是 JDBC 入门的最佳实战工具;

  • MyBatis / Hibernate 适合复杂业务场景,但 JDBC 更轻、更快;

  • 面试常问:“JDBC 和 ORM 哪个好?”别说“我都用”,说“按需选型”。


八、常见问题与面试题精选

JDBC 是 Java 与数据库之间的桥梁,但真正面试时,考察的不是你记了多少 API,而是你对连接池、事务控制、封装思想的理解是否深入。

这一节我们拆解 4 个高频面试方向,帮你建立系统化认知:


1️⃣ JDBC 与连接池的面试高频题解析

问题 1:为什么 JDBC 不建议频繁创建和关闭连接?

JDBC 的 Connection 对象是重量级资源,底层实际是“网络 TCP 连接 + 数据库 session”。 每次 DriverManager.getConnection() 都会发起一次物理连接,成本高昂。

面试建议答法:

  • 每次连接的建立和销毁会造成性能瓶颈;

  • 在高并发环境下更容易造成数据库连接资源耗尽;

  • 正确做法是使用连接池(如 Druid、HikariCP)复用连接。


问题 2:你了解哪些常见连接池?区别在哪?

连接池特点
DBCP早期 Spring 默认使用,已逐渐淘汰
C3P0配置简单,性能一般
Druid阿里出品,监控强大,使用广泛
HikariCP性能最佳,Spring Boot 默认连接池

建议重点掌握 Druid(监控强)和 HikariCP(速度快)。


2️⃣ JDBC 如何实现事务控制?嵌套事务如何处理?

问题 3:JDBC 默认是自动提交事务的吗?如何手动控制?

是的,JDBC 默认执行完每条 SQL 就自动提交。 想手动控制事务,需要关闭自动提交模式:

conn.setAutoCommit(false); // 开启手动提交模式
try {// 执行业务 SQLconn.commit();
} catch (Exception e) {conn.rollback();
}

问题 4:JDBC 支持嵌套事务吗?怎么处理?

原生 JDBC 不支持嵌套事务。你只能通过逻辑封装来模拟“嵌套”控制,比如利用保存点(SavePoint):

Connection conn = getConnection();
conn.setAutoCommit(false);
Savepoint sp = conn.setSavepoint();try {// 第一个事务逻辑// 第二段逻辑出错conn.rollback(sp); // 回滚到 savepointconn.commit();
} catch (Exception e) {conn.rollback();
}

面试建议回答:

JDBC 不直接支持嵌套事务,但可通过 Savepoint 模拟部分事务回滚能力。


3️⃣ 如何解决 JDBC 中连接泄漏问题?

连接泄漏 = 获取了连接但没有释放,长期积压导致连接池耗尽、系统崩溃。

常见原因:

  • 忘记调用 conn.close()

  • 异常发生后未进入 finally 释放连接;

  • 多线程并发下 Connection 被覆盖。

解决方案:

  • 强制使用 try-with-resources(Java 7 起)自动释放资源:

try (Connection conn = getConnection()) {// 操作
} // 自动 close()
  • 使用连接池 + 配置连接超时、空闲检测;

  • 配合 Druid 监控分析连接使用情况。


4️⃣ 面试官提问:“如果让你封装一个通用 JDBC 工具类,你怎么设计?”

这个问题非常经典,既考察编码能力,也考察抽象能力。

答题思路:

我会从复用性、可扩展性、安全性角度出发,封装以下功能模块:

  1. 统一管理连接池数据源(可注入 Druid 或 Hikari);

  2. 提供通用方法(如 queryList()update()queryOne());

  3. 使用泛型 + RowMapper 回调封装结果映射逻辑

  4. 使用 try-with-resources 避免资源泄漏

  5. 异常统一封装为 RuntimeException,避免调用者处理繁琐 SQL 异常。

代码结构示意:

public class JdbcUtil {private static DataSource ds = ...;public static <T> List<T> queryList(String sql, RowMapper<T> mapper, Object... params) {try (Connection conn = ds.getConnection();PreparedStatement ps = conn.prepareStatement(sql)) {// 填充参数 + 执行 + 映射}}
}

✅ 小结

问题维度面试关键点
连接池原理性能优化、复用连接、监控配置
事务控制手动控制、回滚、嵌套模拟 SavePoint
连接泄漏问题try-with-resources、连接池配置、监控排查
封装思想泛型、异常处理、模板方法、低耦合可维护的代码设计思维

http://www.xdnf.cn/news/914.html

相关文章:

  • 【数据结构和算法】3. 排序算法
  • ubuntu20.04安装安装x11vnc服务基于gdm3或lightdm这两种主流的显示管理器。
  • LlamaIndex 生成的本地索引文件和文件夹详解
  • PaginationInnerInterceptor使用(Mybatis-plus分页)
  • RUI 桌面 appTV 版中文版下载 RUI 桌面桌面固件包实用攻略
  • Visual Studio 2022 运行一个后台程序而不显示控制台窗口
  • 悟空黑桃 下载地址
  • 自动驾驶最新算法进展
  • 【EasyPan】项目常见问题解答(自用持续更新中…)
  • 位运算题目:循环码排列
  • Lesson 7 DNS域名解析服务器
  • Java秒杀功能-案例
  • jvm-获取方法签名的方法
  • 【uniapp-兼容性处理】安卓uView组件中u-input后置插槽不展示
  • 03-HTML常见元素
  • win10设置软件开机自启
  • 从0开始配置spark-local模式
  • 聊透多线程编程-线程互斥与同步-12. C# Monitor类实现线程互斥
  • Prompt 攻击与防范:大语言模型安全的新挑战
  • Google Store 如何利用 glTF 3D 模型改变产品教育
  • L1-1、Prompt 是什么?为什么它能“控制 AI”?
  • C++入门语法
  • 在线查看【免费】 mp3,wav,mp4,flv 等音视频格式文件文件格式网站
  • Spark,IDEA编写Maven项目
  • c++算法-(1)
  • Precision Machine Dynamics/Mechatronics Design - 5
  • 算法工程师面试题与参考答案资料(2025年版)
  • Python实例题:Pvthon3实现简单的FTP认证服
  • Pycharm(九)函数的闭包、装饰器
  • 【TeamFlow】4.1 Git使用指南