Home  >  Article  >  Java  >  How to solve the problem of using SpringBoot asynchronously with transactions

How to solve the problem of using SpringBoot asynchronously with transactions

WBOY
WBOYforward
2023-05-27 14:37:241199browse

A recent scenario encountered was that a method B marked by an @Async annotation was called in a method A annotated with @Transactional. Since the transaction of method A was not committed when method B was executed, method B was not committed. During the execution process, the unsubmitted data in method A cannot be obtained, so in the end, method B executes abnormally.

@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);
}

Like the above code, I marked the @Transactional transaction annotation on the method of creating the user, and then called the update() update method in it. This method was marked with the @Async annotation. Although the code seems to have no problem, when the update() method is actually executed, because it is executed by other threads, it is possible that the transaction corresponding to the create() method has not been submitted yet, and the update() method cannot read it. The newly inserted user record is obtained, causing the update to fail.

Solution

Adjust the logic to ensure that the transaction is committed before calling the asynchronous method

The reason for this problem is due to the use of @Transactional and @Async annotations together, then We can start from this direction. First, we can confirm that the transaction of the create() method is submitted, and then execute the asynchronous update method:

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);
}

The asynchronous method is called outside the transaction method, so that the asynchronous method The committed transaction data can be read.

We can use TransactionTemplate to replace the @Transactional annotation

@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);
}

By using TransactionTemplate to subdivide the transaction granularity, ensure that the transaction has been committed before calling the asynchronous method.

The above solutions can basically solve the problem. The following is the solution given by spring found on the Internet:

@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);
}

By registering the asynchronous method as an operation after the transaction is submitted, Spring It can automatically help us perform the corresponding operations after the transaction is submitted.

The above is the detailed content of How to solve the problem of using SpringBoot asynchronously with transactions. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete