JPA仓库为何需绑定事务管理器?如何使其跟随@Transactional指定事务?
你遇到的问题核心在于:@EnableJpaRepositories中指定的transactionManagerRef会强制该包下的所有JPA仓库使用这个固定的事务管理器,直接忽略服务层@Transactional指定的事务上下文。Spring Data JPA的仓库默认会在没有外层事务时自动创建事务,但一旦绑定了特定事务管理器,它就会优先使用这个绑定的管理器,不管外层是否存在其他事务。
另外需要明确:JPA仓库的entityManagerFactoryRef和事务管理器是强绑定的——每个EntityManagerFactory对应唯一的事务管理器,用来管理该工厂对应的数据源事务。如果你的仓库绑定了entityManager1,那么只能使用管理entityManager1的事务管理器,不能跨数据源使用transactionManager2(除非transactionManager2本身就是基于entityManager1创建的)。
要让仓库的事务由服务层@Transactional注解决定,可按以下步骤操作:
1. 移除仓库配置中的固定事务管理器绑定
修改@EnableJpaRepositories配置,删除transactionManagerRef属性,让仓库不绑定固定的事务管理器:
@EnableJpaRepositories( basePackages = "com.ieci.mugeju.data.repositories1", entityManagerFactoryRef = "entityManager1" // 移除transactionManagerRef = "transactionManager1" )
这样配置后,仓库会自动继承当前事务上下文的事务管理器——也就是服务层@Transactional指定的那个。
2. 确保事务管理器与仓库的EntityManagerFactory匹配
如果你的transactionManager2是对应另一个数据源的事务管理器(基于另一个EntityManagerFactory创建),直接用它操作绑定entityManager1的仓库会导致EntityManager与事务管理器不匹配,数据仍会插入到原来的数据源。这种情况下:
- 若要操作另一个数据源,需创建对应该数据源的仓库接口,通过单独的
@EnableJpaRepositories配置绑定对应的entityManagerFactoryRef和transactionManagerRef。 - 若只是想让当前仓库的事务由服务层控制,确保
@Transactional指定的事务管理器是管理entityManager1的那个(比如transactionManager1)。
3. 验证服务层事务注解的有效性
确保服务层的@Transactional注解配置正确,且Spring已开启事务管理(项目中存在@EnableTransactionManagement注解):
@Service public class TestDenunciasServiceImpl implements TestDenunciasService { @Autowired private Repository1 repository1; @Override @Transactional(readOnly = false, transactionManager = "transactionManager1") // 此处指定的事务管理器需与repository1的entityManagerFactory对应 public void test() { MyEntity entity = new MyEntity(); // ... 设置实体属性 ... repository1.saveAndFlush(entity); } }
- JPA仓库的EntityManagerFactory和事务管理器必须一一对应,跨数据源操作需要使用对应数据源的专属仓库接口。
- 移除
@EnableJpaRepositories中的transactionManagerRef,就能让仓库继承服务层的事务上下文,由@Transactional指定的事务管理器控制事务。
内容的提问来源于stack exchange,提问作者Eduardo

