在开发过程中,避免不了需要同时操作多个数据库的情况,通常的应用场景如下 :
使用 Spring Boot 该如何处理多个数据库的读写,一般有以下几种策略:
多套数据源,顾名思义每一个数据库有一套独立的操作。从下往上,数据库、会话工厂、DAO 操作,服务层都是独立的一套,如下所示:
多套数据源
本示例中,以一主一从两个数据库,两数据库的分别有一个表 test_user,表结构一致,为便于说明,两个表中的数据是不一样的。
添加 MyBatis Plus 依赖
com.baomidou
mybatis-plus-boot-starter
3.3.0
主要添加以下几个包:
├─config ----------- // 数据源配置
├─controller ------- // web服务
├─entity ----------- // 实体类
│ ├─master
│ └─slave
├─mapper ----------- // dao操作类
│ ├─master
│ └─slave
└─vo --------------- // 视图返回对象
注:
Spring Boot 的默认配置文件是 application.properties ,由于有两个数据库配置,独立配置数据库是好的实践,因此添加配置文件 jbdc.properties ,添加以下自定义的主从数据库配置:
# master
spring.datasource.master.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.master.jdbc-url=jdbc:mysql://localhost:3306/mytest?useSSL=false&serverTimezone=GMT%2B8&characterEncoding=UTF-8
spring.datasource.master.username=root
spring.datasource.master.password=111111
# slave
spring.datasource.slave.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.slave.jdbc-url=jdbc:mysql://localhost:3306/my_test1?useSSL=false&serverTimezone=GMT%2B8&characterEncoding=UTF-8
spring.datasource.slave.username=root
spring.datasource.slave.password=111111
有了数据源连接信息,需要把数据源注入到 Spring 中。由于每个数据库使用独立的一套数据库连接,数据库连接使用的 SqlSession 进行会话连接,SqlSession 是由SqlSessionFactory 生成。因此,需要分别配置SqlSessionFactory 。以下操作均在 config 目录 下:
添加 **DataSourceConfig **配置文件,注入主从数据源
@Configuration
@PropertySource("classpath:config/jdbc.properties")
publicclass DatasourceConfig {
@Bean("master")
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource(){
return DataSourceBuilder.create().build();
}
@Bean("slave")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDataSource(){
return DataSourceBuilder.create().build();
}
}
添加 MasterMybatisConfig 配置文件,注入 Master 的SqlSessionFactory
@Configuration
@MapperScan(basePackages = "me.mason.demo.basicmultidatasource.mapper.master", sqlSessionFactoryRef = "masterSqlSessionFactory")
publicclass MasterMybatisConfig {
/**
* 注意,此处需要使用MybatisSqlSessionFactoryBean,不是SqlSessionFactoryBean,
* 否则,使用mybatis-plus的内置函数时就会报invalid bound statement (not found)异常
*/
@Bean("masterSqlSessionFactory")
public SqlSessionFactory masterSqlSessionFactory(@Qualifier("master") DataSource dataSource) throws Exception {
// 设置数据源
MybatisSqlSessionFactoryBean mybatisSqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
mybatisSqlSessionFactoryBean.setDataSource(dataSource);
//mapper的xml文件位置
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
String locationPattern = "classpath*:/mapper/master/*.xml";
mybatisSqlSessionFactoryBean.setMapperLocations(resolver.getResources(locationPattern));
//对应数据库的entity位置
String typeAliasesPackage = "me.mason.demo.basicmultidatasource.entity.master";
mybatisSqlSessionFactoryBean.setTypeAliasesPackage(typeAliasesPackage);
return mybatisSqlSessionFactoryBean.getObject();
}
}
添加 SlaveMybatisConfig 配置文件,注入 Slave 的 SqlSessionFactory
与(2)一致,把 master 改为 slave 即可。
在 MyBatis 配置中,实体设置 typeAliases 可以简化 xml 的配置,前面提到,使用 typeAliasesPackage 设置实体路径,在 entity 包下分别设置 master 和 slave 包,存放两个库对应的表实体,使用 Lombok 简化实体操作。如下:
@Data
@TableName("test_user")
publicclass MasterTestUser implements Serializable {
privatestaticfinallong serialVersionUID = 1L;
/** id */
private Long id;
/** 姓名 */
private String name;
...
}
在 mapper 包下,分别添加 master 和 slave 包,存放两个库对应的 Mapper ,由于 MyBatis Plus 本身已包含基本的 CRUD 操作,所以很多时候可以不用 xml 文件配置。若需要自定义操作,需要结合 xml 文件,与此同时需要指定对应的 xml 文件所在目录。如下:
@Repository
publicinterface MasterTestUserMapper extends BaseMapper {
/**
* 自定义查询
* @param wrapper 条件构造器
*/
List selectAll(@Param(Constants.WRAPPER)Wrapper wrapper);
}
slave 对应的 Mapper 与此类似
MyBatis Plus 的默认 mapper xml 文件路径为 classpath*:/mapper/**/*.xml,即 resources/mapper 下,同样设置 master 及 slave 目录,分别存放对应的 mapper xml 文件。以下是 master 的自定义操作:
经过上面的多套数据源配置,可知道,若需要操作哪个数据库,直接使用对应的 mapper 进行 CRUD 操作即可。如下为 Controller 中分别查询两个库,获取到的数据合在一起返回:
@RestController
@RequestMapping("/user")
publicclass TestUserController {
@Autowired
private MasterTestUserMapper masterTestUserMapper;
@Autowired
private SlaveTestUserMapper slaveTestUserMapper;
/**
* 查询全部
*/
@GetMapping("/listall")
public Object listAll() {
//master库,自定义接口查询
QueryWrapper queryWrapper = new QueryWrapper<>();
List resultData = masterTestUserMapper.selectAll(queryWrapper.isNotNull("name"));
//slave库,mp内置接口
List resultDataSlave = slaveTestUserMapper.selectList(null);
//返回
Map result = new HashMap<>();
result.put("master" , resultData);
result.put("slave" , resultDataSlave);
return ResponseResult.success(result);
}
}
至此,多数据源的实现已完成,当前示例是两个同构的数据库,当然,若是异构的数据库,或者多于两个的数据库,处理方式是一样的,只不过是把数据源增加一套而已。
由上述说明,我们可以总结一下使用多套数据源的方法进行多数据库操作,它的优缺点是什么。
正因为有上述的缺点,所以还有改进的空间。于是就有了动态数据源,至于动态数据源如何实现,下回分解。
本文对多个数据库的操作进行了初步探讨,并对使用多套源的策略进行讲解,结合主从代码示例,搭建了 Spring Boot + MyBatis Plus 工程,配置多数据源及使用 Mapper 进行多数据源操作,最后对多套数据源的优缺点进行总结。希望小伙伴们可以对多数据源操作有个初步印象。