最近遇到的一个场景,在一个被 @Transactional 注解的方法A中中调用了一个被 @Async 注解标记的方法B,由于方法B 在执行时方法A 的事务没有提交,但是方法B在执行过程中获取不到方法A中尚未提交的数据,从而最终倒是方法B执行异常。
@Transactional public void create(User user){ // 如果用户已存在,则先删除 delete(user.id); // 创建用户 int userId = insert(user); // 更新用户信息 update(userId); } @Async public void update(Integer userId){ Icon icon = getUserIcon(userId); // 更新用户图片 updateUserPohot(userId,icon); }
像上面的代码,我为创建用户的方法上标记了@Transactional事务注解,然后在其中调用了update()更新方法,这个方法上标记了@Async 注解。这样代码虽然看起来没有什么问题,但是实际在执行update()方法时,由于是其他线程去执行的,就会导致有可能 create()方法对应的事务还没有提交,update() 方法就无法读取到新插入的 user 记录,从而导致更新失败。
通过调整逻辑保证事务在调用异步方法前被提交
这个问题的原因是由于 @Transactional 和 @Async 注解一起使用导致的,那么我们可以从这个方向入手,首先我们可以先确认将create()方法的事务提交后,然后再去执行异步更新方法:
public void create(User user){ int userId = doCreate(user); // 更新用户信息 update(userId); } @Transactional public void doCreate(User user){ // 如果用户已存在,则先删除 delete(user.id); // 创建用户 return insert(user); } @Async public void update(Integer userId){ Icon icon = getUserIcon(userId); // 更新用户图片 updateUserPohot(userId,icon); }
异步方法放在事务方法外调用,这样异步方法就能够读取到已经提交的事务数据了。
我们可以使用TransactionTemplate替换@Transactional注解
@Autowired TransactionTemplate transactionTemplate; public void create(User user){ int userId = transactionTemplate.execute(status->{ // 如果用户已存在,则先删除 delete(user.id); // 创建用户 return insert(user); }); // 更新用户信息 update(userId); } @Async public void update(Integer userId){ Icon icon = getUserIcon(userId); // 更新用户图片 updateUserPohot(userId,icon); }
通过使用TransactionTemplate来细分事务粒度,确保在调用异步方法之前已经提交了事务。
上面的方案基本都能 解决问题,下面是从网上找到的,spring 给出的解决方案:
@Transactional public void create(User user){ // 如果用户已存在,则先删除 delete(user.id); // 创建用户 int userId = insert(user); TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public void afterCommit() { // 更新用户信息 update(userId); } }); } @Async public void update(Integer userId){ Icon icon = getUserIcon(userId); // 更新用户图片 updateUserPohot(userId,icon); }
通过将异步方法注册为事务提交后的操作,这样Spring可以自动帮我们在事务提交后执行对应的操作。
以上是SpringBoot异步与事务一起使用的问题怎么解决的详细内容。更多信息请关注PHP中文网其他相关文章!