springboot,多数据源切换

这篇具有很好参考价值的文章主要介绍了springboot,多数据源切换。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

需求介绍:

        要求做一个平台,有其他第三方系统接入;每个系统有自己的数据源配置,通过调用平台接口,实现将数据保存到第三方自己的数据库中;

实现过程:

        1.在平台项目运行时,通过接口获取每个第三方系统的数据源;以key-value的形式保存到全局变量中;

        2.在调用接口的时候,会通过拦截器获取到每个请求中的第三方系统标识;

        3.根据标识来切换对应的数据源

一、提供一个公共类,保存第三方系统的数据源信息

public class ThreadSystemDataSourceInfo {
    //数据源类型:mongodb, mysql, es
    private String dataType;
    //连接
    private String url;
    //用户名
    private String username;
    //密码
    private String password;
    //用来作为key存储租户配置
    private String datasourceId;
}

二、提供一个接口,用来模拟查询第三方系统的数据源

public List<ThreadSystemDataSourceInfo> listDataSourceInfo() {
    List<ThreadSystemDataSourceInfo> list = new ArrayList<>();
    ThreadSystemDataSourceInfo info = new ThreadSystemDataSourceInfo();
    info.setDataType("mysql");
    info.setUrl("url");
    info.setUsername("root");
    info.setPassword("123456");
    info.setDatasourceId("1");

    ThreadSystemDataSourceInfo info1 = new ThreadSystemDataSourceInfo();
    info1.setDataType("mysql");
    info1.setUrl("url1");
    info1.setUsername("root");
    info1.setPassword("123456");
    info.setDatasourceId("2");

    list.add(info);
    list.add(info1);

    return list;
}

三、系统的开发

1、配置yml,系统要配置自己的数据库
spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    username: root
    password: 123456
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      connection-test-query: SELECT 'x' FROM DUAL
      pool-name: HikariPool
      maximum-pool-size: 20
      connection-timeout: 10000
      validation-timeout: 3000
      minimum-idle: 10
      idle-timeout: 30000
      max-lifetime: 600000

mybatis:
  mapper-locations: classpath*:mapper/**/*Mapper.xml
  configuration:
    map-underscore-to-camel-case: true #打开驼峰
2、创建数据源对象ThreadSystemDataSource

数据库连接池用的是kicari文章来源地址https://www.toymoban.com/news/detail-604409.html

public class ThreadSystemDataSource extends AbstractRoutingDataSource {
    private boolean debug = true;
    private final Logger log = LoggerFactory.getLogger(getClass());
    private Map<Object, Object> targetDataSources;
    private Object dynamicDefaultTargetDataSource;
    private static final String defaultClassDriver = "com.mysql.cj.jdbc.Driver";

    @Override
    protected Object determineCurrentLookupKey() {
        String datasourceId = MysqlContextHolder.getDataSourceId();
        if (!StringUtils.isEmpty(datasourceId)) {
            Map<Object, Object> dynamicTargetDataSources2 = this.targetDataSources;
            if (dynamicTargetDataSources2.containsKey(datasourceId)) {
                log.info("---当前数据源:" + datasourceId + "---");
            } else {
                log.info("不存在的数据源:");
                return null;
//                    throw new ADIException("不存在的数据源:"+datasource,500);
            }
        } else {
            log.info("---当前数据源:默认数据源---");
        }

        return datasourceId;
    }

    // 创建数据源
    public boolean createDataSource(String key, String url, String username, String password) {
        try {
            String driveClass = this.defaultClassDriver;

            if (!testDatasource(url, username, password)) {
                log.error("数据源配置有错误,url:{},username:{},password:{}", url, username, password);
                return false;
            }

            HikariConfig hikariConfig = new HikariConfig();
            // 连接池连接信息
//            hikariConfig.setMinimumIdle(mininum);
//            hikariConfig.setMaximumPoolSize(maximum);
//            hikariConfig.setMaxLifetime(lifeTime);
//            hikariConfig.setConnectionTimeout(connectionTimeOut);
//            hikariConfig.setValidationTimeout(validationTimeOut);
//            hikariConfig.setIdleTimeout(idleTimeOut);
//            hikariConfig.setPoolName(poolName);
//            hikariConfig.setConnectionTestQuery(testQuery);
            // 基础连接信息
            hikariConfig.setJdbcUrl(url);
            hikariConfig.setUsername(username);
            hikariConfig.setPassword(password);
            hikariConfig.setDriverClassName(driveClass);
            HikariDataSource datasource = new HikariDataSource(hikariConfig);

            this.targetDataSources.put(key, datasource);

            // 将map赋值给父类的TargetDataSources
            setTargetDataSources(this.targetDataSources);
            // 将TargetDataSources中的连接信息放入resolvedDataSources管理
            super.afterPropertiesSet();
            log.info(key + "数据源初始化成功");

            return true;
        } catch (Exception e) {
            log.error(e + "");
            return false;
        }
    }


