事务中启用事务问题
开发的时候遇到了一个问题,事务还没提交,开启了异步操作,异步操作里面会读取事务里面修改后的数据问题
问题
如下代码
@Transactional
public void processData(Long dataId) {
saveData(dataId);
afterCommitExecutor.execute(() -> {
fetchDataAfterCommit(dataId);
});
}
fetchDataAfterCommit(Long dataId){
Object data = getData(dataId);
}
如果线程执行的时候,事务还没提交,
此时fetchDataAfterCommit方法里面是获取不到数据的,
此时会抛出空指针异常,解决方法得让事务提交后再开启线程
解决方案
方案一
注册事务同步回调
@Transactional
public void processData(Long dataId) {
saveData(dataId);
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronizationAdapter() { // 使用适配器
@Override
public void afterCommit() {
afterCommitExecutor.execute(() -> {
fetchDataAfterCommit(dataId);
});
}
}
);
}
方案二
使用 @TransactionalEventListener
@Component
public class DataReadyListener {
@Async
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handleDataReadyEvent(DataReadyEvent event) {
// 这里会在主事务提交后异步执行
fetchDataAfterCommit(event.getDataId());
}
private void fetchDataAfterCommit(Long dataId) {
// 获取数据的逻辑
}
}
方案三
手动控制事务提交
@Service
public class DataService {
@Autowired
private PlatformTransactionManager transactionManager;
public void processData() {
TransactionStatus status = transactionManager.getTransaction(
new DefaultTransactionDefinition()
);
try {
// 1. 业务处理和数据保存
transactionManager.commit(status);
// 2. 事务提交后异步执行
CompletableFuture.runAsync(() -> {
fetchDataAfterCommit(dataId);
});
} catch (Exception e) {
transactionManager.rollback(status);
throw e;
}
}
}
方案四
使用消息队列解耦
这种方式也有可能存在事务没有执行完,消息队列先执行的情况,尤其是长事务的情况,使用这种方式一定要到最后才能发送消息队列
@Service
public class DataService {
@Transactional
public void processData() {
// 1. 业务处理和数据保存
// 2. 发送消息到消息队列
messageQueue.send(new DataReadyMessage(dataId));
}
}
@Component
public class DataConsumer {
@RabbitListener(queues = "data.queue")
public void consumeDataMessage(DataReadyMessage message) {
// 消息队列消费时,事务一定已经提交了
fetchDataAfterCommit(message.getDataId());
}
}

浙公网安备 33010602011771号