搞定SpringBoot多数据源(1):多套源策略

tags: multi-datasource java springbootjava


一句话归纳:Spring Boot开发中链接多个数据库进行读写操做,使用多套数据源是最直接、简单的方式。

1. 引言

在开发过程当中,避免不了须要同时操做多个数据库的状况,一般的应用场景以下 :mysql

  • 数据库高性能场景:主从,包括一主一从,一主多从等,在主库进行增删改操做,在从库进行读操做。
  • 数据库高可用场景:主备,包括一往一备,多主多备等,在数据库没法访问时能够切换。
  • 同构或异构数据的业务处理:须要处理的数据存储在不一样的数据库中,包括同构(如都是 MySQL ),异构(如一个MySQL ,另外是 PG 或者 Oracle )。

使用 Spring Boot 该如何处理多个数据库的读写,通常有如下几种策略:git

  • 多套数据源:即针对一个数据库创建一套数据处理逻辑,每套数据库都包括数据源配置、会话工厂( sessionFactory )、链接、SQL 操做、实体。各套数据库相互独立。
  • 动态数据源:肯定数量的多个数据源共用一个会话工厂,根据条件动态选取数据源进行链接、SQL 操做。
  • 参数化变动数据源:根据参数添加数据源,并进行数据源切换,数据源数量不肯定。一般用于对多个数据库的管理工做。

本系列文章“搞定SpringBoot多数据源”将针对以上几个策略进行描述,本文是第一篇:“多套数据源”,主要以主从场景为实例,结合代码,对多套数据源的实现进行描述,内容包括搭建 Spring Boot + MyBatis Plus 工程、多数据源配置、多数据源处理与使用逻辑。github

本文所涉及到的示例代码:https://github.com/mianshenglee/my-example/tree/master/multi-datasource,读者可结合一块儿看。web

2. 运行环境

  • JAVA 运行环境: JDK1.8
  • Spring Boot: 2.2.2.RELEASE
  • MyBatis Plus: 3.3.0
  • 开发IDE: IDEA
  • 构建工具Maven: 3.3.9
  • MySQL : 5.6.26
  • Lombok: 1.18.10

3. 多套数据源

多套数据源,顾名思义每个数据库有一套独立的操做。从下往上,数据库、会话工厂、DAO操做,服务层都是独立的一套,以下所示:spring

多套数据源

本示例中,以一主一从两个数据库,两数据库的分别有一个表 test_user,表结构一致,为便于说明,两个表中的数据是不同的。两个表结构可在示例代码中的 sql 目录中获取。sql

3.1 搭建 Spring Boot 工程

3.1.1 初始化 Spring Boot 工程

使用 spring.io构建初始 Spring Boot 工程,选用如下几个构件:数据库

  • Lombok: 用于简化操做
  • Spring Configuration Processor:配置文件处理器
  • Spring Web: 用于构建web服务
  • MySQL Driver: 数据库驱动

3.1.2 添加 MyBatis Plus 依赖

MyBatis Plus 是对 MyBatis 加强,简化 DAO 操做,提升数据库操做效率。依赖以下:apache

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.3.0</version>
</dependency>

3.1.3 添加包结构

主要添加如下几个包:后端

├─config ---------------------------------- // 数据源配置
├─controller ------------------------------ // web服务
├─entity ---------------------------------- // 实体类
│ ├─master 
│ └─slave 
├─mapper ---------------------------------- // dao操做类
│ ├─master 
│ └─slave 
└─vo -------------------------------------- // 视图返回对象

注:

  • 因为示例简单,省略service层
  • 实体类及mapper均根据主从进行划分

3.2 多套数据源

3.2.1 独立数据库链接信息

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

3.2.2 多套数据源配置

有了数据源链接信息,须要把数据源注入到 Spring 中。因为每一个数据库使用独立的一套数据库链接,数据库链接使用的 SqlSession 进行会话链接,SqlSession 是由SqlSessionFactory 生成。所以,须要分别配置SqlSessionFactory 。如下操做均在 config 目录 下:

(1)添加 DataSourceConfig 配置文件,注入主从数据源

@Configuration
@PropertySource("classpath:config/jdbc.properties")
public class 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();
    }
}
  • 注解 PropertySource 指定配置信息文件
  • 注解 ConfigurationProperties 指定主从配置前缀
  • 分别指定主从数据源的 bean 名称为 masterslave

(2)添加 MasterMybatisConfig 配置文件,注入 Master 的SqlSessionFactory

@Configuration
@MapperScan(basePackages = "me.mason.demo.basicmultidatasource.mapper.master", sqlSessionFactoryRef = "masterSqlSessionFactory")
public class 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();
    }
}
  • 注解 MapperScan 指定那些包下的 mapper 使用本数据源,并指定使用哪一个SqlSessionFactory,注意,此处的 sqlSessionFactoryRef 即本配置中的注入的 SqlSessionFactory
  • 设置指定的数据源,此处是名为 master 的数据源,使用 Qualifier 指定。
  • MyBatis Plus 对应的 Mapper 如有自定义的 mapper.xml, 则使用 setMapperLocations 指定。
  • 若须要对实体进行别名处理,则使用 setTypeAliasesPackage 指定。