    @Override
    public void setDefaultTargetDataSource(Object defaultTargetDataSource) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        this.dynamicDefaultTargetDataSource = defaultTargetDataSource;
    }
    
    @Override
    public void setTargetDataSources(Map<Object, Object> targetDataSources) {
        super.setTargetDataSources(targetDataSources);
        this.targetDataSources = targetDataSources;
    }


    public void setDebug(boolean debug) {
        this.debug = debug;
    }

    
    public boolean testDatasource(String url, String username, String password) {
        try {
            String driveClass = this.defaultClassDriver;
            Class.forName(driveClass);
            DriverManager.getConnection(url, username, password);
            return true;
        } catch (Exception e) {
            return false;
        }
    }


    private void createDataSource(ThreadSystemDataSourceInfo dataSource) {
        String datasourceId = dataSource.getDatasourceId();
        log.info("准备创建数据源" + datasourceId);
        String username = dataSource.getUsername();
        String password = dataSource.getPassword();
        String url = dataSource.getUrl();

        boolean result = this.createDataSource(datasourceId, url, username, password);
        if (!result) {
            log.error("数据源" + datasourceId + "配置正确,但是创建失败");
        }
    }
}
3、创建配置类
@Configuration
public class HikariDBConfig {
    @Value("${spring.datasource.url}")
    private String url;
    @Value("${spring.datasource.username}")
    private String username;
    @Value("${spring.datasource.password}")
    private String password;
    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;
    // 连接池连接信息
    @Value("${spring.datasource.hikari.maximum-pool-size}")
    private int maximum;
    @Value("${spring.datasource.hikari.connection-timeout}")
    private int connectionTimeOut;
    @Value("${spring.datasource.hikari.validation-timeout}")
    private int validationTimeOut;
    @Value("${spring.datasource.hikari.idle-timeout}")
    private int idleTimeOut;
    @Value("${spring.datasource.hikari.max-lifetime}")
    private int lifeTime;
    @Value("${spring.datasource.hikari.pool-name}")
    private String poolName;
    @Value("${spring.datasource.hikari.connection-test-query}")
    private String testQuery;

    /**
     * 主数据源,信息从配置文件获得
     * @return
     * @throws SQLException
     */
    @Bean
    @Primary // 在同样的DataSource中,首先使用被标注的DataSource
    @Qualifier("mainDataSource")
    public DataSource dataSource() throws SQLException {
        HikariConfig hikariConfig = new HikariConfig();
        // 连接池连接信息
        hikariConfig.setMaximumPoolSize(maximum);
        hikariConfig.setMaxLifetime(lifeTime);
        hikariConfig.setConnectionTimeout(connectionTimeOut);
        hikariConfig.setValidationTimeout(validationTimeOut);
        hikariConfig.setIdleTimeout(idleTimeOut);
        hikariConfig.setPoolName(poolName);
        hikariConfig.setConnectionTestQuery(testQuery);
        // 基础连接信息
        hikariConfig.setJdbcUrl(this.url);
        hikariConfig.setUsername(username);
        hikariConfig.setPassword(password);
        hikariConfig.setDriverClassName(driverClassName);
        HikariDataSource datasource = new HikariDataSource(hikariConfig);

        return datasource;
    }

