@Async与@Transactional使用时的注意事项

68 阅读1分钟

一、最关键的一句话

@Async 会开新线程执行,而事务 @Transactional 只在当前线程里生效。
所以它们放在一起时,事务通常会失效。


二、错误写法(❌)

@Async
@Transactional
public void asyncSave() {
    // 这里事务不会生效!
    userMapper.insert(...);
}

原因:
@Async 开了新线程,@Transactional 绑定的事务上下文丢失。


三、正确写法(✅)

✅ 写法 1:异步里调用另一个带事务的方法

@Service
public class AsyncService {
    @Autowired
    private TransactionService transactionService;

    @Async
    public void doAsync() {
        // 这里异步执行,但事务在另一个service里生效
        transactionService.saveData();
    }
}

@Service
public class TransactionService {
    @Transactional
    public void saveData() {
        // 事务生效
        userMapper.insert(...);
    }
}

✅ 写法 2:主事务提交后再异步执行

如果异步任务只是想在主事务提交后执行(比如发消息、发邮件),
可以用事件监听方式:

@Component
public class OrderListener {

    @Async
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void handleOrderCreated(OrderCreatedEvent event) {
        // 事务提交后再异步执行
        sendEmail(event.getOrderId());
    }
}

四、总结

场景正确做法
想异步+事务把事务放到另一个 Service 方法里
想在事务提交后异步干活@TransactionalEventListener
同类里调用异步方法分成两个类,或者注入自己代理对象