You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring配置Propagation.REQUIRES_NEW的内部事务未持久化问题咨询

问题根因

该现象的核心原因是BarService使用的getSubscriberRepo2持有的EntityManager未绑定到REQUIRES_NEW创建的新事务上下文,完全匹配你给出的日志和代码特征:

  1. 移除Propagation.REQUIRES_NEW时,BarService的事务直接加入外层事务,和FooService共用同一个EntityManager实例,两个Repository的操作都被纳入同一个事务,提交时统一持久化,因此两条记录都能正常入库。
  2. 加上Propagation.REQUIRES_NEW时,Spring会挂起外层事务的EntityManager,新创建一个独立的EntityManager绑定到内层事务上下文,但你的getSubscriberRepo2是手动创建的实例,注入的是固定的外层EntityManager引用,不是Spring代理的、可动态获取当前事务上下文的EntityManager:
    • 调用repo.save(sub)保存bar@bar.com时,操作的是已经被挂起的外层EntityManager,不会被纳入内层新事务,也不会触发主键生成逻辑,所以日志中打印的Bar记录ID为null
    • 内层事务提交时,仅处理新的EntityManager中的持久化操作,而新的EntityManager中没有任何待提交的数据,相当于提交了空事务,没有任何数据写入数据库
    • 外层事务恢复后,你保存在被挂起的EntityManager中的Bar实体,会因为持久化上下文状态问题被丢弃,最终不会被持久化

验证方法

你可以在BarService的repo.save(sub)之后新增代码,对比当前事务绑定的EntityManager和Repository持有的EntityManager是否为同一个实例,即可直接确认问题:

// 类中注入EntityManagerFactory
@PersistenceUnit
private EntityManagerFactory emf;

// save执行后新增日志
EntityManager currentTxEm = emf.createEntityManager().getEntityManagerFactory().getPersistenceUnitUtil().getIdentifier(sub);
// 或者通过反射获取repo持有的EntityManager实例打印对比

如果两个实例ID不一致,即可100%确认是该问题。

解决方案

  1. 放弃手动创建Repository实例的逻辑,所有Repository都交给Spring Data JPA自动扫描管理,通过@Autowired注入,Spring会自动为Repository注入代理的EntityManager,动态获取当前线程绑定的事务上下文实例。
  2. 如果必须手动实现Repository,注入EntityManager时使用@PersistenceContext注解,不要用@Autowired直接注入固定的EntityManager实例,@PersistenceContext默认会注入代理对象,可动态关联当前事务的EntityManager。
  3. 如果是多数据源场景,需要为第二个数据源配置独立的事务管理器,并且在BarService的@Transactional注解中通过transactionManager属性指定对应的事务管理器。
  4. 临时验证可以在BarService的save操作之后手动调用repo.flush(),如果抛出「没有活动事务」相关异常,可直接确认EntityManager未绑定到当前内层事务。

内容的提问来源于stack exchange,提问作者AlexG

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 06:54:04