使用事务的几种方式
- XML定义
- 使用
@Transactional注解,默认仅回滚RuntimeException和Error - 使用
TransactionTemplate手动执行事务 - 使用
PlatformTransactionManager手动管理事务
嵌套事务的回滚处理机制
方法解释
transactionExecutor.execute:以Propagation.REQUIRED的形式开始事务,抛出任意异常时回滚
case分析
被嵌套的事务没有异常处理,此时事务正常处理,插入ab的操作都会回滚,也是大多数的情况,此时没有问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @GetMapping("/test") public void test() { transactionExecutor.execute(() -> {
repo.insert(a);
transactionExecutor.execute(() -> { repo.insert(b); throw new IllegalStateException(); });
repo.insert(c);
}); }
|
下面是被嵌套的事务被try包住,此时插入c时报错(即使没有c操作,在外部事务提交时也一样会报错),因为此时事务已处于回滚状态,所以插入a的操作也被回滚了。
看起来是合理的,但如果在插入c前还有其他逻辑,可能就会有问题,并且既然已经使用try包住,那么我们一定是希望try内部发生异常应该不影响try外的逻辑,但目前是影响了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @GetMapping("/test") public void test() { transactionExecutor.execute(() -> {
repo.insert(a);
try { transactionExecutor.execute(() -> { repo.insert(b); throw new IllegalStateException(); }); } catch (Exception e) { }
repo.insert(c);
}); }
|
解决
新增一个方法(transactionExecutor.newTransaction),用于开启Propagation.REQUIRES_NEW的事务
使用newTransaction,包住被嵌套的方法,使事务隔离
- 实际上1处的方法可以省略,但被嵌套的可能是我们无法修改的代码,比如别人提供的接口
newTransaction方法写在try外面是一样的,前提是try里没有其他逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| @GetMapping("/test") public void test() { transactionExecutor.execute(() -> {
repo.insert(a);
try { transactionExecutor.newTransaction(() -> { transactionExecutor.execute(() -> { repo.insert(b); throw new IllegalStateException(); }); }) } catch (Exception e) { }
repo.insert(c);
}); }
|
- 此时插入ac和插入b是两个事务,如果再上层的方法又需要为同一个事务,那这种方法就行不通了,需要调整整个代码结构,比如拆成两个方法,具体略