Spring Boot/JPA中@Async方法能否看到@Transactional线程更新的数据?
问题解答
1. Async方法能否看到更新后的数据?
不能。原因如下:
- Spring的
@Transactional默认会在方法执行完毕后才提交事务,当你在updateContactAndFetchProjects里调用asyncProjectFetcher.fetchContactProjects(contact)时,主线程的事务还处于未提交状态。 - 数据库默认的事务隔离级别(如
READ COMMITTED)下,未提交的数据变更对其他线程是不可见的。Async方法运行在独立线程中,自然无法读取到主线程未提交的更新。
即使你调用了CompletableFuture.allOf(asyncProjectFetcher).join()等待Async方法完成,主线程的事务依然要等到整个updateContactAndFetchProjects方法执行完才会提交,所以Async线程还是看不到更新。
2. 能否让Async方法与调用线程处于同一事务?
不行。Spring的事务管理是基于ThreadLocal实现的,事务上下文和当前线程绑定。Async方法会被Spring调度到新的线程执行,新线程无法继承原线程的事务上下文,因此无法共享同一个事务。
可行的解决方案
要让Async方法能读取到更新后的数据,有两种常用思路:
思路一:拆分方法,提前提交事务
把数据更新操作抽成独立的@Transactional方法,确保更新完成后事务立即提交,再调用Async方法。示例代码如下:
@Service public class ContactService { // 单独的更新方法,事务在方法结束后提交 @Transactional public void updateContact(ContactDTO contact) { contactRepository.updateContact(contact); } // 该方法不需要加@Transactional,因为更新操作已经在独立事务中完成 public ContactDetailsDTO updateContactAndFetchProjects(ContactDTO contact) { // 先执行更新,事务提交 updateContact(contact); // 再调用Async方法,此时数据已提交,能读取到更新后的数据 CompletableFuture<ContactDetailsDTO> future = asyncProjectFetcher.fetchContactProjects(contact); CompletableFuture.allOf(future).join(); // 后续逻辑 return contactDetailsDTO; } }
思路二:使用编程式事务手动提交
如果不想拆分方法,可以用编程式事务控制提交时机,在调用Async方法前手动提交事务:
@Service public class ContactService { @Autowired private PlatformTransactionManager transactionManager; public ContactDetailsDTO updateContactAndFetchProjects(ContactDTO contact) { DefaultTransactionDefinition def = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(def); try { contactRepository.updateContact(contact); // 手动提交事务 transactionManager.commit(status); // 提交后调用Async方法 CompletableFuture<ContactDetailsDTO> future = asyncProjectFetcher.fetchContactProjects(contact); CompletableFuture.allOf(future).join(); return contactDetailsDTO; } catch (Exception e) { transactionManager.rollback(status); throw e; } } }
注意:这种方式会让整个方法脱离声明式事务的管理,需要自己处理事务的提交和回滚。
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

