同事的问题代码(第五期)

4,275 阅读3分钟

前言

🎉兄弟们新年快乐呀🎉

今天小杨哥👨‍🦲过来说: “为啥我数据同步的时候一会儿正常,一会儿又有问题啊!”

我👦:“打开你的代码呢,你这事务还没提交执行完成,另一个事务就开始查询了都嘛,所以另一个事务查询的数据可能是 第一个事务提交前,或者是另一个事务提交后 都有可能”

本篇文章只有一个案例:分享了在事务中,开启新线程去处理任务引发的问题

问题代码

批量异步执行

业务逻辑: 我们的账号管理中心,需要对账号进行批量修改。修改完成之后需要将账号同步到各子系统。

实现方式: 因为同步逻辑有现成的方法,只是单个同步,现在因为要批量操作,因此调用同步逻辑就是开启新的线程,然后循环调用原来的单个同步方法。

问题代码

看一下service的伪代码吧:

@Transactional(rollbackFor = Exception.class)
@Override
public Respoonse<String> updateAccountRoles(BatchUpdateParam param) {
    //校验参数,
    if(!paramCheck(param)){
        return Response.fail("网络繁忙!");
    }
    // 更新账号信息。
    List updateUserList = userService.updateRoles(param);
    if (updateUserList.size() > 0) {
        // 异步通知其他系统更新对应账号信息。
        //❌事务还没提交,以及已经开始异步执行了,异步方法里面可能查询还是更新前的数据。
        userSyncUtil.asyncUserList(updateUserList);
    }
    return Respoonse.ok("批量更新成功。");
}

❌事务还没提交,以及已经开始异步执行了,异步方法里面可能查询还是更新前的数据。

异步发送账号同步通知


public void asyncUserList(List<String> userIds) {
    if (userIds == null || userIds.size() <= 0) {
        return;
    }
    // 自定义线程就不推荐了
    new Thread(() -> {
        userIds.forEach(item -> this.sync(item));
        
    }).start();
}

❌这个创建线程的方式就不推荐了

解决方案

  1. 将异步代码 注册到事务提交后的回调中
@Transactional(rollbackFor = Exception.class)
@Override
public Respoonse<String> updateAccountRoles(BatchUpdateParam param) {
    //校验参数,
    if(!paramCheck(param)){
        return Response.fail("网络繁忙!");
    }
    // 更新账号信息。
    List updateUserList = userService.updateRoles(param);
    if (updateUserList.size() > 0) {
        // 异步通知其他系统更新对应账号信息。
        // 👍事务提交后执行
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                userSyncUtil.asyncUserList(updateUserList);
            }
        });

        
    }
    return Respoonse.ok("批量更新成功。");
}
  1. 控制事务范围
//❌删除外层事务
//@Transactional(rollbackFor = Exception.class)
@Override
public Respoonse<String> updateAccountRoles(BatchUpdateParam param) {
    //校验参数,
    if(!paramCheck(param)){
        return Response.fail("网络繁忙!");
    }
    // 更新账号信息。👍 updateRoles 加事务注解就行了(或者用编程式事务)
    List updateUserList = userService.updateRoles(param);
    if (updateUserList.size() > 0) {
        // 异步通知其他系统更新对应账号信息。
        userSyncUtil.asyncUserList(updateUserList);
    }
    return Respoonse.ok("批量更新成功。");
}

总结

本篇文章只有一个案例,分享了在事务中,开启新线程去处理任务引发的问题。有时候这种问题可能在测试环节还不一定能复现,所以大家在开发中遇到这种问题,还不一定能快速定位到问题。

本篇文章虽然内容不多,但确实是开发过程中需要引起重视的。

ps 往期文章:
同事的代码问题(java) 同事的代码问题(第二期) 同事的代码问题(第三期)
同事的代码问题(第四期)