Spring事务问题:使用@Async与@Transactional时数据变更未持久化
问题分析与解决方案
问题场景
代码结构如下:
class A { @Transactional public void foo() { // 向表中插入数据 async_job(); //.. } } class B{ @Async public void async_job() { bar(); } } class C{ @Transactional public void bar(){ // 更新foo方法中插入的数据行 } }
未使用@Async时运行正常,添加后表中无插入数据。已尝试:
- 为
bar方法设置传播类型为REQUIRED_NEW - 为
foo方法设置隔离级别为SERIALIZABLE——可运行但结果不一致
期望:foo()启动独立线程执行bar(),执行完成后插入和更新的数据能持久化到数据库。
核心原因
foo方法的事务在调用异步任务async_job时尚未提交,异步线程属于独立执行上下文,受数据库默认READ_COMMITTED隔离级别限制,无法读取当前线程未提交的事务数据。如果foo后续逻辑触发事务回滚,或事务延迟提交,就会出现插入数据丢失的情况。
可行解决方案
方案1:拆分插入逻辑为独立事务,提前提交
将foo中的插入逻辑抽离为单独的事务方法,确保插入操作的事务在触发异步任务前完成提交:
class A { @Autowired private DataInsertService dataInsertService; @Autowired private B b; @Transactional public void foo() { // 调用独立事务的插入方法,事务执行完成后立即提交 dataInsertService.insertData(); // 插入事务已提交,再触发异步任务 b.async_job(); //.. } } @Service class DataInsertService { @Transactional public void insertData() { // 向表中插入数据 } }
方案2:注册事务同步回调,等待主事务提交后执行更新
通过TransactionSynchronizationManager注册回调,在foo的主事务提交后再执行bar的更新逻辑,同时需配置异步任务传递事务上下文:
class B{ @Autowired private C c; @Async public void async_job() { // 注册同步回调,主事务提交后执行更新 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { @Override public void afterCommit() { c.bar(); } }); } }
注:需为Spring异步任务的TaskExecutor配置TaskDecorator,确保主事务上下文能传递到异步线程。
方案3:手动提交主事务(不推荐,易引发事务管理混乱)
在foo中调用异步任务前,手动提交当前事务:
class A { @Autowired private PlatformTransactionManager transactionManager; @Autowired private B b; @Transactional public void foo() { // 向表中插入数据 // 手动提交当前事务 TransactionStatus status = TransactionAspectSupport.currentTransactionStatus(); transactionManager.commit(status); // 触发异步任务 b.async_job(); //.. // 注意:后续代码不再处于事务中,如需事务需重新开启 } }
关键注意点
- 禁止在未提交的事务中触发异步任务操作同一份数据,这会直接引发数据可见性问题
REQUIRED_NEW仅能让bar开启独立事务,但无法解决主事务未提交导致的数据不可见问题SERIALIZABLE隔离级别会强制加锁,容易引发死锁或性能瓶颈,无法从根本解决异步与事务的时序冲突
内容的提问来源于stack exchange,提问作者ethereal
相关产品推荐
相关产品推荐

