SpringBoot中@Transactional与@Async引发事务竞态条件异常求助
问题原因分析
当前异常的核心是异步任务与主事务的执行时序不匹配:
- 主事务中插入新父记录后,事务尚未提交(Spring事务默认在方法结束时才提交);
@Async标记的updateChild在独立线程中执行,由于数据库默认READ COMMITTED隔离级别,异步线程无法看到主事务未提交的新父记录;- 子表更新时找不到对应的父记录,触发外键约束异常。
移除@Async后,所有操作在同一个事务内,同一事务内的变更可见,因此能正常执行,但同步执行导致性能瓶颈。
解决方案
方案1:利用事务同步回调,提交后触发异步更新
通过TransactionSynchronizationManager注册回调,确保异步任务仅在主事务提交完成后执行,此时新父记录已持久化到数据库。
代码修改:
- 在
MainRepositoryClass中注入SecondRepositoryClass,并修改determineChildTables方法:
@Repository public class MainRepositoryClass { @Autowired protected JdbcTemplate jdbcTemplate; @Autowired private SecondRepositoryClass secondRepositoryClass; // 新增注入 // 原updateParentWithCascadetoChildren方法不变 public void determineChildTables(String parentTableName, ParentObj pObjOld, ParentObj pObjNew){ List<Child> childMapping = jdbcTemplate.query(<查询受影响子表的SQL>); for(Child child : childMapping) { // 注册事务提交后的回调 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { @Override public void afterCommit() { // 主事务提交后,触发异步子表更新 secondRepositoryClass.updateChild(child, pObjNew); } }); } } }
- 调整
SecondRepositoryClass的事务注解:
由于异步任务在主事务提交后执行,无需加入主事务,可保留@Async并移除@Transactional(或设置为REQUIRES_NEW保证子更新的原子性):
@Repository @EnableAsync public class SecondRepositoryClass { @Autowired protected JdbcTemplate jdbcTemplate; @Async // @Transactional(propagation = Propagation.REQUIRES_NEW) // 可选:保证子更新独立事务 public void updateChild(Child child, ParentObj pObjNew){ jdbcTemplate.update(<更新子表的SQL>); } }
方案2:基于事务感知事件触发异步更新
利用Spring的TransactionAwareApplicationEventPublisher发布事件,确保事件在主事务提交后被异步监听处理,实现解耦。
代码修改:
- 定义子表更新事件类:
public class ChildUpdateEvent extends ApplicationEvent { private final Child child; private final ParentObj pObjNew; public ChildUpdateEvent(Object source, Child child, ParentObj pObjNew) { super(source); this.child = child; this.pObjNew = pObjNew; } // Getter方法 public Child getChild() { return child; } public ParentObj getPObjNew() { return pObjNew; } }
- 在
MainRepositoryClass中注入事务感知事件发布器,修改determineChildTables:
@Repository public class MainRepositoryClass { @Autowired protected JdbcTemplate jdbcTemplate; @Autowired private TransactionAwareApplicationEventPublisher eventPublisher; // 新增注入 // 原updateParentWithCascadetoChildren方法不变 public void determineChildTables(String parentTableName, ParentObj pObjOld, ParentObj pObjNew){ List<Child> childMapping = jdbcTemplate.query(<查询受影响子表的SQL>); for(Child child : childMapping) { // 发布事务感知事件,主事务提交后才会触发监听 eventPublisher.publishEvent(new ChildUpdateEvent(this, child, pObjNew)); } } }
- 编写异步事件监听器(替代原
SecondRepositoryClass的updateChild方法):
@Component public class ChildUpdateListener { @Autowired private JdbcTemplate jdbcTemplate; @Async @EventListener public void handleChildUpdate(ChildUpdateEvent event) { Child child = event.getChild(); ParentObj pObjNew = event.getPObjNew(); jdbcTemplate.update(<更新子表的SQL>); } }
注意事项
- 确保项目中已启用异步支持(通过
@EnableAsync注解,可放在启动类或配置类上); - 若异步任务需要事务保证,可在异步方法上添加
@Transactional(propagation = Propagation.REQUIRES_NEW),避免与其他事务冲突; - 避免在事务同步回调中执行过重逻辑,防止阻塞主事务的提交流程。
内容的提问来源于stack exchange,提问作者Timothy Clotworthy
相关产品推荐
相关产品推荐

