-
背景
-
解决方式
-
spring事务提交扩展点
-
1. 直接利用spring提供的注册api
-
发送事务提交完成事件
-
总结
-
参考
背景
有时候我们会有这样的业务场景,比如在同一个方法中,想前面的事务执行完,再执行后面的方法。语言描述比较抽象,我们直接看代码
@Autowired
@Qualifier(value = "applicationThreadPoolTaskExecutor")
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Transactional(rollbackFor = Exception.class)
public void savePackageConfig(PackageConfigDTO dto){
// 数据库操作 伪代码 保存id为1的数据
dao.save(dto);
dao.update(dto);
// 由于下面的业务耗时很长,所以使用异步
threadPoolTaskExecutor.submit(() -> {
//查询id为1的数据
PackageConfigDO configDO = dao.select(id);
// 使用configDO执行业务操作
});
}
上面的代码逻辑解释也很清晰,就是在执行完保存数据后,会异步再去查询前面保存的数据,可能会导致在方法事务还没有提交的时候异步线程去查询,由于使用的是线程池,所以不算同一个事务session,事务的隔离级别为RC,异步线程查询不到未提交的事务而获取不到数据configDO而报错。
解决方式
spring事务提交扩展点
我们先来简单从源码看看spring事务提交留下的扩展点 我们看看AbstractPlatformTransactionManager
的cmom
public final void commit(TransactionStatus status) throws TransactionException {
// ... 省略前面代码
processCommit(defStatus);
}
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
// 事务提交
// ... 代码省略
// 事务提交后扩展点核心方法 triggerAfterCompletion(status,TransactionSynchronization.STATUS_ROLLED_BACK);
}
private void triggerAfterCompletion(DefaultTransactionStatus status, int completionStatus) {
if (status.isNewSynchronization()) {
// 这里可以看到在事务提交后会执行 TransactionSynchronization接口的代码,所以只需要注册一个接口到List<TransactionSynchronization>中
List<TransactionSynchronization> synchronizations = TransactionSynchronizationManager.getSynchronizations();
TransactionSynchronizationManager.clearSynchronization();
if (!status.hasTransaction() || status.isNewTransaction()) {
if (status.isDebug()) {
logger.trace("Triggering afterCompletion synchronization");
}
// No transaction or new transaction for the current scope ->
// invoke the afterCompletion callbacks immediately
invokeAfterCompletion(synchronizations, completionStatus);
}
else if (!synchronizations.isEmpty()) {
// Existing transaction that we participate in, controlled outside
// of the scope of this Spring transaction manager -> try to register
// an afterCompletion callback with the existing (JTA) transaction.
registerAfterCompletionWithExistingTransaction(status.getTransaction(), synchronizations);
}
}
}
通过源码我们发现了解决方式,即可通过注册接口到List<TransactionSynchronization>
中 说到底就是利用Spring留下的扩展接口afterCommit
1. 直接利用spring提供的注册api
// TransactionSynchronizationAdapter是TransactionSynchronization的默认实现
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
// 事务提交后需要执行的业务逻辑
}
});
上面的代码就改成这样的
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
// 事务提交后需要执行的业务逻辑: 发消息, 日志...
threadPoolTaskExecutor.submit(() -> {
// 业务操作
}});
}
});
发送事务提交完成事件
这里可以利用spring提供的ApplicationEventPublisher
去发布一个事务完成事件,供其他方法订阅。
这种方式感觉相对麻烦,不推荐使用
使用方式和Guava
观察者模式有些类似
@Autowired
private ApplicationEventPublisher publisher;
@Override
@Transactional(rollbackFor = Exception.class)
public void add(AdvanceChargeApplyAddInput input) {
this.save(advanceChargeApply);
// 发送事件
publisher.publishEvent(advanceChargeApply);
}
// 响应事件, 事务提交后执行
@TransactionalEventListener
public void handle(PayloadApplicationEvent<AdvanceChargeApply> event) {
System.out.println("TransactionalEventListener 事务提交后执行");
}
总结
Spring
事务使用起来给我的感觉还是挺复杂的,使用起来使用看似给我们都是封装和好,实际有很多坑需要处理,最常见的就是
@Transactional
public void savePackageConfig(PackageConfigDTO dto){
// crud
throw new Exception();
}
这里的事务会回滚吗?很明显不会,因为Transactional默认只会回滚RuntimeException
和Error
,所以要想使用好事务,还是需要自己对事务有更深入的认识,不然随时都可能掉坑里
参考
简书
原文始发于微信公众号(小奏技术):Spring扩展接口之afterCommit(同一方法中如何设置在事务提交后执行相关代码)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/30431.html