面试遇到Spring双层事务不回滚怎么破
最近有粉丝在朋友圈问我,面试遇到了Spring双事务不会滚问题,怎么破解。本文结合一个简单案例,希望能解决一部分人的疑惑。 系统 A 调用系统 B 执行数据同步,系统 B 返回了错误提示,系统 A 需要将前边保存的回滚掉,同时把错误信息向上抛。
大致代码如下
@Service("noteService")
public class NoteServiceImpl implements NoteService {
@Resource
private SearchService searchService;
@Transactional(rollbackFor = Throwable.class)
@Override
public CommonResponse<NoteEntity> save(NoteEntity note) {
// 一系列 DB 操作
try {
searchService.sync(note);
} catch (Exception e) {
e.printStackTrace();
}
return CommonResponse.success(entity);
}
}
@Service("searchService")
public class SearchServiceImpl implements SearchService {
@Transactional(rollbackFor = Throwable.class)
@Override
public void sync(NoteEntity note) {
// 一系列 DB 操作
throw new RuntimeException("同步异常! [XXX]");
}
}
@SpringBootTest
public class NoteTests {
@Resource
private NoteService noteService;
@Test
public void saveNote() {
NoteEntity entity = new NoteEntity();
entity.setTitle("念奴娇赤壁怀古");
entity.setContent("大江东去,浪淘尽,千古风流人物。故垒西边,人道是:三国周郎赤壁。。。");
entity.setTags("苏轼,宋代");
entity.setCategory("苏轼诗词");
try {
noteService.save(entity);
} catch (Exception e) {
e.printStackTrace();
// FIXME 我想在这里拿到的是 同步异常! [XXX]
// FIXME 但是这里拿到的是 Transaction silently rolled back because it has been marked as rollback-only
System.out.println(">>>>>>>>>> " + e.getMessage());
}
}
}
事出有因
代码历史久远,为何这样写已无从追溯。
纳闷了一会儿,看到双层事务,就想起了 Spring事务传播机制,前边理解得比较肤浅。
没有特殊的配置,自然是走默认的事务传播机制了,也就是 Propagation.REQUIRED。
国际惯例,列出事务传播机制:
1、PROPAGATION_REQUIRED
当前没事务,则创建事务;存在事务,就加入该事务,这是最常用的设置。
2、PROPAGATION_SUPPORTS
当前存在事务,就加入事务,当前不存在事务,就以非事务方式执行。
3、PROPAGATION_MANDATORY
当前存在事务,就加入事务;当前不存在事务,就抛出异常。
4、PROPAGATION_REQUIRES_NEW
无条件创建新事务。
5、PROPAGATION_NOT_SUPPORTED
以非事务方式执行,如果当前存在事务,就将当前事务挂起。
6、PROPAGATION_NEVER
以非事务方式运行,如果存在事务,就抛出异常。
7、PROPAGATION_NESTED
开始执行事务前,先保存一个savepoint,当发生异常时,就回滚到savepoint;没有异常时,跟着外部事务一起提交或回滚。
具体原因
1、看了上边的事务传播机制,继续细化问题,内外层共用一个事务,内层抛出异常,会导致整个事务失败。
2、继续分析,外层逻辑进行了 try catch,就导致内层的异常无法继续向上抛出,外层事务会继续提交。
3、事务提交时,进行事务状态的判断,就发现这个事务是失败的,需要回滚,所以抛出了 Transaction silently rolled back because it has been marked as rollback-only
的异常。
怎么解决?
银弹自然是没有的,根据业务场景选择合适的方案。
1、当前这种场景,直接把外层逻辑中的 try catch 去掉即可。异常直接向上抛,事务就不会继续提交,调用方拿到的就是一手的异常;
2、如果内层不是核心逻辑,记录个日志啥的,可以把内层事务配置为 @Transactional(rollbackFor = Throwable.class, propagation = Propagation.REQUIRES_NEW)
, 无论如何,都创建新的事务,外层事务不受内层事务影响。但是有个问题,外层事务失败了,内层事务还是把记录入库了,有可能产生脏数据;
3、如果外层事务失败了,内层事务也不能提交,那就可以使用 @Transactional(rollbackFor = Throwable.class, propagation = Propagation.NESTED)
。注意:hibernate/jpa 不支持嵌套事务 NESTED,可用 JdbcTemplate 代替。
最后,下面这位粉丝总结的事务不生效问题,大家牢记。面试中能全说出来,Offer基本稳了。
相关文章
- spring boot自动配置原理面试题_Spring boot面试
- Spring 全家桶之 Spring Framework 5.3(七)- 声明式事务
- Spring IOC容器的初始化过程
- 细说Spring——AOP详解(AOP概览)
- Github标星35K+超火的Spring Boot实战项目,附超全教程文档
- 【说站】mysql spring事务的特性
- Spring 事务底层原理
- 8-Spring事务控制
- Alibaba最新推出Spring+SpringBoot+SpringCloud全家桶诛仙手册
- Spring boot串口通信,windows、linux,docker环境配置说明
- Spring的事务我是这么玩的
- Spring Security安全框架中BCrypt强哈希加密算法使用
- Spring:声明式事务
- 消息总线(Spring Cloud Bus)
- 18个示例详解 Spring 事务传播机制
- 解析Spring Cloud Sleuth的跟踪数据
- 微服务架构下使用Spring Cloud Zuul作为网关将多个微服务整合到一个Swagger服务上详解架构师
- Spring Cloud Feign的文件上传实现详解编程语言
- spring中jedis对redis的事务使用注意总结详解大数据
- spring 事务传播行为实例分析详解编程语言
- 浅谈Spring中的事务回滚详解编程语言
- spring中bean的scope属性理解详解编程语言
- spring 学习详解编程语言
- Spring Boot 2 (三):Spring Boot 2 相关开源软件详解编程语言
- Spring Boot(十二):spring boot如何测试打包部署详解编程语言
- Spring事务详解编程语言
- Spring batchUpdate方法:执行批量更新语句