通过spring控制事务——使用TransactionSynchronizationManager
在开发spring应用时,有时我们需要根据业务需要控制事务,以此去满足特定业务。比如创建或更新某个数据后,然后启动一个同步任务执行关联的处理操作等等。这时如果将这些逻辑写在一个事务中时,事务还没提交,数据库里找不到对应数据,也就无法启动对应处理任务了。
有没有办法在确保事务提交后,再去发送这个消息呢?一般有以下几个方式:
1、把启动任务的代码写到事务外面;
2、编程式事务;
3、使用TransactionSynchronizationManager控制事务来处理启动任务的时机,这种方式还分为实现TransactionSynchronization接口或是继承TransactionSynchronizationAdapter类两种方式处理。
1、使用方式介绍
前两种比较好想到如何实现,本文主要介绍第三种方式。下面给出一段伪代码样例:
@Transactional(rollbackFor = RuntimeException.class)
public List<Integer> addExtractJob() {
List<TableExtractJob> triggerJobs =Lists.newArrayListWithExpectedSize(16);
List<Integer> res = Lists.newArrayListWithExpectedSize(16);
Long userId = 1L;
//database operate code part
if (CollectionUtils.isNotEmpty(triggerJobs)) {
TransactionSynchronizationManager.registerSynchronization(new TxTriggerJob(triggerJobs, userId));
}
return res;
}
private class TxTriggerJob implements TransactionSynchronization {
private final List<TableExtractJob> triggerJobs;
private final Long userId;
public TxTriggerJob(List<TableExtractJob> triggerJobs, Long userId) {
this.triggerJobs = triggerJobs;
this.userId = userId;
}
@Override
public void afterCommit() {
//do something
//比如启动执行任务等
}
}
以上就是使用TransactionSynchronizationManager和TransactionSynchronization实现在事务提交后执行特定逻辑的方式,还是较简单的。
这里有个细节需要考虑,本例子中是使用是afterCommit,即数据库操作提交后,就会执行启动任务。这块的处理可以看自己的需求,afterCommit只是事务提交了,但是提交的结果还没有得到,比较保险的是用afterCompletion,可以根据事务提交的结果来执行自己的逻辑。
还有一种继承TransactionSynchronizationAdapter类的方式,也比较优雅可以实现,参考地址。
2、实现原理
咱们从TransactionSynchronizationManager开始看。
首先是接收待注册的TransactionSynchronization对象,注册到当前TransactionSynchronizationManager的synchronizations属性ThreadLocal里。
然后是使用,这里可以看到TransactionSynchronizationUtils调用了这个方法。
最终是事务管理器调用了这个方法进行处理的。即AbstractPlatformTransactionManager类的processCommit方法。