一、最关键的一句话
@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 |
| 同类里调用异步方法 | 分成两个类,或者注入自己代理对象 |