事务(Transaction)
一. Java 事务的核心概念
- 原子性:事务中的所有操作要么全部完成,要么全部不完成
- 一致性:事务执行前后,数据库状态保持一致
- 隔离性:多个事务的执行不受其他事务干扰
- 持久性:事务完成后,修改永久保存到数据库
1. Spring 声明事务
@Service
public class TransferService {
@Autowired
private JdbcTemplate jdbcTemplate;
// 声明式事务,默认发生异常时回滚
@Transactional
public void transferMoney(Long fromId, Long toId, double amount) {
// 转出
jdbcTemplate.update(
"UPDATE account SET balance = balance - ? WHERE id = ?",
amount, fromId
);
// 转入
jdbcTemplate.update(
"UPDATE account SET balance = balance + ? WHERE id = ?",
amount, toId
);
}
}
2. Spring 事务注解的常用属性
@Transactional(
propagation = Propagation.REQUIRED, // 事务传播行为
isolation = Isolation.READ_COMMITTED, // 事务隔离级别
readOnly = false, // 是否为只读事务
timeout = 30, // 超时时间(秒)
rollbackFor = {Exception.class}, // 指定回滚的异常类型
noRollbackFor = {RuntimeException.class} // 指定不回滚的异常类型
)
2.1. 事务传播行为 Propagation
当一个事务方法调用另一个事务方法时,传播行为定义了如何处理事务:
REQUIRED:如果当前有事务,加入该事务;否则新建事务REQUIRES_NEW:无论当前是否有事务,都新建一个事务SUPPORTS:如果当前有事务,加入该事务;否则以非事务方式执行NOT_SUPPORTED:以非事务方式执行,如果当前有事务,暂停该事务MANDATORY:必须在事务中执行,否则抛出异常NEVER:必须在非事务中执行,否则抛出异常NESTED:如果当前有事务,则嵌套在该事务中执行
2.2. 事务隔离级别
Java 定义了 5 种事务隔离级别:
DEFAULT:使用数据库默认隔离级别READ_UNCOMMITTED:最低级别,允许读取未提交的数据READ_COMMITTED:保证读取已提交的数据,避免脏读REPEATABLE_READ:保证多次读取同一数据结果一致,避免不可重复读SERIALIZABLE:最高级别,完全隔离,避免幻读
-----------------------------------------------------------------------------------------
二.事务的回滚行为主要由事务传播行为(Propagation) 决定
-----------------------------------------------------------------------------------------
场景 1:事务方法 A 调用事务方法 B(两个都有@Transactional注解)
假设方法 A 和方法 B 都有@Transactional注解,当方法 A 调用方法 B 时:
- 关键因素:两个方法的
propagation属性配置(默认是REQUIRED) - 回滚规则:
- 若 B 抛出异常,且异常满足回滚条件(默认是
RuntimeException及其子类):- 如果 B 的传播行为是
REQUIRED(默认):B 会加入 A 的事务,A 和 B 会一起回滚(因为在同一个事务中)。 - 如果 B 的传播行为是
REQUIRES_NEW:B 会开启新事务,只有 B 回滚,A 的事务不受影响(除非 A 也捕获并抛出异常)。
- 如果 B 的传播行为是
- 若 A 在调用 B 之后抛出异常:
- 无论 B 的传播行为如何,A 的事务会回滚,但
REQUIRES_NEW的 B 事务不会回滚(已独立提交)。
- 无论 B 的传播行为如何,A 的事务会回滚,但
- 若 B 抛出异常,且异常满足回滚条件(默认是
看回滚规则,是否还是同一个事务
场景 2:普通函数调用事务方法 B(只有 B 有@Transactional)
普通函数(无@Transactional)调用事务方法 B 时:
- B 的事务会正常生效(独立事务)。
- 若 B 内部抛出异常并满足回滚条件,只有 B 的事务回滚,调用者的普通函数无事务,不存在回滚概念。
- 若 B 成功执行后,调用者的普通函数后续逻辑出错,B 的事务不会回滚(已提交)。
事务提交了,不会回滚
场景 3:事务方法 A 调用普通函数 C(只有 A 有@Transactional)
事务方法 A 调用无事务的普通函数 C 时:
- C 的逻辑属于 A 的事务范围(因为没有开启新事务)。
- 若 C 中抛出异常且被 A 捕获:若 A 不再次抛出异常,事务不会回滚;若 A 重新抛出异常,整个 A 的事务(包括 C 的操作)会回滚。
- 若 C 中抛出异常未被 A 捕获:异常向上传播到 A,A 的事务会回滚(包括 C 的操作)。
捕获异常没有抛出不回滚,抛出了就回滚
2195

被折叠的 条评论
为什么被折叠?



