静态多数据源配置

发布于:2025-09-09 ⋅ 阅读:(12) ⋅ 点赞:(0)

静态多数据源配置(或称为物理隔离)的标准做法-不使用动态切换的方式,使用扫描不同包路径,使用不同DataSource,初始化不同SqlSessionFactory分别实现对应业务。这种做法通过为每个数据源创建独立的 DataSourceSqlSessionFactory以及事务管理器,并将不同数据源的 Mapper 接口和 XML 文件严格放在不同的包路径下来实现清晰的管理和彻底的隔离。

下面我用一个表格对比静态配置和动态数据源路由的主要区别,帮助你更好地理解这种方案的特点:

特性维度

静态多数据源配置 (你提出的方案)

动态数据源路由 (作为对比参考)

核心思想

每个数据源有专属SqlSessionFactoryMapper 包/XML 目录

一个SqlSessionFactory,其背后数据源根据条件动态变化

Mapper 管理

Mapper 接口和 XML 文件按数据源分开放置在不同目录

Mapper 接口和 XML 文件通常放在一起,通过注解在运行时切换数据源

优点

稳定直观,隔离彻底,避免冲突;事务管理简单

灵活,代码冗余少,适合需要运行时动态决定数据源的场景

缺点

数据源较多时配置略显繁琐;Mapper 分散

事务管理复杂(尤其跨数据源事务);对编码有一定侵入性(需加注解)

适用场景

不同业务模块固定使用不同数据库,且模块间界限清晰

需要运行时动态决定数据源(如根据租户ID、业务标识操作不同库)、多租户

⚙️ 静态多数据源配置核心步骤

这种方案的关键在于清晰的隔离正确的配置

项目结构规划

建议按数据源划分包和资源目录,结构清晰易懂:

复制
src/main/java
└── com
    └── example
        └── yourproject
            ├── config
            │   ├── PrimaryDataSourceConfig.java
            │   └── SecondaryDataSourceConfig.java
            ├── mapper
            │   ├── primary  # 数据源一的Mapper接口
            │   │   ├── UserMapper.java
            │   │   └── OrderMapper.java
            │   └── secondary # 数据源二的Mapper接口
            │       ├── ProductMapper.java
            │       └── LogMapper.java
            └── service
                ├── PrimaryService.java
                └── SecondaryService.java

