静态多数据源配置(或称为物理隔离)的标准做法-不使用动态切换的方式,使用扫描不同包路径,使用不同DataSource,初始化不同SqlSessionFactory分别实现对应业务。这种做法通过为每个数据源创建独立的 DataSource
、SqlSessionFactory
以及事务管理器,并将不同数据源的 Mapper 接口和 XML 文件严格放在不同的包路径下来实现清晰的管理和彻底的隔离。
下面我用一个表格对比静态配置和动态数据源路由的主要区别,帮助你更好地理解这种方案的特点:
特性维度 |
静态多数据源配置 (你提出的方案) |
动态数据源路由 (作为对比参考) |
核心思想 |
每个数据源有专属的 |
一个 |
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 扫描隔离:确保每个配置类的 @MapperScan
的 basePackages
指向不同的包,且与 sqlSessionFactoryRef
正确关联,防止 Mapper 接口被多个 SqlSessionFactory
重复扫描导致意外行为。
- •
XML 文件位置:确保 XML 文件放在 src/main/resources
下正确的目录中(如 mapper/primary
)。如果 Maven 未将 XML 文件复制到 target/classes
,需在 pom.xml
中配置资源过滤。
- •
连接池配置:为每个数据源单独配置连接池参数(如初始大小、最大连接数、最小空闲连接等),根据各业务的并发压力进行调整。
💡 方案选择建议
- •
对于大多数应用,静态配置(方案一) 更简单、稳定、易于理解和维护,推荐优先使用。
- •
只有在需要根据运行时参数(如租户ID、业务标识符)动态切换数据源的场景下(如SaaS多租户系统),才考虑动态数据源路由方案。此方案实现更复杂,且需要仔细处理事务和线程安全问题。
希望这些信息能帮助你更好地管理和配置多数据源项目中的 MyBatis Mapper 和 XML 文件。祝你编程愉快!