在我们日常的开发中,经常会用到一个项目中使用多个数据源的问题,本次就带你了解怎样在spring boot项目中使用mybatis整合多个数据源的示例。使用spring boot3.5版本
1、创建一个spring boot项目,并引入相应的maven依赖
java
<dependencies>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
<version>4.5.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.20</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2、在配置文件中设置相应的连接信息
java
spring:
datasource:
# 主数据源
zh:
jdbc-url: jdbc:mysql://localhost:3306/zh?useSSl=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
# 次数据源
en:
jdbc-url: jdbc:mysql://localhost:3306/en?useSSl=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:/mapper/**/*.xml
configuration:
map-underscore-to-camel-case: true
这里需要注意的一点是,我们引入了mybatis-springboot的自动装配的依赖,那么,项目在启动的时候会检查我们有没有配置默认的数据源连接信息,如果没有配置会报错。这个时候我们有两种解决思路。一种是在配置文件中指定一下连接信息,但是这个数据连接信息我们不用,另一种是把数据源自动配置的bean给排除掉。需要在启动类上指定
java
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
3、手动指定多数据源的连接配置
我的项目中是指定了两套数据源,分别为zh、en。我只需要配置两套数据连接信息即可;
配置zh数据库的连接配置:
java
@Configuration
@MapperScan(
basePackages = "com.zq.testmybatis.mapper.zh",
sqlSessionFactoryRef = "zhSqlSessionFactory"
)
public class ZhMyBatisConfig {
/**
* 创建并返回一个以 "spring.datasource.zh" 为前缀的配置属性绑定的数据源对象。
*
* @return DataSource 对象
*/
@Primary
@Bean
@ConfigurationProperties(prefix = "spring.datasource.zh")
public DataSource zhDataSource() {
return DataSourceBuilder.create().build();
}
/**
* 创建中文数据库SQL会话工厂。
*
* @param dataSource 数据源,必须被标注为@Qualifier("zhDataSource")
* @return SqlSessionFactory 对象
* @throws Exception 抛出异常
*/
@Primary
@Bean
public SqlSessionFactory zhSqlSessionFactory(
@Qualifier("zhDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
// 重点:手动启用驼峰映射
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(true); // 启用驼峰映射
sessionFactory.setConfiguration(configuration);
sessionFactory.setMapperLocations(
new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/zh/**/*.xml"));
return sessionFactory.getObject();
}
/**
* 创建一个名为zhSqlSessionTemplate的SqlSessionTemplate对象,该对象使用名为zhSqlSessionFactory的SqlSessionFactory。
*
* @param sqlSessionFactory 类型为SqlSessionFactory,用于创建SqlSessionTemplate对象的SqlSessionFactory。
* @return 返回类型为SqlSessionTemplate的zhSqlSessionTemplate对象。
*/
@Primary
@Bean
public SqlSessionTemplate zhSqlSessionTemplate(
@Qualifier("zhSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
配置en数据库的连接信息:
java
@Configuration
@MapperScan(
basePackages = "com.zq.testmybatis.mapper.en",
sqlSessionFactoryRef = "enSqlSessionFactory"
)
public class EnMyBatisConfig {
/**
* 创建并返回一个名为 "enDataSource" 的数据源。
*
* @return 返回一个配置为 "spring.datasource.en" 前缀的配置属性的数据源
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource.en")
public DataSource enDataSource() {
return DataSourceBuilder.create().build();
}
/**
* 创建一个 SqlSessionFactory Bean,使用指定的数据源。
*
* @param dataSource 数据源,通过 @Qualifier 注解指定
* @return SqlSessionFactory 对象
* @throws Exception 如果在创建 SqlSessionFactory 过程中发生异常
*/
@Bean
public SqlSessionFactory enSqlSessionFactory(
@Qualifier("enDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
// 重点:手动启用驼峰映射
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(true); // 启用驼峰映射
sessionFactory.setConfiguration(configuration);
sessionFactory.setMapperLocations(
new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/en/**/*.xml"));
return sessionFactory.getObject();
}
/**
* 创建一个SqlSessionTemplate对象
*
* @param sqlSessionFactory 数据库会话工厂,通过@Qualifier注解指定使用"enSqlSessionFactory"
* @return 返回创建的SqlSessionTemplate对象
*/
@Bean
public SqlSessionTemplate enSqlSessionTemplate(
@Qualifier("enSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
这里需要我们注意的主要有两点:mapper接口的扫射路径、XML文件的扫射路径;
还有一点要注意的是,由于我们是手动指定了XML路径的连接信息,所有如果spring项目在启动时扫描到你指定的XML地址下没有相应的XML文件时,项目会报错。
4、使用多数据源
配置好了以上的所有信息之后,我们就可以正常的使用mapper接口来进行数据操作了。这里要提醒的一点是,两个数据源的mapper接口尽量不要相同,因为我们是在同一个spring boot中,也就是这两个mapper接口都会被扫描到IOC容器中。
java
@RestController
public class TestController {
@Resource
private EnUserMapper enUserMapper;
@Resource
private ZhUserMapper zhUserMapper;
@PostMapping("/addEnUser")
public String addEnUser(@RequestBody EnUser enUser) {
enUserMapper.insert(enUser);
return "success";
}
@GetMapping("/getEnUser")
public List<EnUser> getEnUser() {
return enUserMapper.listAll();
}
@PostMapping("/addZHUser")
public String addZhUser(@RequestBody ZhUser zhUser) {
zhUserMapper.insert(zhUser);
return "success";
}
@GetMapping("/getZhUser")
public List<ZhUser> getZhUser() {
return zhUserMapper.listAll();
}
}
就和我们正常使用一样就可以了;
5、多数据源的事务一致性
在单个数据源中,我们只需要使用@Transactional注解就能保证事务的一致性,但是在多数据源中不行,但是同样有解决的办法,我们可以使用JTA分布式事务(Atomikos)或者分布式事务seata
首先介绍一下JTA是基于XA协议的两阶段提交(2PC),属于Java EE规范的一部分。事务由应用服务器(如WebLogic、JBoss)的事务管理器(TM)协调,数据库作为资源管理器(RM)实现XA接口它的工作流程为:
-
阶段一:TM询问所有RM是否可提交,RM锁定资源并返回准备状态。
-
阶段二:TM根据RM反馈决定全局提交或回滚。
-
特点:强一致性,但存在同步阻塞(阶段一锁定资源)和单点故障风险。
seata是阿里巴巴开源分布式事务框架,相应的特性可以看我之前写的这篇文章:
seata的快速入门和实战_seata快速入门-CSDN博客
维度 | JTA/XA | Seata |
---|---|---|
事务模型 | 基于XA协议的2PC | 支持AT、TCC、Saga、XA多种模式 |
一致性 | 强一致性(实时生效) | AT模式为最终一致性,TCC/XA为强一致性 |
性能 | 低(阶段一长期锁资源) | 高(AT模式一阶段提交释放锁)14 |
侵入性 | 无代码侵入,依赖数据库XA驱动 | AT模式无侵入,TCC需业务编码补偿逻辑57 |
架构依赖 | 强依赖应用服务器(如WebLogic) | 独立中间件,与Spring Cloud等微服务框架集成26 |
资源要求 | 数据库需支持XA协议 | AT模式无需XA驱动,仅需本地事务支持13 |
锁机制 | 数据库行锁 | AT模式通过全局锁(lock_table )避免脏写 |
这两种方法都能解决多数据源的数据不一致问题,具体使用哪一种就看你自己的业务场景了。