Spring JPA @Transactional事务场景下新增实例查询不到问题求助
问题成因
- 事务未提交导致数据不可见:
method_m1标注的@Transactional会开启一个新事务,method_m2使用Propagation.REQUIRED传播级别会直接加入该事务,你在method_m2中执行save或saveAndFlush操作,仅会将实体存入JPA持久化上下文、将对应的插入SQL同步到数据库执行,但整个事务要等到method_m1完全执行结束才会正式提交,未提交的变更对其他事务完全不可见。这也是你尝试saveAndFlush仍然无效的核心原因——flush不等于commit,只是把变更同步到数据库的事务中,没有提交前外部还是读不到。 - 异步逻辑脱离Spring事务管控:
audit方法上标注的@Transactional(propagation = Propagation.REQUIRES_NEW)只会作用于同步调用的方法逻辑,你在方法内部通过CompletableFuture.runAsync提交的异步任务运行在独立线程中,完全脱离Spring AOP的代理范围,该事务注解不会对异步代码块生效。即使你将事务配置作用到异步线程,新开的事务也受数据库默认隔离级别(通常为READ_COMMITTED)限制,无法读取到method_m1未提交的用户数据。
解决方案
可根据业务场景选择适配方案:
- 若审计不需要异步执行:直接去掉
CompletableFuture.runAsync异步包装,让审计逻辑运行在method_m1的现有事务中,可直接读取到JPA上下文中的用户实体,不需要查询数据库,也不会出现找不到用户的问题。 - 若必须保留异步审计逻辑:通过Spring事务同步机制,在
method_m1的事务提交完成后再触发异步审计任务,示例代码如下:
public void audit(Result result){ // 校验当前是否存在活动事务 if (TransactionSynchronizationManager.isActualTransactionActive()) { // 注册事务回调,在事务提交完成后执行逻辑 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { @Override public void afterCommit() { CompletableFuture.runAsync(() -> { User user = userRepository.findById(result.get().getId()) .orElseThrow(() -> new UserNotFoundException("user not found, userId :" + result.get().getId())); // 其余审计逻辑 }); } }); } }
- 不推荐方案:调整数据库隔离级别为
READ_UNCOMMITTED允许脏读,会引发大量数据一致性问题,仅可用于极端非核心业务场景。
内容的提问来源于stack exchange,提问作者Shekhar Khairnar
相关产品推荐
相关产品推荐

