spring源码学习(四)spring复习之事务
读的问题
脏读
事务A开启事务,事务B开启事务,事务B增加或修改数据,但是未提交,事务A到读取了未提交的数据

不可重复读

演示
事务A先查,得到1000

事务B修改,并提交

事务A再查,得到800

解决方案

- 相当于事务A,select where id=1时,给这行数据加了锁,其他事务无法修改
幻读

- 幻读强调的是insert
演示
事务A统计表总数为1000,事务B新增了100条,并提交事务,事务A又统计,表总数为1100。
解决

隔离级别

数据库的隔离级别

默认隔离级别


传播属性

举例理解
示例一
Service1的save

Service2的save

- 假设Controller层调用Service1的save1的方法,然后调用Service2的save2方法
- Controller层没有@Transactional,所以Service1的save1被调用时,要创建事务,而save2被调用时,要融入进save1的事务
示例二



捕获异常之REQUIRED


- 两个方法都会回滚
捕获异常之REQUIRES_NEW
情况一


- save2会回滚,save1不会回滚
- save2是事务2,save1是事务1,save2内部出现了异常,回滚,而save1捕获了异常,不会回滚
情况二

- save2会回滚,save1会回滚
- save2出异常了,往外抛,save1又没捕获,那save1相当于接住了异常,出现了异常
事务超时

使用场景

事务失效的原因
1. 非 public 方法
现象:private/protected 方法添加 @Transactional 无效
原因:Spring AOP 代理仅拦截 public 方法
2. 同类内部自调用
现象:methodA() 调用同类中的 @Transactional methodB() 事务失效
原因:自调用绕过代理对象
解决:
方案1:拆分到不同类
@Service
public class ServiceB {
@Transactional
public void methodB() { ... }
}
方案2:通过代理对象调用(需开启 exposeProxy)
((ServiceA) AopContext.currentProxy()).methodB();
现象:static/final 方法事务不生效
原因:无法生成代理子类
解决:避免在 final/static 方法上使用 @Transactional。
捕获异常未抛出
现象:try-catch 吞没异常导致不回滚
解决:
@Transactional
public void process() {
try {
dbOperation();
} catch (Exception e) {
//重新抛出或标记回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
throw new RuntimeException(e);
}
}
错误异常类型
现象:抛出 IOException 等检查异常不回滚
原因:默认仅回滚 RuntimeException 和 Error
解决:
@Transactional(rollbackFor = Exception.class) // 指定回滚异常
数据库引擎不支持
现象:MySQL 使用 MyISAM 引擎(不支持事务)
解决:更换为 InnoDB 引擎:
未启用事务管理
现象:缺少 @EnableTransactionManagement
解决:在配置类添加注解启用事务。
多数据源未指定管理器
现象:多数据源场景事务混乱
解决:
@Transactional("orderTransactionManager") //指定事务管理器
public void createOrder() { ... }
浙公网安备 33010602011771号