    /**
     * 动态获取的数据源,初始化的时候,默认只有主数据源
     * @return
     * @throws SQLException
     */
    @Bean(name = "threadSystemDataSource")
    @Qualifier("threadSystemDataSource")
    public ThreadSystemDataSource threadDataSource() throws SQLException {
        ThreadSystemDataSource threadDataSource = new ThreadSystemDataSource();
        threadDataSource.setDebug(false);
        //配置缺省的数据源
        // 默认数据源配置 DefaultTargetDataSource
        threadDataSource.setDefaultTargetDataSource(dataSource());
        Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
        //额外数据源配置 TargetDataSources
        targetDataSources.put("mainDataSource", dataSource());
        threadDataSource.setTargetDataSources(targetDataSources);
        return tenantDataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(threadDataSource());
        //解决手动创建数据源后字段到bean属性名驼峰命名转换失效的问题
        sqlSessionFactoryBean.setConfiguration(configuration());

        // 设置mybatis的主配置文件
         ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        // Resource mybatisConfigXml = resolver.getResource("classpath:mybatis/mybatis-config.xml");
        //  sqlSessionFactoryBean.setConfigLocation(mybatisConfigXml);
        // 设置别名包
        //  sqlSessionFactoryBean.setTypeAliasesPackage("com.testdb.dbsource.pojo");

        // 手动配置mybatis的mapper.xml资源路径,如果单纯使用注解方式,不需要配置该行
         sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
        return sqlSessionFactoryBean.getObject();
    }

    @Bean
    @ConfigurationProperties(prefix = "mybatis.configuration")
    public org.apache.ibatis.session.Configuration configuration() {
        return new org.apache.ibatis.session.Configuration();
    }
}
4、上下文工具类
public class MysqlContextHolder {
    private final static Logger log = LoggerFactory.getLogger(MysqlContextHolder.class);
    // 对当前线程的操作-线程安全的
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    // 调用此方法,切换数据源
    public static void setDataSourceId(String dataSource) {
        contextHolder.set(dataSource);
        log.info("已切换到数据源:{}",dataSource);
    }

    // 获取数据源
    public static String getDataSourceId() {
        return contextHolder.get();
    }

    // 删除数据源
    public static void clearDataSource() {
        contextHolder.remove();
        log.info("已切换到主数据源");
    }
}
5、存放数据源的工具类
public class ThreadSystemDataSourceLocalMapUtil {
    //存放初始化各个租户,保存的配置信息
    private static final Map<String, ThreadSystemDataSourceInfo> dataSourceInfoMap = new ConcurrentHashMap<>();

    public static Map<String, ThreadSystemDataSourceInfo> getDataSourceInfoMap() {
        return dataSourceInfoMap;
    }


    public static void putTenantDataSource(String key, ThreadSystemDataSourceInfo dataSourceInfo) {
        dataSourceInfoMap.put(key, dataSourceInfo);

    }


    public static TenantDataSourceInfo getDataSource(String key) {
        return dataSourceInfoMap.get(key);
    }


    public static String getTenantDataSourceType(String key){
        ThreadSystemDataSourceInfo dataSourceInfo = dataSourceInfoMap.get(key);
        if(dataSourceInfo!=null){
            return dataSourceInfo.getDataType();
        }
        return null;
    }
}
6、初始化第三方系统的数据源
@Component
public class InitDataSource implements CommandLineRunner {
    @Autowired
    private ITenantList iTenantList;

    @Autowired
    private ThreadSystemDataSource tenantDataSource;

    /**
     * 获取租户数据源配置,创建数据源
     * @param args
     * @throws Exception
     */
    @Override
    public void run(String... args) throws Exception {
        //获取租户数据源配置
        List<ThreadSystemDataSourceInfo> dataSourceInfos = iTenantList.listDataSourceInfo();

        if(CollectionUtil.isNotEmpty(dataSourceInfos)){
            //初始化数据源
            for (ThreadSystemDataSourceInfo info : dataSourceInfos) 
                    tenantDataSource.createDataSource(info.getDatasourceId(), info.getUrl(), info.getUsername(), info.getPassword());

                ThreadSystemDataSourceLocalMapUtil.putTenantDataSource(info.getDatasourceId(), info);
            }
        }
    }
}
7、开始使用
@Service
public class TenantLogServiceForMysqlImpl {

