在 Spring Boot 项目中同时引入多个持久化框架(如 JPA 和 MyBatis)或多数据源时,核心挑战在于Bean 的隔离与事务的统一。以下是针对这两个核心问题的系统性解决方案总结:

一、 多框架/多数据源的隔离与指定(解决“用哪个”的问题)

当项目中存在多个数据源时,必须通过包路径隔离显式绑定来避免 Spring 容器发生冲突。

  1. 包路径物理隔离
    将 JPA 的实体/Repository 与 MyBatis 的 Mapper 接口分别放在不同的包下(例如 com.example.jpa 和 com.example.mybatis)。
  2. 配置类独立绑定
    • JPA 侧:使用 @EnableJpaRepositories 指定扫描包,并通过 entityManagerFactoryRef 和 transactionManagerRef 绑定专属的工厂和事务管理器8。
    • MyBatis 侧:使用 @MapperScan 指定扫描包,并通过 sqlSessionFactoryRef 绑定专属的 SqlSessionFactory3。
  3. 注入时的精确指定
    在注入 DataSource 时,使用 @Qualifier("beanName") 按名称精确匹配;在存在多个同类型 Bean 时,必须使用 @Primary 标记一个主数据源作为默认首选4。

二、 多框架事务管理器的解决方案(解决“事务一致性”的问题)

不同框架默认依赖不同的事务管理器(JPA 依赖 JpaTransactionManager,MyBatis 依赖 DataSourceTransactionManager)。如果各自为战,会导致跨框架调用时事务上下文隔离,引发部分回滚或脏读问题1。

最佳实践:统一底层事务管理器
为了保证 JPA 和 MyBatis 在同一个事务中“同生共死”,推荐统一使用 DataSourceTransactionManager 作为 @Primary 主事务管理器9。

  • 原理:JPA 和 MyBatis 底层都是基于 JDBC 连接。DataSourceTransactionManager 能够直接接管底层的 Connection,从而确保两个框架的操作共享同一个数据库连接和事务上下文3。

三、 事务管理器的选择与使用规范

在配置了多个事务管理器后,Spring 决定使用哪个事务管理器遵循以下优先级规则:1

  1. 显式指定(最高优先级)
    在 @Transactional 注解中明确指定名称,如 @Transactional("mybatisTransactionManager")。Spring 会无条件使用指定的管理器3。
  2. @Primary 标记(默认首选)
    如果仅写 @Transactional 未指定名称,Spring 会优先使用带有 @Primary 注解的事务管理器。这能避免每次都要手动指定名称的繁琐5。
  3. 类型匹配(兜底机制)
    如果既没有显式指定,也没有 @Primary,且容器中存在多个同类型的事务管理器,Spring 将抛出 NoUniqueBeanDefinitionException 导致启动失败7。

四、 跨数据源事务的终极方案

需要特别注意的是,如果业务逻辑需要在同一个事务中同时操作两个不同的数据源(例如主库写订单,从库写日志),上述的本地事务管理器将失效,因为不同的事务管理器之间无法共享事务上下文1。

解决方案:必须引入分布式事务机制。

  • 可以使用 JTA(Java Transaction API) 规范,如集成 Atomikos 或 Bitronix,配置 JtaTransactionManager1。
  • 或者采用柔性事务方案(如 Seata),通过 TCC 或 AT 模式保证跨库操作的最终一致性5。

更多推荐