SpringBoot 动态数据源

核心类AbstractRoutingDataSource

  • 核心类AbstractRoutingDataSource

继承该类实现determineCurrentLookupKey方法来决定数据源

package com.tom.datasource;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import java.util.Map;

/**
 * 扩展 Spring 的 AbstractRoutingDataSource 抽象类,重写 determineCurrentLookupKey 方法
 * 动态数据源
 * determineCurrentLookupKey() 方法决定使用哪个数据源
 */
public class DynamicDataSourceContext extends AbstractRoutingDataSource {


    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    /**
     * 动态刷新数据源列表,//TODO 生产开发时需要优化
     *
     * @param targetDataSources
     */
    public void freshDataSources(Map<Object, Object> targetDataSources) {
        //设置一个默认数据
        super.setDefaultTargetDataSource(targetDataSources.get(DbConsts.DEFAULT_KEY));
        //设置所有数据源
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return getDataSource();
    }

    public static void setDataSource(String dataSource) {
        CONTEXT_HOLDER.set(dataSource);
    }

    public static String getDataSource() {
        return CONTEXT_HOLDER.get();
    }

    public static void clearDataSource() {
        CONTEXT_HOLDER.remove();
    }

}

  • 初始化数据源
public class DbConsts {
    public static final String DEFAULT_KEY="default_db";
    public static final String DEFAULT_DB2="tenant_db2";
    public static final String DEFAULT_DB3="tenant_db3";
}


/**
 * 配置多数据源
 */
@Configuration
public class DynamicDataSourceConfig {



    @Bean
    @Primary
    public DynamicDataSourceContext dataSource() {
        Map<Object, Object> targetDataSources = getDataSources();
        DynamicDataSourceContext dynamicDataSourceContext = new DynamicDataSourceContext();
        dynamicDataSourceContext.freshDataSources(targetDataSources);
        return dynamicDataSourceContext;
    }


    /**
     * 构建初始化的数据源
     * @return
     */
    public Map<Object, Object> getDataSources() {
        DataSourceItem ds1 = DataSourceItem.builder().key(DbConsts.DEFAULT_KEY).url("jdbc:mysql://localhost:3306/db1?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8").username("root").password("123456").driverClassName("com.mysql.jdbc.Driver").build();
        DataSourceItem ds2 = DataSourceItem.builder().key(DbConsts.DEFAULT_DB2).url("jdbc:mysql://localhost:3306/db2?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8").username("root").password("123456").driverClassName("com.mysql.jdbc.Driver").build();
        Map<Object, Object> map = new HashMap<>();
        map.put(DbConsts.DEFAULT_KEY, buildDataSource(ds1));
        map.put(DbConsts.DEFAULT_DB2, buildDataSource(ds2));
        return map;
    }

    /**
     * 构建 新增的数据源
     * @return
     */
    public Map<Object, Object> getDataSourcesNew() {
        DataSourceItem ds1 = DataSourceItem.builder().key(DbConsts.DEFAULT_KEY).url("jdbc:mysql://localhost:3306/db1?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8").username("root").password("123456").driverClassName("com.mysql.jdbc.Driver").build();
        DataSourceItem ds2 = DataSourceItem.builder().key(DbConsts.DEFAULT_DB2).url("jdbc:mysql://localhost:3306/db2?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8").username("root").password("123456").driverClassName("com.mysql.jdbc.Driver").build();
        DataSourceItem ds3 = DataSourceItem.builder().key(DbConsts.DEFAULT_DB3).url("jdbc:mysql://localhost:3306/db3?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8").username("root").password("123456").driverClassName("com.mysql.jdbc.Driver").build();
        Map<Object, Object> map = new HashMap<>();
        map.put(DbConsts.DEFAULT_KEY, buildDataSource(ds1));
        map.put(DbConsts.DEFAULT_DB2, buildDataSource(ds2));
        map.put(DbConsts.DEFAULT_DB3, buildDataSource(ds3));
        return map;
    }

    /**
     * 构建数据源
     *
     * @param item
     * @return
     */
    private DataSource buildDataSource(DataSourceItem item) {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl(item.getUrl());
        dataSource.setUsername(item.getUsername());
        dataSource.setPassword(item.getPassword());
        dataSource.setDriverClassName(item.getDriverClassName());
        return dataSource;

    }

}

UserController

@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    @Autowired
    DynamicDataSourceContext dataSource;
    @Autowired
    DynamicDataSourceConfig config;

    @GetMapping("/find1")
    public User find1() {
        //不设置数据源,则使用默认数据源
        User user = userService.getById(1);
        System.out.println(user);
        return user;
    }

    @GetMapping("/find2")
    public User find2() {
        //设置数据源为db3
        DynamicDataSourceContext.setDataSource(DbConsts.DEFAULT_DB2);
        User user = userService.getById(2);
        System.out.println(user);
        return user;
    }

    @GetMapping("/find3")
    public User find3() {
        //设置数据源为db3
        DynamicDataSourceContext.setDataSource(DbConsts.DEFAULT_DB3);
        User user = userService.getById(3);
        System.out.println(user);
        return user;
    }

    /**
     * 刷新数据源
     *
     * @return
     */
    @GetMapping("/freshDb")
    public int freshDb() {
        Map<Object, Object> dataSourcesNew = config.getDataSourcesNew();
        dataSource.freshDataSources(dataSourcesNew);
        return 1;
    }
}

db 准备

建立三个数据源db1,db2,db3
并往三个数据源里添加数据

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` bigint NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

-- ----------------------------
-- db1
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'user1_db1');
INSERT INTO `user` VALUES ('2', 'user2_db1');
INSERT INTO `user` VALUES ('3', 'user3_db1');


-- ----------------------------
-- db2
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'user1_db2');
INSERT INTO `user` VALUES ('2', 'user2_db2');
INSERT INTO `user` VALUES ('3', 'user3_db2');

-- ----------------------------
-- db3
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'user1_db3');
INSERT INTO `user` VALUES ('2', 'user2_db3');
INSERT INTO `user` VALUES ('3', 'user3_db3');

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

效果

http://localhost:8080/user/find1

{"id":1,"name":"user1_db1"}

http://localhost:8080/user/find3
这里因为初始化默认的数据库只有db1,db2,所以此时 DynamicDataSourceContext.setDataSource(DbConsts.DEFAULT_DB3);是设置了一个不存在的数据源,也会从默认数据源中读取;

{"id":3,"name":"user3_db1"}

执行刷新数据源后:
http://localhost:8080/user/freshDb
再访问http://localhost:8080/user/find3 可以看到数据源是从db3读取回来的;

{"id":3,"name":"user3_db3"}

代码仓库地址:https://gitee.com/zwbeyond/dynamic-data-source.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值