    @Resource
    private SystemMapper systemMapper;

 
    public R saveOperLog(String tenantId) {
        if(ThreadSystemDataSourceLocalMapUtil.getDataSource(key) == null){
            //没有配置,则使用默认数据源
            MysqlContextHolder.clearDataSource();
        }else{
             //根据tenantId作为key,来切换数据源
             MysqlContextHolder.setDataSourceId(key);       
        }
        
        systemMapper.insertOperlog(operLog);
        return R.ok();
    }
}

到了这里,关于springboot,多数据源切换的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • SpringBoot——动态数据源(多数据源自动切换)

    日常的业务开发项目中只会配置一套数据源,如果需要获取其他系统的数据往往是通过调用接口, 或者是通过第三方工具比如kettle将数据同步到自己的数据库中进行访问。 但是也会有需要在项目中引用多数据源的场景。比如如下场景: 自研数据迁移系统,至少需要新、老两

    2024年02月16日
    浏览(39)
  • SpringBoot动态切换数据源

      Spring提供一个DataSource实现类用于动态切换数据源—— AbstractRoutingDataSource pom.xml 大概的项目结构 注意:这两个事务管理器,并不能处理分布式事务 链接:https://pan.baidu.com/s/1ymxeKYkI-cx7b5nTQX0KWQ  提取码:6bii  --来自百度网盘超级会员V4的分享                

    2024年02月06日
    浏览(49)
  • springboot,多数据源切换

    需求介绍:         要求做一个平台,有其他第三方系统接入;每个系统有自己的数据源配置,通过调用平台接口,实现将数据保存到第三方自己的数据库中; 实现过程:         1.在平台项目运行时,通过接口获取每个第三方系统的数据源;以key-value的形式保存到全局

    2024年02月16日
    浏览(38)
  • SpringBoot从数据库读取数据数据源配置信息,动态切换数据源

            首先准备多个数据库,主库smiling-datasource,其它库test1、test2、test3         接下来,我们在主库smiling-datasource中,创建表databasesource,用于存储多数据源相关信息。表结构设计如下         创建好表之后,向表databasesource中存储test1、test2、test3三个数据库的相关配置

    2024年01月16日
    浏览(64)
  • 【Spring Boot 3】【数据源】自定义多数据源

    软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历中,每次学习新技术总是要花费或多或少的时间、检索不止一篇资料才能得出一个可工作的DEMO,这占用了我大量的时

    2024年02月01日
    浏览(60)
  • 【Spring Boot 3】【数据源】自定义JPA数据源

    软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历中,每次学习新技术总是要花费或多或少的时间、检索不止一篇资料才能得出一个可工作的DEMO,这占用了我大量的时

    2024年01月21日
    浏览(70)
  • 【Spring Boot】Spring Boot整合多数据源

    在实际的开发工作中,我们经常会遇到需要整合多个数据源的情况,比如同时连接多个数据库、读写分离、跨数据库查询等。本文将介绍如何使用Spring Boot来实现多数据源的整合,对于刚刚接触开发的小伙伴可能有一些帮助。 在一个应用程序中使用多个数据源意味着我们需要

    2024年02月10日
    浏览(42)
  • 【Spring Boot 3】【数据源】自定义JDBC多数据源

    软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历中,每次学习新技术总是要花费或多或少的时间、检索不止一篇资料才能得出一个可工作的DEMO,这占用了我大量的时

    2024年01月23日
    浏览(49)
  • 【Spring Boot 3】【数据源】自定义JPA多数据源

    软件开发是一门实践性科学,对大多数人来说,学习一种新技术不是一开始就去深究其原理,而是先从做出一个可工作的DEMO入手。但在我个人学习和工作经历中,每次学习新技术总是要花费或多或少的时间、检索不止一篇资料才能得出一个可工作的DEMO,这占用了我大量的时

    2024年01月22日
    浏览(80)
  • 一种实现Spring动态数据源切换的方法

    不在现有查询代码逻辑上做任何改动,实现dao维度的数据源切换(即表维度) 节约bdp的集群资源。接入新的宽表时,通常uat验证后就会停止集群释放资源,在对应的查询服务器uat环境时需要查询的是生产库的表数据(uat库表因为bdp实时任务停止,没有数据落入),只进行服务

    2024年02月09日
    浏览(53)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包