(3)添加 SlaveMybatisConfig 配置文件,注入Slave 的 SqlSessionFactory

与(2)一致,把 master 改成 slave便可。

3.2.3 多套实体

在 MyBatis 配置中,实体设置 typeAliases 能够简化 xml 的配置,前面提到,使用 typeAliasesPackage 设置实体路径,在 entity 包下分别设置 masterslave 包,存放两个库对应的表实体,使用 Lombok 简化实体操做。以下:

@Data
@TableName("test_user")
public class MasterTestUser implements Serializable {
    private static final long serialVersionUID = 1L;
    /** id */
    private Long id;
    /** 姓名 */
    private String name;
    ...
}

3.2.4 多套Mapper操做

mapper 包下,分别添加 masterslave 包,存放两个库对应的 Mapper ,因为 MyBatis Plus 自己已包含基本的 CRUD 操做,因此不少时候能够不用 xml 文件配置。若须要自定义操做,须要结合 xml文件,与此同时须要指定对应的 xml 文件所在目录。以下:

@Repository
public interface MasterTestUserMapper extends BaseMapper<MasterTestUser> {
    /**
     * 自定义查询
     * @param wrapper 条件构造器
     */
    List<MasterTestUser> selectAll(@Param(Constants.WRAPPER)Wrapper<MasterTestUser> wrapper);
}

slave对应的Mapper与此相似

3.2.5 多套 mapper xml 文件

MyBatis Plus 的默认mapper xml 文件路径为 classpath*:/mapper/**/*.xml,即 resources/mapper 下,一样设置 masterslave 目录,分别存放对应的mapper xml 文件。如下是 master 的自定义操做:

<mapper namespace="me.mason.demo.basicmultidatasource.mapper.master.MasterTestUserMapper">
    <select id="selectAll" resultType="masterTestUser">
        select * from test_user
        <if test="ew!=null">
          ${ew.customSqlSegment}
        </if>
    </select>
</mapper>

3.3 多数据源使用

通过上面的多套数据源配置,可知道,若须要操做哪一个数据库,直接使用对应的 mapper 进行 CRUD 操做便可。以下为 Controller 中分别查询两个库,获取到的数据合在一块儿返回:

@RestController
@RequestMapping("/user")
public class TestUserController {

    @Autowired
    private MasterTestUserMapper masterTestUserMapper;
    @Autowired
    private SlaveTestUserMapper slaveTestUserMapper;
    /**
     * 查询所有
     */
    @GetMapping("/listall")
    public Object listAll() {
        //master库,自定义接口查询
        QueryWrapper<MasterTestUser> queryWrapper = new QueryWrapper<>();
        List<MasterTestUser> resultData = masterTestUserMapper.selectAll(queryWrapper.isNotNull("name"));
        //slave库,mp内置接口
        List<SlaveTestUser> resultDataSlave = slaveTestUserMapper.selectList(null);
        //返回
        Map<String, Object> result = new HashMap<>();
        result.put("master" , resultData);
        result.put("slave" , resultDataSlave);
        return ResponseResult.success(result);
    }

}
  • 使用Autowired注解注入对应的mapper
  • 使用对应数据库的 mapper 进行业务操做
  • 根据业务在数据库中执行相应的操做,如主只作增删改操做、从只读操做

至此,多数据源的实现已完成,当前示例是两个同构的数据库,固然,如果异构的数据库,或者多于两个的数据库,处理方式是同样的,只不过是把数据源增长一套而已。

4. 优缺点

由上述说明,咱们能够总结一下使用多套数据源的方法进行多数据库操做,它的优缺点是什么。

4.1 优势

  • 简单、直接:一个库对应一套处理方式,很好理解。
  • 符合开闭原则( OCP ):开发的设计模式告诉咱们,对扩展开放,对修改关闭,添加多一个数据库,原来的那一套不须要改动,只添加便可。

4.2 缺点

  • 资源浪费:针对每个数据源写一套操做,链接数据库的资源也是独立的,分别占用一样多的资源。SqlSessionFactory 是一个工厂,建议是使用单例,彻底能够重用,不须要创建多个,只须要更改数据源便可,跟多线程,使用线程池减小资源消耗是同一道理。
  • 代码冗余:在前面的多数据源配置中能够看出,其实 master 和 slave 的不少操做是同样的,只是改个名称而已,所以会形成代码冗余。
  • 缺少灵活:全部须要使用的地方都须要引入对应的mapper,对于不少操做,只是选择数据源的不同,代码逻辑是一致的。另外,对于一主多从的状况,若须要对多个从库进行负载均衡,相对比较麻烦。

正由于有上述的缺点,因此还有改进的空间。因而就有了动态数据源,至于动态数据源如何实现,下回分解。

5. 总结

本文对多个数据库的操做进行了初步探讨,并对使用多套源的策略进行讲解,结合主从代码示例,搭建了 Spring Boot + MyBatis Plus 工程,配置多数据源及使用 Mapper 进行多数据源操做,最后对多套数据源的优缺点进行总结。但愿小伙伴们能够对多数据源操做有个初步印象。

本文有配套的示例代码,有兴趣的能够跑一下示例来感觉一下。

参考资料

往期文章

个人公众号(搜索Mason技术记录),获取更多技术记录:

mason

相关文章
相关标签/搜索