06. 事务管理:@Transactional 与 ACID¶
本节目标
在上一节中,我们实现了“批量删除用户”的功能。
想象一下,管理员勾选了 10 个用户点击删除。系统正在一个个地执行删除操作,当删到第 5 个用户时,系统突然报错(比如网络断了,或者触发了某种保护机制)。
后果: 前 4 个用户已经被删没了,后面的没删掉,系统处于一个“半死不活”的尴尬状态。
本节我们将学习数据库的事务 (Transaction) 机制,利用 Spring Boot 的 @Transactional 注解,实现“要么全部删除成功,要么一个都别动”的安全机制。
💣 第一步:危机时刻——数据“半死不活”¶
我们先来看一个没有事务保护的批量删除场景。假设由于业务逻辑复杂,我们需要在 Service 层遍历删除。
1. 模拟代码 (无事务)¶
2. 后果演示¶
假设前端传来了 [101, 102, 103, 104, 105] 五个 ID。
- 执行前:5 个用户都在。
- 执行后:
- ID
101,102没了 (被永久删除了)。 - ID
103报错。 -
ID
104,105还在。 -
结论:操作只成功了一半,数据不一致!如果这是银行转账,后果不堪设想。
🛡️ 第二步:事务与 ACID 理论¶
为了解决这个问题,我们需要引入 事务 (Transaction)。事务能保证这一组删除操作是一个原子包:同生共死。
1. 核心理论:ACID¶
面试必考题,结合用户模块理解:
| 特性 | 英文 | 解释 (用户管理版) |
|---|---|---|
| 原子性 | Atomicity | 同生共死。这一批 10 个用户的删除操作是一个整体。要么 10 个全删掉,要么一个都别删(回滚)。 |
| 一致性 | Consistency | 守恒定律。事务结束后,数据库必须处于合法的状态。 |
| 隔离性 | Isolation | 各玩各的。当我在批量删除时(事务未提交),别的管理员在查询用户列表,他应该看到的是删除前的完整列表,而不是只剩一半的残缺列表。 |
| 持久性 | Durability | 落袋为安。一旦提示“删除成功”,这些数据就彻底从硬盘消失了,服务器重启也找不回。 |
2. 事务的运作流程¶
graph TD
Start(("开始批量删除")) --> Del1["删除用户 1"]
Del1 --> Del2["删除用户 2"]
Del2 --> Crash{"第 3 个报错!"}
Crash -- 无事务 --> ResultBad["❌ 悲剧发生<br/>用户 1,2 丢失<br/>用户 3,4,5 尚存"]
Crash -- 有事务 --> Rollback["🔄 回滚 (Rollback)<br/>时光倒流<br/>复活用户 1,2"]
Rollback --> ResultGood["✅ 原始状态<br/>所有用户都在"]
style ResultBad fill:#ef5350,stroke:#b71c1c
style ResultGood fill:#81c784,stroke:#2e7d32
⚡ 第三步:Spring Boot 的魔法——@Transactional¶
在 Spring Boot 中,开启事务保护易如反掌。
1. 加上注解¶
在 UserService 的方法上添加 @Transactional 注解。
2. 效果验证¶
当代码运行到第 3 个用户抛出异常时:
- Spring 捕获到异常。
- Spring 指挥数据库执行 ROLLBACK 操作。
- 你会惊奇地发现:之前被删掉的用户 1 和 用户 2,又重新出现在数据库里了!
🚧 第四步:避坑指南——事务失效的“元凶”¶
很多新手加了注解,发现事务还是没回滚。90% 的原因是你自己把异常“吃掉”了!
❌ 错误写法 (Swallowing Exceptions)¶
✅ 正确写法 1:不捕获 (推荐)¶
让异常直接抛给 Controller,最后由全局异常处理器(GlobalExceptionHandler)去处理。
✅ 正确写法 2:捕获并手动抛出¶
如果你非要自己 catch 做记录,必须在 catch 块里再抛出一个 RuntimeException。
🤖 第五步:AI 辅助理解¶
事务隔离级别(Isolation Level)也是面试重点。可以让 AI 帮你模拟场景。
让 AI 解释隔离级别
Prompt:
"请以用户管理系统为例,解释数据库事务的四大隔离级别(读未提交、读已提交、可重复读、串行化)。
场景:管理员 A 正在修改张三的密码(事务未提交),管理员 B 同时在查询张三的信息。不同的隔离级别下,B 会看到什么?(脏读、不可重复读、幻读)"
📝 总结¶
- 为什么需要事务:为了保证批量操作或多步骤操作(如删用户+删日志)的原子性。
- ACID:原子性、一致性、隔离性、持久性。
@Transactional:Spring 的声明式事务注解,通常加在 Service 层。- 失效陷阱:try-catch 必须抛出异常,否则事务不回滚。
下一步: 至此,你已经掌握了 Java Web 数据持久化层的四大护法:
- CRUD (基本功)
- 动态 SQL (灵活搜索)
- PageHelper (海量数据分页)
- 事务管理 (数据安全兜底)
但纸上得来终觉浅,目前我们的 BookDao 还是个“冒牌货”(使用 static Map 模拟)。
接下来,是时候进行大扫除了!我们将移除所有模拟代码,运用本章所学,真正打通 Spring Boot + MyBatis + MySQL 的任督二脉,实现数据的持久化存储。