事务(Transaction)详解
1. 什么是事务?
事务(Transaction)是数据库操作的最小工作单元,保证多个 SQL 语句作为一个整体执行,要么全部成功,要么全部失败,确保数据的一致性和完整性。
例如,在银行转账的场景中:
- 从 A 账户扣除 100 元
- 向 B 账户增加 100 元
如果第一步执行成功,但第二步失败(比如数据库崩溃),账户 A 的钱就凭空少了,这会导致数据不一致。事务可以保证这两个操作要么都成功,要么都回滚(撤销),避免数据错误。
2. 事务的 4 大特性(ACID)
事务具有 ACID 这四个关键特性:
特性 | 含义 |
---|---|
A(Atomicity)原子性 | 事务中的操作不可分割,要么全部成功,要么全部失败 |
C(Consistency)一致性 | 事务执行前后,数据必须保持一致(符合业务规则) |
I(Isolation)隔离性 | 并发事务之间相互隔离,避免影响 |
D(Durability)持久性 | 事务提交后,数据永久保存,即使系统崩溃 |
3. 事务隔离级别
当多个事务同时运行时,可能会出现脏读、不可重复读、幻读等问题。数据库提供了不同的隔离级别来控制事务之间的影响。
隔离级别 | 脏读 | 不可重复读 | 幻读 | 性能 |
---|---|---|---|---|
读未提交(Read Uncommitted) | 可能 | 可能 | 可能 | 高(并发高) |
读已提交(Read Committed) | 不可能 | 可能 | 可能 | 中 |
可重复读(Repeatable Read)(MySQL 默认) | 不可能 | 不可能 | 可能 | 低 |
串行化(Serializable) | 不可能 | 不可能 | 不可能 | 最低(锁表) |
问题解释:
- 脏读:一个事务可以读取另一个未提交事务的数据(导致错误)。
- 不可重复读:同一事务中多次读取同一条记录,结果不一致(因为别的事务修改了数据)。
- 幻读:事务 A 读取了一批符合条件的记录,但事务 B 插入了新记录,事务 A 重新查询时发现数据变了。
4. Spring 事务管理
Spring 提供了声明式事务和编程式事务两种方式来管理事务。
4.1 声明式事务(推荐)
Spring 通过 @Transactional
注解 实现事务管理,无需手动编写 commit()
或 rollback()
。
示例:转账操作
@Service
public class AccountService {
@Autowired
private AccountDao accountDao;
@Transactional // 开启事务
public void transferMoney(int fromId, int toId, double amount) {
accountDao.decreaseBalance(fromId, amount); // 扣款
accountDao.increaseBalance(toId, amount); // 加款
}
}
🚀 如果 increaseBalance()
失败,Spring 会自动回滚 decreaseBalance()
,保证事务完整性!
4.2 事务的传播机制
当一个事务调用另一个事务方法时,Spring 提供了7 种事务传播机制,决定新事务如何运行:
传播行为 | 作用 |
---|---|
REQUIRED(默认) | 如果当前没有事务,就创建新事务;否则加入已有事务 |
REQUIRES_NEW | 总是创建新事务,挂起当前事务 |
SUPPORTS | 支持当前事务,如果没有就不开启事务 |
MANDATORY | 必须在已有事务中运行,否则抛异常 |
NOT_SUPPORTED | 不支持事务,在非事务环境下运行 |
NEVER | 不能在事务中运行,否则抛异常 |
NESTED | 在当前事务中创建子事务,可独立回滚 |
示例:REQUIRES_NEW
让子方法独立事务执行
@Transactional
public void mainTransaction() {
saveOrder(); // 事务 1
saveLog(); // 事务 2
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void saveLog() {
// 这个方法的事务与 mainTransaction() 独立
}
🚀 如果 saveOrder()
失败,saveLog()
依然会提交!
5. 编程式事务
如果你需要手动控制事务提交/回滚,可以使用 TransactionTemplate
或 PlatformTransactionManager
。
@Autowired
private PlatformTransactionManager transactionManager;
public void updateData() {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 执行业务逻辑
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status); // 出错回滚
}
}
🚀 适用于需要灵活控制事务提交/回滚的场景!
6. 事务的回滚
默认情况下,Spring 只会对运行时异常(RuntimeException
)回滚,而不会回滚 CheckedException
(受检异常)。
如果你希望 所有异常都回滚,可以使用 rollbackFor
:
@Transactional(rollbackFor = Exception.class)
public void processTransaction() {
// 任何异常都会回滚
}
7. 数据库事务 VS Spring 事务
特性 | 数据库事务 | Spring 事务 |
---|---|---|
适用范围 | 仅限数据库 | 支持数据库、消息队列、缓存等 |
事务传播 | 由数据库控制 | Spring 提供 7 种传播机制 |
编程方式 | 手动 commit()/rollback() | 声明式 @Transactional |
8. 总结
- 事务保证数据一致性,避免数据丢失、不一致问题。
- ACID 特性确保事务的可靠性。
- Spring 事务管理支持 声明式(推荐) 和 编程式 两种方式。
- 事务传播机制控制事务如何在不同方法间传播。
- 事务隔离级别影响并发事务的行为,MySQL 默认"可重复读"(Repeatable Read)。
- 默认情况下,Spring 只对
RuntimeException
进行回滚,CheckedException
不回滚。
Spring 事务管理非常强大,可以帮助我们轻松实现高并发环境下的数据安全,在企业级开发中至关重要!🚀