src/main/resources
└── mapper
    ├── primary   # 数据源一的XML文件
    │   ├── UserMapper.xml
    │   └── OrderMapper.xml
    └── secondary # 数据源二的XML文件
        ├── ProductMapper.xml
        └── LogMapper.xml

    配置数据源信息 (application.yml)

    yaml
    
    复制
    
    spring:
      datasource:
        primary: # 主数据源配置
          url: jdbc:mysql://localhost:3306/db_primary?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
          username: your_username
          password: your_password
          driver-class-name: com.mysql.cj.jdbc.Driver
          # 如果使用Druid连接池,还可以配置更多参数
          # type: com.alibaba.druid.pool.DruidDataSource
          # initial-size: 5
          # max-active: 20
        secondary: # 从数据源配置
          url: jdbc:mysql://localhost:3306/db_secondary?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
          username: your_username
          password: your_password
          driver-class-name: com.mysql.cj.jdbc.Driver

      编写数据源配置类

      为每个数据源创建独立的 DataSource, SqlSessionFactory, 事务管理器,并使用 @MapperScan指定扫描的包。

      主数据源配置示例 (PrimaryDataSourceConfig.java)

      java
      
      下载
      复制
      运行
      
      @Configuration
      // 指定扫描的Mapper接口包和对应的SqlSessionFactory引用
      @MapperScan(basePackages = "com.example.yourproject.mapper.primary", sqlSessionFactoryRef = "primarySqlSessionFactory")
      public class PrimaryDataSourceConfig {
      
          @Primary // 标记为主数据源
          @Bean(name = "primaryDataSource")
          @ConfigurationProperties(prefix = "spring.datasource.primary")
          public DataSource primaryDataSource() {
              return DataSourceBuilder.create().build(); // 或 DruidDataSourceBuilder.create().build();
          }
      
          @Primary
          @Bean(name = "primarySqlSessionFactory")
          public SqlSessionFactory primarySqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
              SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
              sessionFactoryBean.setDataSource(dataSource);
              // 设置主数据源对应的Mapper XML文件位置
              sessionFactoryBean.setMapperLocations(
                  new PathMatchingResourcePatternResolver()
                      .getResources("classpath:mapper/primary/*.xml"));
              // 可选:其他MyBatis配置,如驼峰命名
              org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
              configuration.setMapUnderscoreToCamelCase(true);
              sessionFactoryBean.setConfiguration(configuration);
              return sessionFactoryBean.getObject();
          }
      
          @Primary
          @Bean(name = "primaryTransactionManager")
          public PlatformTransactionManager primaryTransactionManager(@Qualifier("primaryDataSource") DataSource dataSource) {
              return new DataSourceTransactionManager(dataSource);
          }
      }

      从数据源配置示例 (SecondaryDataSourceConfig.java)

      java
      
      下载
      复制
      运行
      
      @Configuration
      // 指定扫描的Mapper接口包和对应的SqlSessionFactory引用
      @MapperScan(basePackages = "com.example.yourproject.mapper.secondary", sqlSessionFactoryRef = "secondarySqlSessionFactory")
      public class SecondaryDataSourceConfig {
      
          @Bean(name = "secondaryDataSource")
          @ConfigurationProperties(prefix = "spring.datasource.secondary")
          public DataSource secondaryDataSource() {
              return DataSourceBuilder.create().build();
          }
      
          @Bean(name = "secondarySqlSessionFactory")
          public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("secondaryDataSource") DataSource dataSource) throws Exception {
              SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
              sessionFactoryBean.setDataSource(dataSource);
              // 设置从数据源对应的Mapper XML文件位置
              sessionFactoryBean.setMapperLocations(
                  new PathMatchingResourcePatternResolver()
                      .getResources("classpath:mapper/secondary/*.xml"));
              return sessionFactoryBean.getObject();
          }
      
          @Bean(name = "secondaryTransactionManager")
          public PlatformTransactionManager secondaryTransactionManager(@Qualifier("secondaryDataSource") DataSource dataSource) {
              return new DataSourceTransactionManager(dataSource);
          }
      }

        在 Service 中使用

        在 Service 中注入对应数据源的 Mapper,并使用 @Transactional注解指定事务管理器。

        java
        
        下载
        复制
        运行
        
        @Service
        public class SomeService {
        
            @Autowired
            private com.example.yourproject.mapper.primary.UserMapper primaryUserMapper; // 主数据源Mapper
        
            @Autowired
            private com.example.yourproject.mapper.secondary.ProductMapper secondaryProductMapper; // 从数据源Mapper
        
            // 使用主数据源的事务
            @Transactional(transactionManager = "primaryTransactionManager")
            public void doSomethingWithPrimary() {
                primaryUserMapper.insert(...);
            }
        
            // 使用从数据源的事务
            @Transactional(transactionManager = "secondaryTransactionManager")
            public void doSomethingWithSecondary() {
                secondaryProductMapper.update(...);
            }
        }

        ⚠️ 注意事项

        事务管理:每个数据源必须有自己独立的事务管理器DataSourceTransactionManager)。@Transactional注解默认使用主数据源的事务管理器。操作非主数据源时,必须@Transactional中通过 transactionManager属性显式指定事务管理器 Bean 的名称。

        Mapper 扫描隔离:确保每个配置类的 @MapperScanbasePackages指向不同的包,且与 sqlSessionFactoryRef正确关联,防止 Mapper 接口被多个 SqlSessionFactory重复扫描导致意外行为。

        XML 文件位置:确保 XML 文件放在 src/main/resources下正确的目录中(如 mapper/primary)。如果 Maven 未将 XML 文件复制到 target/classes,需在 pom.xml中配置资源过滤。

        连接池配置:为每个数据源单独配置连接池参数(如初始大小、最大连接数、最小空闲连接等),根据各业务的并发压力进行调整。

        💡 方案选择建议

        对于大多数应用,静态配置(方案一) 更简单、稳定、易于理解和维护,推荐优先使用。

        只有在需要根据运行时参数(如租户ID、业务标识符)动态切换数据源的场景下(如SaaS多租户系统),才考虑动态数据源路由方案。此方案实现更复杂,且需要仔细处理事务和线程安全问题。

        希望这些信息能帮助你更好地管理和配置多数据源项目中的 MyBatis Mapper 和 XML 文件。祝你编程愉快!


        网站公告

        今日签到

        点亮在社区的每一天
        去签到