spring boot项目整合mybatis实现多数据源的配置
在我们日常的开发中,经常会用到一个项目中使用多个数据源的问题,本次就带你了解怎样在spring boot项目中使用mybatis整合多个数据源的示例。使用spring boot3.5版本
1、创建一个spring boot项目,并引入相应的maven依赖
<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、在配置文件中设置相应的连接信息
spring:datasource:# 主数据源zh:jdbc-url: jdbc:mysql://localhost:3306/zh?useSSl=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTCusername: rootpassword: 123456type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driver# 次数据源en:jdbc-url: jdbc:mysql://localhost:3306/en?useSSl=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=UTCusername: rootpassword: 123456type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driver
mybatis:mapper-locations: classpath:/mapper/**/*.xmlconfiguration:map-underscore-to-camel-case: true
这里需要注意的一点是,我们引入了mybatis-springboot的自动装配的依赖,那么,项目在启动的时候会检查我们有没有配置默认的数据源连接信息,如果没有配置会报错。这个时候我们有两种解决思路。一种是在配置文件中指定一下连接信息,但是这个数据连接信息我们不用,另一种是把数据源自动配置的bean给排除掉。需要在启动类上指定
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
3、手动指定多数据源的连接配置
我的项目中是指定了两套数据源,分别为zh、en。我只需要配置两套数据连接信息即可;
配置zh数据库的连接配置:
@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@Beanpublic 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@Beanpublic SqlSessionTemplate zhSqlSessionTemplate(@Qualifier("zhSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {return new SqlSessionTemplate(sqlSessionFactory);}
}
配置en数据库的连接信息:
@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 过程中发生异常*/@Beanpublic 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对象*/@Beanpublic SqlSessionTemplate enSqlSessionTemplate(@Qualifier("enSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {return new SqlSessionTemplate(sqlSessionFactory);}
}
这里需要我们注意的主要有两点:mapper接口的扫射路径、XML文件的扫射路径;
还有一点要注意的是,由于我们是手动指定了XML路径的连接信息,所有如果spring项目在启动时扫描到你指定的XML地址下没有相应的XML文件时,项目会报错。
4、使用多数据源
配置好了以上的所有信息之后,我们就可以正常的使用mapper接口来进行数据操作了。这里要提醒的一点是,两个数据源的mapper接口尽量不要相同,因为我们是在同一个spring boot中,也就是这两个mapper接口都会被扫描到IOC容器中。
@RestController
public class TestController {
@Resourceprivate EnUserMapper enUserMapper;
@Resourceprivate 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 )避免脏写 |
这两种方法都能解决多数据源的数据不一致问题,具体使用哪一种就看你自己的业务场景了。