当前位置: 首页 > news >正文

事务与异步方法(@Async)协同工作

目录

1. 问题场景与风险

(1)典型场景

(2)风险分析

2. 解决方案:事务提交后触发异步操作

(1)代码示例

(2)关键注解

3. 原理解析

(1)事务同步机制

(2)执行流程

4. 优化方案:@TransactionalEventListener

(1)定义事件

(2)发布事件

(3)监听事件

(4)优势

5. 注意事项

(1)事务传播与隔离

(2)异常处理

(3)线程上下文

6. 总结


当异步方法(@Async)在事务(@Transactional)上下文中被调用时,事务的提交与异步方法的执行顺序会直接影响数据一致性。

异步方法需要在事务提交后执行。

1. 问题场景与风险

(1)典型场景
  • 主方法:包含数据库操作,使用 @Transactional 管理事务。
  • 异步方法:在事务提交后执行,如发送通知、更新缓存等。
@Transactional
public void mainMethod() {
    //1. 数据库操作(如插入数据)
    repository.save(entity);
    //2. 调用异步方法
    asyncService.sendNotification(entity.getId());//可能未提交事务!
}
(2)风险分析
  • 异步方法在事务提交前执行:由于事务提交发生在主方法返回后,异步方法可能读取到未提交的数据,导致脏读或操作失败。
  • 数据不一致:若事务回滚,异步方法已执行的操作无法自动回滚。

2. 解决方案:事务提交后触发异步操作

通过 TransactionSynchronizationManager 注册事务同步回调,确保异步方法在事务提交后执行。

(1)代码示例
@Transactional
public void mainMethod() {
    // 1. 数据库操作
    FileInfo fileInfo = fileRepository.save(new FileInfo());
    // 2. 注册事务提交后的回调
    TransactionSynchronizationManager.registerSynchronization(
        new TransactionSynchronization() {
            @Override
            public void afterCommit() {
                //事务提交后执行异步方法
                fileInfoService.transferFileAsync(fileInfo.getFileId(), webUserDto);
            }
        }
    );
}
(2)关键注解
  • @Async 方法需独立于事务:确保异步方法本身不参与当前事务。
  • 避免事务传播:异步方法的事务传播级别应为 REQUIRES_NEW(若需独立事务)。

3. 原理解析

(1)事务同步机制
  • TransactionSynchronization:Spring 提供的事务同步接口,允许在事务的关键阶段(如提交、回滚)插入自定义逻辑。
  • afterCommit:事务成功提交后触发,此时数据已持久化到数据库。
(2)执行流程
  1. 主方法执行:数据库操作进入事务,但未提交。
  2. 注册回调:通过 registerSynchronization 注册 afterCommit 钩子。
  3. 事务提交:主方法退出,事务提交。
  4. 触发异步方法afterCommit 中调用异步方法,确保数据可见性。

4. 优化方案:@TransactionalEventListener

Spring 4.2+ 提供了更简洁的事务事件监听机制,替代手动注册 TransactionSynchronization

(1)定义事件
public class FileTransferEvent {
    private String fileId;
    private WebUserDto webUserDto;

    // 构造方法、Getter/Setter
}
(2)发布事件
@Transactional
public void mainMethod() {
    FileInfo fileInfo = fileRepository.save(new FileInfo());

    // 发布事件(事务提交后触发)
    applicationEventPublisher.publishEvent(
        new FileTransferEvent(fileInfo.getFileId(), webUserDto)
    );
}
(3)监听事件
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleFileTransferEvent(FileTransferEvent event) {
    fileInfoService.transferFile(event.getFileId(), event.getWebUserDto());
}
(4)优势
  • 解耦:将事务提交后的逻辑与主方法分离。
  • 简化代码:无需手动管理事务同步器。
  • 灵活触发:支持按事务阶段(提交、回滚等)监听事件。

5. 注意事项

(1)事务传播与隔离
  • 异步方法的事务:若异步方法需要操作数据库,应使用 @Transactional(propagation = Propagation.REQUIRES_NEW) 启动新事务。
  • 避免长事务:异步方法不应阻塞,以免影响主线程性能。
(2)异常处理
  • 事务回滚:若主方法事务回滚,afterCommit@TransactionalEventListener 不会触发。
  • 异步方法异常:通过 AsyncUncaughtExceptionHandler 捕获异步方法中的异常。
(3)线程上下文

ThreadLocal 数据:事务上下文(如 SecurityContext)默认不传递到异步线程,需手动传递:

SecurityContext context = SecurityContextHolder.getContext();
CompletableFuture.runAsync(() -> {
    SecurityContextHolder.setContext(context);
    // 执行业务逻辑
});

6. 总结

  • 核心方案:通过事务同步器(TransactionSynchronization)或 @TransactionalEventListener 确保异步方法在事务提交后执行。
  • 最佳实践
    1. 使用 @TransactionalEventListener(phase = AFTER_COMMIT) 简化代码。
    2. 异步方法避免依赖未提交事务的数据。
    3. 合理配置事务传播和线程上下文。

相关文章:

  • python中print函数的flush如何使用
  • 什么是状态管理?有何种方式可以实现?它们之间有什么区别?
  • ChatGPT-4
  • MyBatis操纵数据库-XML实现(补充)
  • C语言指针与数组深度解析
  • 【ASMbits--常用算术运算指令】
  • 《从零手写Linux Shell:详解进程控制、环境变量与内建命令实现 --- 持续更新》
  • 掌握Windows命令提示符中的万能符:*号的全方位使用指南
  • 2.2[frontEnd]ESLint
  • 语音识别 FireRedASR-AED模型主要特点
  • Gitee重新远程连接仓库(Linux)
  • Spring boot+mybatis的批量删除
  • 模运算的艺术:从基础到高阶的算法竞赛应用
  • AI大白话(一):5分钟了解AI到底是什么?
  • 知识图谱流程说明
  • 开源通义万相本地部署方案,文生视频、图生视频、视频生成大模型,支持消费级显卡!
  • DAY34 贪心算法Ⅲ
  • MinIo前后端实现
  • 深度解析React Native底层核心架构
  • Axure高级功能深度解析一一高效原型设计的利器
  • 【社论】人工智能,年轻的事业
  • 金融街:去年净亏损约110亿元,今年努力实现经营稳健和财务安全
  • BNEF:亚洲、中东和非洲是电力基础设施投资的最大机会所在
  • 现场|西岸美术馆与蓬皮杜启动新五年合作,新展今开幕
  • 人社部:就业政策储备充足,将会根据形势变化及时推出
  • 清华姚班,正走出一支军团