java exception 二次抛出_Spring 双层事务,我抛出的异常去哪了?

系统 A 调用系统 B 执行数据同步,系统 B 返回了错误提示,系统 A 需要将前边保存的回滚掉,同时把错误信息向上抛。

大致代码如下

@Service("noteService")

public class NoteServiceImpl implements NoteService {

@Resource

private SearchService searchService;

@Transactional(rollbackFor = Throwable.class)

@Override

public CommonResponse 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 代替。

近期热文推荐:

觉得不错,别忘了随手点赞+转发哦!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值