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 1 | JDBC-ODBC 桥接 | 早期版本,几乎废弃 | ❌ 不推荐 |
Type 2 | Java 到本地 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,最绕不开的就是这几个核心接口:DriverManager
、Connection
、Statement
、PreparedStatement
、ResultSet
。这些东西不仅写代码用得上,面试时也常被用来考察对底层机制的理解。
2.1 核心接口一览
接口/类名 | 作用 |
---|---|
DriverManager | 管理数据库驱动,创建数据库连接 |
Connection | 表示数据库连接,支持事务管理 |
Statement | 用于执行静态 SQL 语句 |
PreparedStatement | 用于执行参数化 SQL,预编译,防止 SQL 注入 |
ResultSet | 查询结果集,可遍历读取每一行数据 |
2.2 PreparedStatement
与 Statement
的区别与优劣
虽然两者都能执行 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 | 执行 SQL | ps.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 最核心的接口就是这一组:
Connection
、PreparedStatement
、ResultSet
; -
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;} }
✅ 你可以很容易扩展出 update
、delete
、findById
等方法,完全覆盖项目需求。
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 | 老牌连接池,功能齐全,但性能较差 | ❌ 过时 |
DBCP | Apache 提供,轻量但稳定性一般 | ⚠️ 一般 |
HikariCP | Spring 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 等)
-
对字段名做白名单过滤(限定只能是
id
、name
、price
等)
6.4 数据库权限控制(代码层 + 数据库层双保险)
除了写代码要安全,还要从数据库账号本身“限权”:
✅ 原则:最小权限原则(Least Privilege)
-
业务用账号不能有
DROP
、ALTER
等高权限 -
管理账号仅供管理员使用,不放进代码里
-
分模块设置账号:只读账号 / 读写账号 / 审计账号
👇 代码层也可通过 @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?
对比项 | JdbcTemplate | MyBatis |
---|---|---|
性质 | 半自动(基于 SQL) | 全自动映射 ORM 框架 |
学习曲线 | 较低 | 中等(XML / 注解配置) |
灵活性 | 更贴近原生 SQL | 可扩展性好,支持 SQL 映射、插件机制 |
适合场景 | 简单 CRUD、批处理 | 复杂业务、多表关联、动态 SQL |
与 JDBC 的关系 | 基于 JDBC 封装 | 底层仍然使用 JDBC |
简单说:
-
小型服务 / 简单 CRUD:
JdbcTemplate
非常轻量高效; -
中大型项目 / 动态查询多:
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 工具类,你怎么设计?”
这个问题非常经典,既考察编码能力,也考察抽象能力。
答题思路:
我会从复用性、可扩展性、安全性角度出发,封装以下功能模块:
-
统一管理连接池数据源(可注入 Druid 或 Hikari);
-
提供通用方法(如
queryList()
、update()
、queryOne()
); -
使用泛型 + RowMapper 回调封装结果映射逻辑;
-
使用 try-with-resources 避免资源泄漏;
-
异常统一封装为 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、连接池配置、监控排查 |
封装思想 | 泛型、异常处理、模板方法、低耦合可维护的代码设计思维 |
相关文章:
JDBC:数据库访问的原始接口
目录 一、JDBC 基础入门:数据库访问的原始接口 JDBC 是什么?它在 Java 中扮演什么角色? JDBC 工作原理图解(驱动 -> 连接 -> 执行 -> 关闭) 常见 JDBC 驱动类型及差异 第一个 JDBC 示例程序:连…...
使用 Electron 打包可执行文件和资源:完整实战教程
一.项目结构 项目结构建议如下: my-electron-app/ ├── example.exe ← 需打包的外部程序 ├── config.json ← 配置文件 ├── native-lib/ ← 自定义库或 DLL │ └── yourlib.dll ├── main.js …...
【网络安全】CI/CD 流水线漏洞
【网络安全】CI/CD 流水线漏洞 1. 保护您的软件管道:CI/CD 安全2. 什么是 CI/CD 以及它为何重要?2.1 持续集成(CI):构建坚实的基础2.2 持续交付(CD):准备发布2.3 持续部署࿰…...
计算机是如何工作的(上)
对于学习JavaEE初阶为什么要知道计算机是如何工作的,是因为在未来我们写代码的时候,会出现一些bug,而在代码层面是看不出来的,所以我们需要了解一些关于计算机内部是如何工作的,从而提高代码的健壮度。 计算机的组成&…...
【SF顺丰】顺丰开放平台API对接(Java对接篇)
对接前置篇: 【SF顺丰】顺丰开放平台API对接(注册、API测试篇)_顺丰api接口对接指南-CSDN博客 1.实现效果展示 2.SF顺丰开放平台,JDK资源下载。 下载地址:顺丰开放平台 3.将下载的JDK放入项目中。 4.将JDK资源引入p…...
【KWDB创作者计划】_针对KWDB时序数据库(多副本集群环境)进行压力测试
【KWDB创作者计划】_针对KWDB时序数据库(多副本集群环境)进行压力测试 1. 概述2. 压测环境部署3. 生成测试数据4. 写入性能测试5. 查询性能测试7. 总结 1. 概述 KWDB是一款主要应用于工业物联网、数字能源、车联网、智慧产业等领域的时序数据库ÿ…...
24.中医知识问答删除历史对话功能前端代码实现
前端实现对话删除功能的完整指南 功能概述 前篇文章介绍了删除历史对话的后端开发,本篇将介绍如何在前端实现一个完整的对话删除功能,包括用户确认、API调用、状态管理和错误处理等关键环节。 功能拆解 1. 用户确认机制 javascript const confirmDe…...
在Cursor编辑器上部署MCP(Minecraft Coder Pack)完整指南
MCP(Minecraft Coder Pack)是用于反编译和修改Minecraft Java版代码的工具包。本教程将详细介绍如何在Cursor编辑器中配置和运行MCP,以便高效地进行Minecraft模组开发或代码研究。 1. 准备工作 1.1 所需工具 Cursor编辑器(基于VS…...
STM32——相关软件安装
本文是根据江协科技提供的教学视频所写,旨在便于日后复习,同时供学习嵌入式的朋友们参考,文中涉及到的所有资料也均来源于江协科技(资料下载)。 Keil5 MDK安装 1.安装Keil5 MDK2.安装器件支持包方法一:离线…...
蓝牙WiFi模组rtl8821cs在Android14调
环境 SDK: AOSP14 主控:RK3576 蓝牙:RTL8821CS 先记一下官网文档关于蓝牙的资料 蓝牙 | Android Open Source Project 还在调,先看看啥情况,点赞多或者想起来记录再回来 TODO...
MCP实践第一步--磕磕碰碰搭环境
由于deepseek-r1不支持function calling,所以我们采用了deepseek-v3进行实践,模型名称为deepseek-chat,在deepseek官网获取api-key。 一、参照MCP官网设置环境 创建项目目录 uv init mcp-client # 若没有uv,则先通过pip instal…...
Java并发:线程池
目录 一、核心概念与设计原理 1、线程池的核心价值 2、核心接口和类 3、线程池的核心构造参数 4、线程池工作流程 二、参数选择 1、任务队列选择 2、拒绝策略选择 3、常见线程池选择 4、参数调优 三、 应用 1、创建建议 2、生命周期管理:优雅关闭 3、…...
Kubernetes集群超配节点容量
目录: 1、节点超配简介2、创建 PriorityClass3、运行请求节点容量的 Pod4、调整占位资源请求5、设置所需的副本数量6、自动扩缩容组件6.1、手动方式6.2、自动方式 1、节点超配简介 节点超配是一种主动预留部分集群计算资源的策略。这种预留有助于减少在扩缩容事件期…...
每日一题(小白)回溯篇7
首先我们可以判断出这是一个dfs的题目,因为简言之就是要求最短路径。其次这个题目与直接找最短路径有所不同,增加了条件必须依次穿过指定的符号。无论坦克走到任何一点都有四个方向可以走(越界要判断),结束的条件是到达…...
rk3588上完成halcon的形状模型配准以及和opencv的图像转换
一、准备工作 1)安装好halcon,确保halcon的c的调用是正常的 2)编译好opencv 上面的两个步骤,均可以参考我的两个博文完成: Halcon在linux及ARM上的安装及c工程化_halcon linux-CSDN博客 RK3588上编译opencv 及基于…...
Spring Boot 断点续传实战:大文件上传不再怕网络中断
精心整理了最新的面试资料和简历模板,有需要的可以自行获取 点击前往百度网盘获取 点击前往夸克网盘获取 一、痛点与挑战 在网络传输大文件(如视频、数据集、设计稿)时,常面临: 上传中途网络中断需重新开始服务器内…...
Springboot集成websocket实现消息推送
假设有个需求需要多个用户同时在对应的消息面板实时查看相关接口的执行流程进度,此时可以可考虑使用websocket来实现结果进度推送 一、引入websocket依赖,并编写WebSocket配置类 <dependency><groupId>org.springframework.boot</group…...
PostgreSQL 用户资源管理
PostgreSQL 用户资源管理 PostgreSQL 提供了多种机制来管理和限制用户对数据库资源的使用,以下是全面的资源管理方法: 1 连接限制 1.1 限制最大连接数 -- 在 postgresql.conf 中设置 max_connections 100 -- 全局最大连接数-- 为特定用户设置连接限…...
Uniapp:pages.json页面路由
目录 一、pages二、style 一、pages uni-app 通过 pages 节点配置应用由哪些页面组成,pages 节点接收一个数组,数组每个项都是一个对象,其属性值如下: 属性类型默认值描述pathString配置页面路径styleObject配置页面窗口表现nee…...
使用open3d将pcd点云按照颜色等级分块显示并令其随颜色变化播放
👑主页:吾名招财 👓简介:工科学硕,研究方向机器视觉,爱好较广泛… 💫签名:面朝大海,春暖花开! 使用open3d将pcd点云按照颜色等级分块显示并令其随颜色变化播放 引言显示效果点云获取完整代码引言 有很多时候我们需要更改pcd点云某些区域的颜色,可能是颜色随着点…...
玩转Docker | 使用Docker部署nullboard任务管理工具
玩转Docker | 使用Docker部署nullboard任务管理工具 前言一、nullboard介绍简介主要特点二、系统要求环境要求环境检查Docker版本检查检查操作系统版本三、部署nullboard服务下载镜像创建容器创建容器检查容器状态检查服务端口安全设置四、访问nullboard服务访问nullboard首页五…...
如何避免流程形式化导致的效率低下?
要避免流程形式化导致的效率低下,核心在于:聚焦流程价值、保障执行灵活性、优化流程设计、建立反馈机制、提升执行感知。其中,聚焦流程价值 是解决流程“空转”的首要原则。流程不应只是文档或制度的堆叠,而要服务于业务目标&…...
Java学习手册:HTTP 协议基础知识
一、HTTP 协议概述 HTTP(HyperText Transfer Protocol)即超文本传输协议,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传输协议。它是一个应用层协议,基于请求-响应模型…...
基于多模态融合算法的航空武器毁伤评估技术方案
基于多模态融合算法的航空武器毁伤评估技术方案 1. 引言 航空武器毁伤评估(Damage Assessment, DA)是现代战争中的关键环节,直接影响后续作战决策。传统的人工评估方式效率低、主观性强,且在高强度战场环境下难以实时完成。因此,本研究提出一种基于多模态融合算法的自动…...
欧拉-国产操作系统替代产品如何
欧拉(openEuler)国产操作系统是由华为发起并联合开源社区共同开发的企业级操作系统,旨在构建自主可控的数字基础设施生态底座。以下从开发背景、技术特点、应用场景、生态建设及市场表现等方面进行全面介绍: 一、开发背景与战略定位 国家需求驱动 在中美技术竞争背景下,国…...
入门-C编程基础部分:16、 预处理器
飞书文档https://x509p6c8to.feishu.cn/wiki/DzSJwsGiTiXkeCkyEYUcuXbKnbf C 预处理是编译过程中一个单独的步骤,是一个文本替换工具而已。所有的预处理命令都是以井号(#)开头。 指令描述#define定义宏#ifdef如果宏已经定义,则返…...
Spark-SQL4
一 .Spark-SQL核心编程(六) Spark-SQL连接Hive Apache Hive 是 Hadoop 上的 SQL 引擎,Spark SQL 编译时可以包含 Hive 支持,也可以不包含。包含 Hive 支持的 Spark SQL 可以支持 Hive 表访问、UDF (用户自定义函数)、Hive 查询语…...
模数转换【1】AD7699
1.开发背景 一款可以模数转换芯片,兼容通用 SPI 接口,支持最多支持 8 个通道和 500KSPS 的采样率。支持单个通道采集和扫描采集模式。 同系列的芯片还有 AD7682 和 AD7689 等。 2.开发需求 配置内部参考电压4.096V,实现单个通道采集和扫描采集…...
P5670 秘籍-反复异或 Solution
Description 给定序列 a ( a 1 , a 2 , ⋯ , a n ) a(a_1,a_2,\cdots,a_n) a(a1,a2,⋯,an) 和常数 m m m,有 q q q 个操作分两种: add ( l , r , x ) \operatorname{add}(l,r,x) add(l,r,x):对每个 i ∈ [ l , r ] i\in[l,r] …...
PID控制程序编写
目录 一、C语言PID控制器示例代码 二、代码解释 三、重要注意事项 一、C语言PID控制器示例代码 #include <stdio.h> #include <stdlib.h> #include <unistd.h> // For sleep function typedef struct { double Kp; // 比例增益 double…...
推荐系统/业务,相关知识/概念2
一、漫画库更新大量新作品,如何融入推荐系统? 参考答案: 快速提取新作品特征:除基础属性外,利用自然语言处理技术提取漫画简介关键词等丰富特征向量,分析情节、角色设定等深层次特征结合物品画像体系分类…...
基于Python的施工图与竣工图对比小工具开发方案
基于Python的施工图与竣工图对比小工具开发方案 一、引言 在工程建设领域,施工图与竣工图的对比是项目验收的关键环节。传统人工对比方式效率低、易出错,本文基于Python开发一款自动化对比工具,支持快速检测图纸差异并生成可视化报告&#x…...
2025年世界职业院校技能大赛实施方案(意见稿)
为贯彻落实《教育强国建设规划纲要(2024—2035年)》,进一步提升世界职业院校技能大赛(以下简称“大赛”)内涵质量,发挥大赛引领作用,提升高技能人才培养质量,服务现代职业教育体系建…...
辛格迪客户案例 | 苏州富士莱医药GMP培训管理(TMS)项目
一、案例概述 富士莱医药股份有限公司位于美丽的江南水乡常熟,前身为常熟富士莱医药化工有限公司,从建厂初期面积仅有10余亩,逐步扩展到100余亩。近年来公司飞速发展,以黑马姿态发展成为中国专业生产硫辛酸系列产品、肌肽系列产品…...
安恒安全渗透面试题
《网安面试指南》https://mp.weixin.qq.com/s/RIVYDmxI9g_TgGrpbdDKtA?token1860256701&langzh_CN 5000篇网安资料库https://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247486065&idx2&snb30ade8200e842743339d428f414475e&chksmc0e4732df793fa3bf39…...
【基于WSAAsyncSelec模型的通信程序设计】
文章目录 一、实验背景与目的二、实验设计与实现思路1. 设计思想2. 核心代码实现 总结 一、实验背景与目的 这次实验主要是为了让大家了解基于 WSAAsyncSelect 模型通信程序的编写、编译和执行过程。通过实践操作,深入掌握这种模型在实现计算机之间通信时的应用。 …...
Spring Boot+Mybatis设置sql日志打印
Spring BootMybatis设置sql日志打印 在全局配置文件添加以下内容:logging.level.com.demo.mapperdebug,com.demo.mapper:src下的mapper路径,debug:设置日志打印级别为debug,亦可设置为:ERROR、…...
Java 性能优化:从硬件到软件的全方位思考
Java 性能优化:从硬件到软件的全方位思考 在数字化飞速发展的当下,Java 作为广泛使用的编程语言,在各个应用领域发挥着关键作用。而 Java 性能优化作为提升系统运行效率、降低成本的核心环节,需要我们从硬件到软件进行全方位深入…...
ros2 humble moveit调试笔记
Overview — ROS2 Grasp Library Tutorials 0.5.0 documentation Robot Interface — ROS2 Grasp Library Tutorials 0.5.0 documentation 手动添加ompl_planning.yaml文件 planning_plugins:- ompl_interface/OMPLPlanner # To optionally use Ruckig for jerk-limited s…...
静态点亮数码管的某几段(STC89C52单片机)
#include <reg52.h> sbit ADDR0 P1^0; sbit ADDR1 P1^1; sbit ADDR2 P1^2; sbit ADDR3 P1^3; sbit ENLED P1^4; void main() { ENLED 0; //使能U3,选择数码管DS1 ADDR3 1; ADDR2 0; ADDR1 0; ADDR0 0; P0 0xF9; /…...
搭建哨兵架构
Redis大纲 3.1.sentinel环境准备: a.3个sentinel实例信息: 1.sentinel实例信息如下,sentinel只是起到监控作用,不存放数据!!2.为了节省资源,在同一台虚拟机开启3个实例,必须准备三…...
阿里云入门手册
阿里云入门手册 一、付费方式二、云计算基础概念(一)云计算定义(二)云计算优势(三)云计算部署模式(四)专有网络 VPC 三、阿里云ECS实例(一)实例类型ÿ…...
AI日报 - 2024年04月22日
🌟 今日概览(60秒速览) ▎🤖 模型进展 | Google发布Gemini 2.5 Flash,强调低延迟与成本效益;Kling AI 2.0展示多轴运动视频生成;研究揭示SLM在知识图谱上优于LLM,RLHF在推理提升上存局限。 ▎💼…...
Windows BilibiliHistoryFetcher-v1.3.2-v1.2.1-开源B站历史记录管理工具[支持批量管理下载]
Windows BilibiliHistoryFetcher 链接:https://pan.xunlei.com/s/VOONAVJpANYPmzu1Zc3B0q1XA1?pwdnrjc# 使用说明: 以1.3.2与1.2.1版本为例 需要下载 BiliBili-History-Frontend 前端程序,在下载 BilibiliHistoryAnalyzer 解压运行&#…...
CH585单片机的LCD外设怎么驱动段式LCD
1、首先这里只讲应用,不讲具体原理。 要驱动段式LCD,这里就要知道占空比的调整,比如1/4为例就需要4个COM口。这4个COM口由单片机自行驱动,你不用管。就像硬件IIC和SPI,时序问题不用你去操心,你要做的就是向…...
包管理工具有哪些?主流软件分享
常见的包管理工具主要有:npm、Yarn、pnpm、Composer、Maven、pip、Conda 等,其中 npm 是目前全球使用最广泛的JavaScript包管理工具,以丰富的生态、便捷的使用体验以及强大的社区支持闻名。npm具备依赖管理、版本控制、脚本执行等强大功能&am…...
【网络安全】OWASP 十大漏洞
1. OWASP 十大漏洞 为了应对未来的风险,安全专业人员需要随时掌握最新信息。之前,您了解了CVE 列表,这是一个公开的已知漏洞和暴露列表。CVE 列表是全球安全社区相互共享信息的重要信息来源。 在本文中,您将了解安全专业人士参考…...
Fortran中FORALL的用法与性能分析
Fortran中FORALL的用法与性能分析 FORALL语句的基本用法 FORALL是Fortran 95引入的一种结构,用于表达并行数组操作。它的基本语法如下: FORALL (index start:end:stride, mask)array(index) expression END FORALL或者更复杂的多维度形式࿱…...
web组件和http协议
1.web组件 2.自定义元素 3.影子DOM 4.HTML模板 5.http协议 6.tcp ip协议...
论文笔记-arXiv2025-FilterLLM
论文笔记-arXiv2025-FilterLLM: Text-To-Distribution LLM for Billion-Scale Cold-Start Recommendation FilterLLM:面向十亿级冷启动推荐的文本到分布大语言模型摘要1.引言2.前言2.1符号说明2.2文本到判断2.2.1候选生成(从数十亿到数百)2.2…...