动态数据源
在很多具体应用场景的时候,我们需要用到动态数据源的情况,比如多租户的场景,系统登录时需要根据用户信息切换到用户对应的数据库。又比如业务A要访问A数据库,业务B要访问B数据库等,都可以使用动态数据源方案进行解决。接下来,我们就来讲解如何实现动态数据源,以及在过程中剖析动态数据源背后的实现原理。
实现案例
本教程案例基于 Spring Boot + Mybatis + MySQL 实现。
数据库设计
首先需要安装好MySQL数据库,新建数据库 master,slave,分别创建用户表,用来测试数据源,SQL脚本如下。
复制代码
-- ----------------------------------------------------
--  用户
-- ----------------------------------------------------
--  Table structure for `sys_user`
-- ----------------------------------------------------
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
  `name` varchar(50) NOT NULL COMMENT '用户名',
  `password` varchar(100) COMMENT '密码',
  `salt` varchar(40) COMMENT '盐',
  `email` varchar(100) COMMENT '邮箱',
  `mobile` varchar(100) COMMENT '手机号',
  `status` tinyint COMMENT '状态  0:禁用   1:正常',
  `dept_id` bigint(20) COMMENT '机构ID',
  `create_by` varchar(50) COMMENT '创建人',
  `create_time` datetime COMMENT '创建时间',
  `last_update_by` varchar(50) COMMENT '更新人',
  `last_update_time` datetime COMMENT '更新时间',
  `del_flag` tinyint DEFAULT 0 COMMENT '是否删除  -1:已删除  0:正常',
  PRIMARY KEY (`id`),
  UNIQUE INDEX (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户';
复制代码
新建工程
新建一个Spring Boot工程,最终代码结构如下。
 
添加依赖
添加Spring Boot,Spring Aop,Mybatis,MySQL,Swagger相关依赖。Swagger方便用来测试接口。
pom.xml
复制代码
    4.0.0
    top.ivan.demo
    springboot-dynamic-datasource
    0.0.1-SNAPSHOT
    jar
    springboot-dynamic-datasource
    Demo project for Spring Boot
    
        org.springframework.boot
        spring-boot-starter-parent
        2.0.4.RELEASE
         
    
    
        UTF-8
        UTF-8
        1.8
        1.3.2
        2.8.0
    
    
           
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
        
            org.springframework.boot
            spring-boot-starter-aop
        
        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            ${mybatis.spring.version}
        
        
        
            mysql
            mysql-connector-java
        
        
        
            io.springfox
            springfox-swagger2
            ${swagger.version}
        
        
            io.springfox
            springfox-swagger-ui
            ${swagger.version}
        
    
    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    
复制代码
配置文件
修改配置文件,添加两个数据源,可以是同一个主机地址的两个数据库master,slave,也可是两个不同主机的地址,根据实际情况配置。
application.yml
复制代码
spring:
  datasource:
    master:
      driver-class-name: com.mysql.jdbc.Driver
      type: com.zaxxer.hikari.HikariDataSource
      jdbcUrl: jdbc:mysql://127.0.0.1:3306/master?useUnicode=true&zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=utf-8
      username: root
      password: 123
    slave:
      driver-class-name: com.mysql.jdbc.Driver
      type: com.zaxxer.hikari.HikariDataSource
      jdbcUrl: jdbc:mysql://127.0.0.1:3306/slave?useUnicode=true&zeroDateTimeBehavior=convertToNull&autoReconnect=true&characterEncoding=utf-8
      username: root
      password: 123
复制代码
启动类
启动类添加 exclude = {DataSourceAutoConfiguration.class}, 以禁用数据源默认自动配置。
数据源默认自动配置会读取 spring.datasource.* 的属性创建数据源,所以要禁用以进行定制。
@ComponentScan(basePackages = "com.louis.springboot") 是扫描范围,都知道不用多说。
DynamicDatasourceApplication.java
复制代码
package com.louis.springboot.dynamic.datasource;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
/**
 * 启动器
 * @author Louis
 * @date Oct 31, 2018
 */
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})    // 禁用数据源自动配置
@ComponentScan(basePackages = "com.louis.springboot")
public class DynamicDatasourceApplication {
    public static void main(String[] args) {
        SpringApplication.run(DynamicDatasourceApplication.class, args);
    }
}
复制代码
数据源配置类
创建一个数据源配置类,主要做以下几件事情:
1. 配置 dao,model,xml mapper文件的扫描路径。
2. 注入数据源配置属性,创建master、slave数据源。
3. 创建一个动态数据源,并装入master、slave数据源。
4. 将动态数据源设置到SQL会话工厂和事务管理器。
如此,当进行数据库操作时,就会通过我们创建的动态数据源去获取要操作的数据源了。
复制代码
package com.louis.springboot.dynamic.datasource.config;
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import com.louis.springboot.dynamic.datasource.dds.DynamicDataSource;
/**
 * Mybatis配置
 * @author Louis
 * @date Oct 31, 2018
 */
@Configuration
@MapperScan(basePackages = {"com.louis.**.dao"}) // 扫描DAO
public class MybatisConfig {
    @Bean("master")
    @Primary
    @ConfigurationProperties(prefix = "spring.datasource.master")
    public DataSource master() {
        return DataSourceBuilder.create().build();
    }
    @Bean("slave")
    @ConfigurationProperties(prefix = "spring.datasource.slave")
    public DataSource slave() {
        return DataSourceBuilder.create().build();
    }
    @Bean("dynamicDataSource")
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        Map