Spring Boot 2.0+ ByBatis-Plus动态切换数据源

Spring Boot 2.0+ ByBatis-Plus动态切换数据源

Laughing
2021-04-24 / 0 评论 / 1,213 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2024年03月18日,已超过341天没有更新,若内容或图片失效,请留言反馈。

前端时间做了一个SaaS的应用,也是第一次弄SaaS相关软件。系统设计的不同租户之前是数据库级别的隔离。所以核心的内容基本就是如何实现不同之间的数据源的切换。

创建模拟数据库

创建两个数据库,分别命名为dynamic_datasource_one和dynamic_datasource_two,两个数据库内分别创建两张表,分别名称为table_one和table_two。

数据库名表名
dynamic_datasource_onetable_one
dynamic_datasource_twotable_two

创建表的Sql如下:

create table table_one
(
    id bigint auto_increment
        primary key,
    name varchar(30) null
);
INSERT INTO table_one (id, name) VALUES (1, 'dynamic_datasource_one');
create table table_two
(
    id bigint auto_increment
        primary key,
    name varchar(30) null
);

INSERT INTO table_two (id, name) VALUES (1, 'dynamic_datasource_two');

创建工程

创建工程就不多说了,主要是添加mybatis-plus依赖就好了。

 <!--        添加MyBatis-plus依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus</artifactId>
            <version>3.4.1</version>
        </dependency>

修改配置文件

配置两个数据库的连接信息,我这里放到配置文件中,如果是多租户的,也可以从数据库里面获取数据库连接信息。

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/dynamic_datasource_one?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
  type-aliases-package: net.xiangcaowuyu.DynamicDataSource.doamin
  mapper-locations: classpath:mapper/*Mapper.xml

添加配置文件

yaml中的配置信息映射到类上,方便使用。

创建MyBatisConfig.java

@Configuration
@EnableTransactionManagement
public class MyBatisConfig {

    private final Logger logger = LoggerFactory.getLogger(MyBatisConfig.class);

    /**
     * 数据源1
     * @return
     */
    public DataSource dynamicDataSourceOne( ) {
        return     DataSourceBuilder
                .create()
                .url("jdbc:mysql://localhost:3306/dynamic_datasource_one?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true")
                .username("root")
                .password("123456")
                .driverClassName("com.mysql.jdbc.Driver")
                .build();
    }

    /**
     * 数据源2
     * @return
     */
    public DataSource dynamicDataSourceTwo() {
        return     DataSourceBuilder
                .create()
                .url("jdbc:mysql://localhost:3306/dynamic_datasource_two?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&allowMultiQueries=true")
                .username("root")
                .password("123456")
                .driverClassName("com.mysql.jdbc.Driver")
                .build();
    }

    @Bean("dynamicDataSource")
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        DataSource dynamicDataSourceOne = dynamicDataSourceOne();
        DataSource dynamicDataSourceTwo = dynamicDataSourceTwo();
        Map<Object,Object> dynamicDataSourceMap = new HashMap<>();
        dynamicDataSourceMap.put("dynamicDataSourceOne",dynamicDataSourceOne);
        dynamicDataSourceMap.put("dynamicDataSourceTwo",dynamicDataSourceTwo);
        DynamicDataSourceContextHolder.addDataSourceKeys(dynamicDataSourceMap.keySet());
        dynamicDataSource.setTargetDataSources(dynamicDataSourceMap);
        //必须执行此操作,才会重新初始化AbstractRoutingDataSource 中的 resolvedDataSources,也只有这样,动态切换才会起效
        dynamicDataSource.afterPropertiesSet();
        return dynamicDataSource;
    }

}

创建DataSourceContextHolder存储当前数据源

public class DynamicDataSourceContextHolder {

    private static final  ThreadLocal<String> contextHolder = new ThreadLocal<String>(){
        @Override
        protected String initialValue(){
            return "dynamicDataSourceOne";
        }
    };

    public static List<Object> dataSourceKeyList = new ArrayList<>();

    public static void setDataSourceKey(String key) throws Exception {
        if(!dataSourceKeyList.contains(key)){
            throw new Exception("数据库连接不存在");
        }
        contextHolder.set(key);
    }

    public static String getDataSourceKey(){
        return contextHolder.get();
    }

    public static void clearDataSourceKey(String key){
        contextHolder.remove();
    }

    /**
     * 添加数据库连接键
     * @param keys
     */
    public static void addDataSourceKeys(Collection<Object> keys){
        dataSourceKeyList.addAll(keys);
    }

}

创建DynamicDataSource

配置每次执行sql时,调用的数据源。

/**
 * 动态数据源
 */
public class DynamicDataSource extends AbstractRoutingDataSource {

    /**
     * 设置数据源Key值来切换数据,每次执行sql会先调用这个方法
     * @return
     */
    @Override
    protected Object determineCurrentLookupKey() {
        return DynamicDataSourceContextHolder.getDataSourceKey();
    }

}

测试

创建Mapper

TableOneMapper

@Mapper
public interface TableOneMapper extends BaseMapper<TableOne> {
}

TableTwoMapper

@Mapper
public interface TableTwoMapper extends BaseMapper<TableOne> {
}

创建controller

@RestController
@RequestMapping("/")
public class DynamicDatSourceController {

    private final Logger logger = LoggerFactory.getLogger(DynamicDatSourceController.class);

    @Resource
    TableOneMapper tableOneMapper;

    @Resource
    TableTwoMapper tableTwoMapper;

    @GetMapping
    public void test() throws Exception {
        logger.info("准备查询数据源1的数据");
        logger.info(tableOneMapper.selectList(new QueryWrapper<>()).get(0).getName());
        logger.info("准备查询数据源2的数据");
        DynamicDataSourceContextHolder.setDataSourceKey("dynamicDataSourceTwo");
        logger.info(tableTwoMapper.selectList(new QueryWrapper<>()).get(0).getName());
    }

}
0

评论 (0)

取消
  1. 头像
    靖哥哥
    Windows 7 · FireFox

    测试下,谢谢

    回复