Spring Boot 2.x基础教程:MyBatis的多数据源配置
前两天,我们已经介绍了关于JdbcTemplate的多数据源配置以及Spring Data JPA的多数据源配置,接下来具体说说使用MyBatis时候的多数据源场景该如何配置。
添加多数据源的配置
先在Spring Boot的配置文件application.properties中设置两个你要链接的数据库配置,比如这样:
spring.datasource.primary.jdbc-url=jdbc:mysql://localhost:3306/test1
spring.datasource.primary.username=root
spring.datasource.primary.password=123456
spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.secondary.jdbc-url=jdbc:mysql://localhost:3306/test2
spring.datasource.secondary.username=root
spring.datasource.secondary.password=123456
spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver
说明与注意:
- 多数据源配置的时候,与单数据源不同点在于spring.datasource之后多设置一个数据源名称primary和secondary来区分不同的数据源配置,这个前缀将在后续初始化数据源的时候用到。
- 数据源连接配置2.x和1.x的配置项是有区别的:2.x使用spring.datasource.secondary.jdbc-url,而1.x版本使用spring.datasource.secondary.url。如果你在配置的时候发生了这个报错java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName.,那么就是这个配置项的问题。
- 可以看到,不论使用哪一种数据访问框架,对于数据源的配置都是一样的。
初始化数据源与MyBatis配置
完成多数据源的配置信息之后,就来创建个配置类来加载这些配置信息,初始化数据源,以及初始化每个数据源要用的MyBatis配置。
这里我们继续将数据源与框架配置做拆分处理:
- 单独建一个多数据源的配置类,比如下面这样:
@Configuration
public class DataSourceConfiguration {
@Primary
@Bean
@ConfigurationProperties(prefix = "spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
@ConfigurationProperties(prefix = "spring.datasource.secondary")
public DataSource secondaryDataSource() {
return DataSourceBuilder.create().build();
}
}
可以看到内容跟JdbcTemplate、Spring Data JPA的时候是一模一样的。通过@ConfigurationProperties可以知道这两个数据源分别加载了spring.datasource.primary.*和spring.datasource.secondary.*的配置。@Primary注解指定了主数据源,就是当我们不特别指定哪个数据源的时候,就会使用这个Bean真正差异部分在下面的JPA配置上。
- 分别创建两个数据源的MyBatis配置。
Primary数据源的JPA配置:
@Configuration
@MapperScan(
basePackages = "com.didispace.chapter39.p",
sqlSessionFactoryRef = "sqlSessionFactoryPrimary",
sqlSessionTemplateRef = "sqlSessionTemplatePrimary")
public class PrimaryConfig {
private DataSource primaryDataSource;
public PrimaryConfig(@Qualifier("primaryDataSource") DataSource primaryDataSource) {
this.primaryDataSource = primaryDataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactoryPrimary() throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(primaryDataSource);
return bean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplatePrimary() throws Exception {
return new SqlSessionTemplate(sqlSessionFactoryPrimary());
}
}
Secondary数据源的JPA配置:
@Configuration
@MapperScan(
basePackages = "com.didispace.chapter39.s",
sqlSessionFactoryRef = "sqlSessionFactorySecondary",
sqlSessionTemplateRef = "sqlSessionTemplateSecondary")
public class SecondaryConfig {
private DataSource secondaryDataSource;
public SecondaryConfig(@Qualifier("secondaryDataSource") DataSource secondaryDataSource) {
this.secondaryDataSource = secondaryDataSource;
}
@Bean
public SqlSessionFactory sqlSessionFactorySecondary() throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(secondaryDataSource);
return bean.getObject();
}
@Bean
public SqlSessionTemplate sqlSessionTemplateSecondary() throws Exception {
return new SqlSessionTemplate(sqlSessionFactorySecondary());
}
}
说明与注意:
- 配置类上使用
@MapperScan注解来指定当前数据源下定义的Entity和Mapper的包路径;另外需要指定sqlSessionFactory和sqlSessionTemplate,这两个具体实现在该配置类中类中初始化。 - 配置类的构造函数中,通过
@Qualifier注解来指定具体要用哪个数据源,其名字对应在DataSourceConfiguration配置类中的数据源定义的函数名。 - 配置类中定义SqlSessionFactory和SqlSessionTemplate的实现,注意具体使用的数据源正确(如果使用这里的演示代码,只要第二步没问题就不需要修改)。
上一篇介绍JPA的时候,因为之前介绍JPA的使用时候,说过实体和Repository定义的方法,所以省略了 User 和 Repository的定义代码,但是还是有读者问怎么没有这个,其实都有说明,仓库代码里也都是有的。未避免再问这样的问题,所以这里就贴一下吧。
根据上面Primary数据源的定义,在com.didispace.chapter39.p包下,定义Primary数据源要用的实体和数据访问对象,比如下面这样:
@Data
@NoArgsConstructor
public class UserPrimary {
private Long id;
private String name;
private Integer age;
public UserPrimary(String name, Integer age) {
this.name = name;
this.age = age;
}
}
public interface UserMapperPrimary {
@Select("SELECT * FROM USER WHERE NAME = #{name}")
UserPrimary findByName(@Param("name") String name);
@Insert("INSERT INTO USER(NAME, AGE) VALUES(#{name}, #{age})")
int insert(@Param("name") String name, @Param("age") Integer age);
@Delete("DELETE FROM USER")
int deleteAll();
}
根据上面Secondary数据源的定义,在com.didispace.chapter39.s包下,定义Secondary数据源要用的实体和数据访问对象,比如下面这样:
@Data
@NoArgsConstructor
public class UserSecondary {
private Long id;
private String name;
private Integer age;
public UserSecondary(String name, Integer age) {
this.name = name;
this.age = age;
}
}
public interface UserMapperSecondary {
@Select("SELECT * FROM USER WHERE NAME = #{name}")
UserSecondary findByName(@Param("name") String name);
@Insert("INSERT INTO USER(NAME, AGE) VALUES(#{name}, #{age})")
int insert(@Param("name") String name, @Param("age") Integer age);
@Delete("DELETE FROM USER")
int deleteAll();
}
测试验证
完成了上面之后,我们就可以写个测试类来尝试一下上面的多数据源配置是否正确了,先来设计一下验证思路:
- 往Primary数据源插入一条数据
- 从Primary数据源查询刚才插入的数据,配置正确就可以查询到
- 从Secondary数据源查询刚才插入的数据,配置正确应该是查询不到的
- 往Secondary数据源插入一条数据
- 从Primary数据源查询刚才插入的数据,配置正确应该是查询不到的
- 从Secondary数据源查询刚才插入的数据,配置正确就可以查询到
具体实现如下:
@Slf4j
@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
public class Chapter39ApplicationTests {
@Autowired
private UserMapperPrimary userMapperPrimary;
@Autowired
private UserMapperSecondary userMapperSecondary;
@Before
public void setUp() {
// 清空测试表,保证每次结果一样
userMapperPrimary.deleteAll();
userMapperSecondary.deleteAll();
}
@Test
public void test() throws Exception {
// 往Primary数据源插入一条数据
userMapperPrimary.insert("AAA", 20);
// 从Primary数据源查询刚才插入的数据,配置正确就可以查询到
UserPrimary userPrimary = userMapperPrimary.findByName("AAA");
Assert.assertEquals(20, userPrimary.getAge().intValue());
// 从Secondary数据源查询刚才插入的数据,配置正确应该是查询不到的
UserSecondary userSecondary = userMapperSecondary.findByName("AAA");
Assert.assertNull(userSecondary);
// 往Secondary数据源插入一条数据
userMapperSecondary.insert("BBB", 20);
// 从Primary数据源查询刚才插入的数据,配置正确应该是查询不到的
userPrimary = userMapperPrimary.findByName("BBB");
Assert.assertNull(userPrimary);
// 从Secondary数据源查询刚才插入的数据,配置正确就可以查询到
userSecondary = userMapperSecondary.findByName("BBB");
Assert.assertEquals(20, userSecondary.getAge().intValue());
}
}
代码示例
本文的相关例子可以查看下面仓库中的chapter3-9目录:
- Github:https://github.com/dyc87112/SpringBoot-Learning/
- Gitee:https://gitee.com/didispace/SpringBoot-Learning/
如果您觉得本文不错,欢迎Star支持,您的关注是我坚持的动力!
Spring Boot 2.x基础教程:MyBatis的多数据源配置的更多相关文章
- Spring Boot 2.x基础教程:使用MyBatis的XML配置方式
上一篇我们介绍了如何在Spring Boot中整合我们国人最常用的MyBatis来实现对关系型数据库的访问.但是上一篇中使用了注解方式来实现,而对于很多MyBatis老用户还是习惯于XML的开发方式, ...
- Spring Boot 2.x基础教程:使用JTA实现多数据源的事务管理
在一个Spring Boot项目中,连接多个数据源还是比较常见的.之前也介绍了如何在几种常用框架的场景下配置多数据源,具体可见: Spring Boot 2.x基础教程:JdbcTemplate的多数 ...
- Spring Boot 2.x基础教程:使用Swagger2构建强大的API文档
随着前后端分离架构和微服务架构的流行,我们使用Spring Boot来构建RESTful API项目的场景越来越多.通常我们的一个RESTful API就有可能要服务于多个不同的开发人员或开发团队:I ...
- Spring Boot 2.x基础教程:JSR-303实现请求参数校验
请求参数的校验是很多新手开发非常容易犯错,或存在较多改进点的常见场景.比较常见的问题主要表现在以下几个方面: 仅依靠前端框架解决参数校验,缺失服务端的校验.这种情况常见于需要同时开发前后端的时候,虽然 ...
- Spring Boot 2.x基础教程:Swagger接口分类与各元素排序问题详解
之前通过Spring Boot 2.x基础教程:使用Swagger2构建强大的API文档一文,我们学习了如何使用Swagger为Spring Boot项目自动生成API文档,有不少用户留言问了关于文档 ...
- Spring Boot 2.x基础教程:Swagger静态文档的生成
前言 通过之前的两篇关于Swagger入门以及具体使用细节的介绍之后,我们已经能够轻松地为Spring MVC的Web项目自动构建出API文档了.如果您还不熟悉这块,可以先阅读: Spring Boo ...
- Spring Boot 2.x基础教程:使用国产数据库连接池Druid
上一节,我们介绍了Spring Boot在JDBC模块中自动化配置使用的默认数据源HikariCP.接下来这一节,我们将介绍另外一个被广泛应用的开源数据源:Druid. Druid是由阿里巴巴数据库事 ...
- Spring Boot 2.x基础教程:找回启动日志中的请求路径列表
如果您看过之前的Spring Boot 1.x教程,或者自己原本就对Spring Boot有一些经验,或者对Spring MVC很熟悉.那么对于Spring构建的Web应用在启动的时候,都会输出当前应 ...
- Spring Boot 2.x基础教程:Spring Data JPA的多数据源配置
上一篇我们介绍了在使用JdbcTemplate来做数据访问时候的多数据源配置实现.接下来我们继续学习如何在使用Spring Data JPA的时候,完成多数据源的配置和使用. 添加多数据源的配置 先在 ...
- Spring Boot 2.x基础教程:事务管理入门
什么是事务? 我们在开发企业应用时,通常业务人员的一个操作实际上是对数据库读写的多步操作的结合.由于数据操作在顺序执行的过程中,任何一步操作都有可能发生异常,异常会导致后续操作无法完成,此时由于业务逻 ...
随机推荐
- 总结了一下css的部分基础知识点。---css学习笔记01
一. css基础 1. 什么是css 层叠样式表:层叠 2. css的三种使用方式 style 属性 --> <div style="css属性值"></d ...
- Java实现 蓝桥杯 算法提高 抽卡游戏
试题 算法提高 抽卡游戏 某个抽卡游戏卡池抽出限定卡的概率为p,该游戏有一个"井"的机制,抽满k次卡后直接送这张限定卡.试求获得这张限定卡需要的期望抽卡次数.输入为一行,用空格隔开 ...
- Java实现 蓝桥杯VIP 基础练习 时间转换
问题描述 给定一个以秒为单位的时间t,要求用"h️s" 的格式来表示这个时间.H表示时间,M表示分钟,而s表示秒,它们都是整数且没有前导的"0".例如,若t=0 ...
- Java实现 LeetCode 392 判断子序列
392. 判断子序列 给定字符串 s 和 t ,判断 s 是否为 t 的子序列. 你可以认为 s 和 t 中仅包含英文小写字母.字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符 ...
- Java实现 蓝桥杯VIP 算法训练 完数
问题描述 一个数如果恰好等于它的因子之和,这个数就称为"完数".例如,6的因子为1.2.3,而6=1+2+3,因此6就是"完数".又如,28的因子为1.2.4. ...
- Java实现 LeetCode 134 加油站
134. 加油站 在一条环路上有 N 个加油站,其中第 i 个加油站有汽油 gas[i] 升. 你有一辆油箱容量无限的的汽车,从第 i 个加油站开往第 i+1 个加油站需要消耗汽油 cost[i] 升 ...
- Java实现 蓝桥杯 算法提高 扶老奶奶过街
1 问题描述 一共有5个红领巾,编号分别为A.B.C.D.E,老奶奶被他们其中一个扶过了马路. 五个红领巾各自说话: A :我和E都没有扶老奶奶 B :老奶奶是被C和E其中一个扶过大街的 C :老奶奶 ...
- MySQL死锁及解决方案
一.MySQL锁类型 1. MySQL常用存储引擎的锁机制 MyISAM和MEMORY采用表级锁(table-level locking) BDB采用页面锁(page-level locking)或表 ...
- (四)SQLMap之Tamper篡改脚本的类型、作用、适用场景
目录结构 一.SQLMap中tamper的简介 1.tamper的作用 2.tamper用法 二.适配不同数据库类型的测试tamper 三.SQLMap中tamper篡改脚本的功能解释 一.SQLMa ...
- 权限系统设计(0):权限系统设计基本概念改需-MAC/RBAC引子
此篇主要对权限系统设计所涉的一些专业术语重点梳理.从我们windows的文件系统 自主访问控制 到基于角色访问控制. 权限设计基本术语 对后面会用到的词汇做一个简要说明 什么是权限(许可) 权限(Pr ...