SpringBoot学习笔记:动态数据源切换

数据源

  Java的javax.sql.DataSource接口提供了一种处理数据库连接的标准方法。通常,DataSource使用URL和一些凭据来建立数据库连接。

  SpringBoot默认提供了针对内存数据库的数据源,如H2、hqldb、Derby等。

配置数据源信息

  当我们引入spring-boot-start-jdbc时,SpringBoot会默认使用其绑定的Tomcat-JDBC的数据源

  DataSource可由spring.datasource.*中的外部配置属性控制。例如,您可以在application.properties中声明以下部分:

spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

  当然,我们也可以是使用基于Java代码的配置方法:

    @Bean
public DataSource mysqlDataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/demo");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}

使用第三方数据源

  有时候,我们需要使用第三方的数据源,如Druid、C3P0等,在SpringBoot中也很简单。

  以Druid为例,首先添加Maven依赖:

<dependencies>
<!-- 添加MySQL依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 添加JDBC依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- 添加Druid依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
</dependencies>

  配置数据库连接信息即可:

@Configuration
public class DataSourceConfig { @Autowired
private Environment env; @Bean
public DataSource getDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl(env.getProperty("spring.datasource.url"));
dataSource.setUsername(env.getProperty("spring.datasource.username"));
dataSource.setPassword(env.getProperty("spring.datasource.password"));
return dataSource;
}
}

动态新增数据源

  有时候,我们需要在程序运行过程中动态增加或改变数据源。

改变数据源

  Spring2.0 引入了AbstractRoutingDataSource,该类可以充当DataSource的路由中介,能在运行时,动态切换当前数据源。如下为其核心代码,可见他是根据determineCurrentLookupKey()获取Key值,来从resolvedDataSources中找到要切换的数据源

private Map<Object, Object> targetDataSources;
private Map<Object, DataSource> resolvedDataSources; protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = this.determineCurrentLookupKey();
DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
} if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
} else {
return dataSource;
}
}

  而这个determineCurrentLookupKey函数需要我们自已来实现,所以我们要定义一个子类DynamicRoutingDataSource来继承AbstractRoutingDataSource,然后实现这个方法,其实就是返回一个Key字符串!。

  为了保证线程安全,我们可以把数据源的Key信息保存在ThreadLocal中,如下:

public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
@Override
protected String initialValue() {
return "dynamic_db0";
}
}; /**
* To switch DataSource
*
* @param key the key
*/
public static void setDataSourceKey(String key) {
contextHolder.set(key);
} /**
* Get current DataSource
*
* @return data source key
*/
public static String getDataSourceKey() {
return contextHolder.get();
} /**
* To set DataSource as default
*/
public static void clearDataSourceKey() {
contextHolder.remove();
} }

  这样,determineCurrentLookupKey就可以如下定义:

    @Override
protected Object determineCurrentLookupKey() {
System.out.println("Current DataSource is [{}]"+ DynamicDataSourceContextHolder.getDataSourceKey());
return DynamicDataSourceContextHolder.getDataSourceKey();
}

  所以我们在使用时,只需要使用DynamicDataSourceContextHolder.setDataSourceKey(),并可以切换数据源。

新增数据源

  新增数据源,其实就是更新targetDataSources字段,一个简单的Demo如下:

/**
* 动态增加数据源
*
* @param map 数据源属性
* @return
*/
public synchronized boolean addDataSource(Map<String, String> map) {
try {
String database = map.get("database");//获取要添加的数据库名
if (database==null||database.equals("")) return false;
if (DynamicRoutingDataSource.isExistDataSource(database)) return true;
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl(map.get("url"));
dataSource.setUsername(map.get("username"));
dataSource.setPassword(map.get("password")); Map<Object, Object> targetMap = DynamicRoutingDataSource.targetDataSources;
targetMap.put(database, dataSource);
// 当前 targetDataSources 与 父类 targetDataSources 为同一对象 所以不需要set
// this.setTargetDataSources(targetMap);
this.afterPropertiesSet();
} catch (Exception e) {
logger.error(e.getMessage());
return false;
}
return true;
}

配置动态数据源

  我们可以将其以Bean形式配置,并在其中设置默认数据源。

    @Bean("dynamicDataSource")
public DynamicRoutingDataSource dynamicDataSource() {
DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>();
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/demo");
dataSource.setUsername("root");
dataSource.setPassword("123456");
dataSourceMap.put("DEMO", dataSource);
dynamicRoutingDataSource.setDefaultTargetDataSource(dataSource);// 设置默认数据源
dynamicRoutingDataSource.setTargetDataSources(dataSourceMap);
return dynamicRoutingDataSource;
}

  之后,我们在代码中便可以执行新增数据源、切换数据源操作等等。

参考文档:

SpringBoot学习笔记:动态数据源切换的更多相关文章

  1. SpringBoot+Mybatis 实现动态数据源切换方案

    背景 最近让我做一个大数据的系统,分析了一下,麻烦的地方就是多数据源切换抽取数据.考虑到可以跨服务器跨数据库抽数,再整理数据,就配置了这个动态数据源的解决方案.在此分享给大家. 实现方案 数据库配置文 ...

  2. springboot学习笔记:9.springboot+mybatis+通用mapper+多数据源

    本文承接上一篇文章:springboot学习笔记:8. springboot+druid+mysql+mybatis+通用mapper+pagehelper+mybatis-generator+fre ...

  3. 【开发笔记】- AbstractRoutingDataSource动态数据源切换,AOP实现动态数据源切换

    AbstractRoutingDataSource动态数据源切换 上周末,室友通宵达旦的敲代码处理他的多数据源的问题,搞的非常的紧张,也和我聊了聊天,大概的了解了他的业务的需求.一般的情况下我们都是使 ...

  4. Spring主从数据库的配置和动态数据源切换原理

    原文:https://www.liaoxuefeng.com/article/00151054582348974482c20f7d8431ead5bc32b30354705000 在大型应用程序中,配 ...

  5. SpringBoot和Mycat动态数据源项目整合

    SpringBoot项目整合动态数据源(读写分离) 1.配置多个数据源,根据业务需求访问不同的数据,指定对应的策略:增加,删除,修改操作访问对应数据,查询访问对应数据,不同数据库做好的数据一致性的处理 ...

  6. SpringBoot学习笔记(7):Druid使用心得

    SpringBoot学习笔记(7):Druid使用心得 快速开始 添加依赖 <dependency> <groupId>com.alibaba</groupId> ...

  7. SpringBoot学习笔记(1):配置Mybatis

    SpringBoot学习笔记(1):配置Mybatis 反思:如果自己写的笔记自己都看不懂,那就不要拿出来丢人现眼! IDEA插件 Free MyBatis Plugin插件可以让我们的MyBatis ...

  8. 30个类手写Spring核心原理之动态数据源切换(8)

    本文节选自<Spring 5核心原理> 阅读本文之前,请先阅读以下内容: 30个类手写Spring核心原理之自定义ORM(上)(6) 30个类手写Spring核心原理之自定义ORM(下)( ...

  9. Java注解--实现动态数据源切换

    当一个项目中有多个数据源(也可以是主从库)的时候,我们可以利用注解在mapper接口上标注数据源,从而来实现多个数据源在运行时的动态切换. 实现原理 在Spring 2.0.1中引入了Abstract ...

随机推荐

  1. javascript之大文件分段上传、断点续传

    这里只写后端的代码,基本的思想就是,前端将文件分片,然后每次访问上传接口的时候,向后端传入参数:当前为第几块文件,和分片总数 下面直接贴代码吧,一些难懂的我大部分都加上注释了: 上传文件实体类: 看得 ...

  2. CF316G3 Good Substrings 广义后缀自动机

    太累了,刷刷水~ code: #include <bits/stdc++.h> #define N 500005 #define LL long long #define setIO(s) ...

  3. pt

    https://www.hdarea.co/torrents.php http://hdhome.org/torrents.php https://ourbits.club/torrents.php ...

  4. $\text{fhq-treap}$总结

    \(\text{fhq-treap}\)总结 又名范浩强\(\text{treap}\),是一种无旋\(\text{treap}\).其原理同\(\text{treap}\)一样都是通过维护一个随机堆 ...

  5. linux protobuf 测试官方例子遇到报错及解决办法。

    测试例子时出现报错如下,在最下面会写出安装流程. -------------------------------------报错----1------------------------------- ...

  6. 2019强网杯web upload writeup及关键思路

    <?phpnamespace app\web\controller; class Profile{    public $checker;    public $filename_tmp;    ...

  7. AtCoder Grand Contest 030 (AGC030) F - Permutation and Minimum 动态规划

    原文链接www.cnblogs.com/zhouzhendong/p/AGC030F.html 草率题解 对于每两个相邻位置,把他们拿出来. 如果这两个相邻位置都有确定的值,那么不管他. 然后把所有的 ...

  8. DevOps-如何构建持续交付流水线

    引言 DevOps 是一套实践方法,在保证高质量的前提下缩短系统变更从提交到部署至生产环境的时间,其中持续集成和持续交付是 DevOps 里面非常重要的一环.本文讲述了达到自动化持续交付需要做的准备工 ...

  9. 纯JavaScript开发飞机大战项目

    开发工具: HBuilder 编程语言:JavaScript 其他技术:Html + Css 项目截图:   视频:   源代码:   在线观看地址: (暂无) 百度网盘下载地址: 请加QQ群:915 ...

  10. 【阿里云IoT+YF3300】9.快速开发modbus设备驱动

    Modbus是一种串行通信协议,是莫迪康公司为PLC(编程逻辑控制器)通信而设计的协议.Modbus目前已经成为工业领域通信协议的业界标准,大部分的仪器仪表都支持该通信协议.很早以前就开发过基于Mod ...