背景
- 业务要求即使内层代码有报错,也要执行外层代码,保存数据,代码案例如下所示。
@Override
@Transactional(rollbackFor = Exception.class)
public void testInsert() {
Users entity = new Users();
entity.setName("张三啊");
usersMapper.insert(entity);
System.out.println(entity.getId());
try {
testExceptionService.test();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
@Transactional(rollbackFor = Exception.class)
public void test() {
Users entity = new Users();
entity.setName("李四啊");
usersMapper.insert(entity);
System.out.println(entity.getId());
if (true){
throw new RuntimeException("测试事务回滚");
}
}
- 按照我们主观认知,
try catch
捕获异常后,事务不会回滚。 - 我们来执行一下。
- 此时
testExceptionService.test()
报错,就会抛出Transaction rolled back because it has been marked as rollback-only
,外层的代码也会回滚。如下图所示。

- 同样的数据全回滚了,张三啊,李四啊都不会保存,如下图所示。

问题分析
- 首先
@Transactional
的属性propagation
是默认的Propagation.REQUIRED
。 - 此时外层有事务,那么
testExceptionService.test()
是不会新开事务的。 - 也就是说用的是
同一个事务
,肯定有一些事务状态(变量)是共用的,那么testExceptionService.test()
报错,要回滚,那么就都要回滚。捕获
是无效的。
解决方案
- 问题的关键点就是
同一个事务
。开启另一个事务就可以了,像本例的背景就可以这样操作。使用 Propagation.REQUIRES_NEW
。如下代码所示。
@Override
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
public void test() {
Users entity = new Users();
entity.setName("李四啊");
usersMapper.insert(entity);
System.out.println(entity.getId());
if (true){
throw new RuntimeException("测试事务回滚");
}
}
- 也可以在controller层调用2个方法,分层2个事务执行。
- 注意:2个事务中,一个事务对数据修改,另一个事务内不可见。如果有依赖另一个事务的数据要处理下。
- 再次测试,外层代码操作保存了。结果如下所示。

案例代码
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~