Spring Data多数据源自定义SimpleJpaRepository报TransactionManager冲突错误
问题根源
你继承的SimpleJpaRepository内置的CRUD方法(比如你调用的save)已经标注了不带transactionManager属性的@Transactional注解,Spring事务注解的优先级为方法级注解 > 类级注解,你在子类ProductCustomRepository上添加的带事务管理器的类级@Transactional,不会覆盖父类已声明的方法级@Transactional的属性。当调用父类继承来的save方法时,Spring会查找默认的TransactionManager,你项目中存在两个事务管理器且未指定主事务管理器,因此抛出匹配冲突错误。
解决方案
方案1:重写父类事务方法(推荐,无全局影响)
在自定义DAO类中重写所有你会用到的父类CRUD方法,方法上添加@Transactional注解,即可继承类上声明的事务管理器:
@Repository @Transactional(transactionManager = "db2TransactionManager") public class ProductCustomRepository extends SimpleJpaRepository<ProductEntity2, Integer> { private final EntityManager entityManager; public ProductCustomRepository(@Qualifier("db2EntityManagerFactory") EntityManager entityManager) { super(ProductEntity2.class, entityManager); this.entityManager = entityManager; } // 重写save方法,继承类级事务管理器配置 @Override @Transactional public <S extends ProductEntity2> S save(S entity) { return super.save(entity); } // 按需重写其他用到的父类方法,比如saveAll、deleteById等 @Override @Transactional public void deleteById(Integer id) { super.deleteById(id); } public List<ProductEntity2> customQuery() { var query = entityManager.createQuery("SELECT p FROM ProductEntity2 p WHERE p.name='special'",ProductEntity2.class); return query.getResultList(); } }
方案2:指定主事务管理器(适合单数据源为主、多数据源为辅的场景)
给常用数据源的事务管理器添加@Primary注解,作为未指定事务管理器时的默认实现,比如修改DB1Config中的事务管理器定义:
@Bean @Primary // 添加此注解 public PlatformTransactionManager db1TransactionManager(@Qualifier("db1EntityManagerFactory") FactoryBean<EntityManagerFactory> db1EntityManagerFactory) throws Exception { var tm = new JpaTransactionManager(); tm.setEntityManagerFactory(db1EntityManagerFactory.getObject()); return tm; }
注意:此方案全局生效,若其他数据源操作未明确指定事务管理器,会默认使用主事务管理器,存在数据写入错误数据源的风险。
方案3:自定义通用基类(适合多数据源存在大量自定义DAO的场景)
为每个数据源定义专属的Repository基类,统一配置事务管理器,后续所有对应数据源的自定义DAO直接继承对应基类即可,无需重复配置:
// DB2数据源的通用基类 @Transactional(transactionManager = "db2TransactionManager") public abstract class DB2BaseRepository<T, ID> extends SimpleJpaRepository<T, ID> { public DB2BaseRepository(Class<T> domainClass, EntityManager entityManager) { super(domainClass, entityManager); } // 统一重写所有父类事务方法 @Override @Transactional public <S extends T> S save(S entity) { return super.save(entity); } @Override @Transactional public void deleteById(ID id) { super.deleteById(id); } // 其他需要用到的方法统一重写 }
修改你的ProductCustomRepository继承该基类即可:
@Repository public class ProductCustomRepository extends DB2BaseRepository<ProductEntity2, Integer> { private final EntityManager entityManager; public ProductCustomRepository(@Qualifier("db2EntityManagerFactory") EntityManager entityManager) { super(ProductEntity2.class, entityManager); this.entityManager = entityManager; } public List<ProductEntity2> customQuery() { var query = entityManager.createQuery("SELECT p FROM ProductEntity2 p WHERE p.name='special'",ProductEntity2.class); return query.getResultList(); } }
内容的提问来源于stack exchange,提问作者vscoding
相关产品推荐
相关产品推荐

