今天同事遇到了,因事务方法中,调用了异步线程,导致数据未查询到(主订单的包装信息,该主订单包含了两个子订单信息)。
解决思路:手动提交事务后,再调用异步线程。
看一下伪代码
@Autowired
@Qualifier(value = "applicationThreadPoolTaskExecutor")
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Autowired
private PackageInfoMapper packageInfoMapper;
@Autowired
private OrderMapper orderMapper;
@Transactional(rollbackFor = Exception.class)
public void saveOrderPackageInfo(PackageInfo packageInfo){
//保存包装信息
packageInfoMapper.save(packageInfo);
//修改订单状态
orderMapper.update(packageInfo);
//异步把包装信息发送至第三方
threadPoolTaskExecutor.submit(() -> {
//查询id为1的数据
List<OrderPackageInfo> 0rderPackageInfo= packageInfoMapper.select(packageInfo.getOrderNo);
// todo把数据发送发给第三方
});
}
第一种方案:
手动提交事务后,在调用异步线程。
@Autowired
@Qualifier(value = "applicationThreadPoolTaskExecutor")
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Autowired
private PackageInfoMapper packageInfoMapper;
@Autowired
private OrderMapper orderMapper;
@Autowired
private TransactionDefinition transactionDefinition;
public void saveOrderPackageInfo(PackageInfo packageInfo){
TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition);
try {
//保存包装信息
packageInfoMapper.save(packageInfo);
//修改订单状态
orderMapper.update(packageInfo);
platformTransactionManager.commit(transaction);
System.out.println("提交成功。。。");
//异步把包装信息发送至第三方
threadPoolTaskExecutor.submit(() -> {
//查询id为1的数据
List<OrderPackageInfo> 0rderPackageInfo= packageInfoMapper.select(packageInfo.getOrderNo);
// todo把数据发送发给第三方
});
} catch (Exception e) {
System.out.println("进入了异常。。。");
platformTransactionManager.rollback(transaction);
}
}
第二种方案:
用Spring提供的AbstractPlatformTransactionManager的api
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);
}
}
}
// TransactionSynchronizationAdapter是TransactionSynchronization的默认实现
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
// 事务提交后需要执行的业务逻辑
}
});
更改后的代码
@Autowired
@Qualifier(value = "applicationThreadPoolTaskExecutor")
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Autowired
private PackageInfoMapper packageInfoMapper;
@Autowired
private OrderMapper orderMapper;
@Transactional(rollbackFor = Exception.class)
public void saveOrderPackageInfo(PackageInfo packageInfo){
//保存包装信息
packageInfoMapper.save(packageInfo);
//修改订单状态
orderMapper.update(packageInfo);
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
// 事务提交后需要执行的业务逻辑
//异步把包装信息发送至第三方
threadPoolTaskExecutor.submit(() -> {
//查询id为1的数据
List<OrderPackageInfo> 0rderPackageInfo= packageInfoMapper.select(packageInfo.getOrderNo);
// todo把数据发送发给第三方
});
}
});
}
注意:此方法会在事务提交后执行afterCommit 的代码块。
我推荐第二种,代码简化
PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。点“在看”支持我吧!