SpringBoot学习笔记:动态数据源切换
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学习笔记:动态数据源切换的更多相关文章
- SpringBoot+Mybatis 实现动态数据源切换方案
背景 最近让我做一个大数据的系统,分析了一下,麻烦的地方就是多数据源切换抽取数据.考虑到可以跨服务器跨数据库抽数,再整理数据,就配置了这个动态数据源的解决方案.在此分享给大家. 实现方案 数据库配置文 ...
- springboot学习笔记:9.springboot+mybatis+通用mapper+多数据源
本文承接上一篇文章:springboot学习笔记:8. springboot+druid+mysql+mybatis+通用mapper+pagehelper+mybatis-generator+fre ...
- 【开发笔记】- AbstractRoutingDataSource动态数据源切换,AOP实现动态数据源切换
AbstractRoutingDataSource动态数据源切换 上周末,室友通宵达旦的敲代码处理他的多数据源的问题,搞的非常的紧张,也和我聊了聊天,大概的了解了他的业务的需求.一般的情况下我们都是使 ...
- Spring主从数据库的配置和动态数据源切换原理
原文:https://www.liaoxuefeng.com/article/00151054582348974482c20f7d8431ead5bc32b30354705000 在大型应用程序中,配 ...
- SpringBoot和Mycat动态数据源项目整合
SpringBoot项目整合动态数据源(读写分离) 1.配置多个数据源,根据业务需求访问不同的数据,指定对应的策略:增加,删除,修改操作访问对应数据,查询访问对应数据,不同数据库做好的数据一致性的处理 ...
- SpringBoot学习笔记(7):Druid使用心得
SpringBoot学习笔记(7):Druid使用心得 快速开始 添加依赖 <dependency> <groupId>com.alibaba</groupId> ...
- SpringBoot学习笔记(1):配置Mybatis
SpringBoot学习笔记(1):配置Mybatis 反思:如果自己写的笔记自己都看不懂,那就不要拿出来丢人现眼! IDEA插件 Free MyBatis Plugin插件可以让我们的MyBatis ...
- 30个类手写Spring核心原理之动态数据源切换(8)
本文节选自<Spring 5核心原理> 阅读本文之前,请先阅读以下内容: 30个类手写Spring核心原理之自定义ORM(上)(6) 30个类手写Spring核心原理之自定义ORM(下)( ...
- Java注解--实现动态数据源切换
当一个项目中有多个数据源(也可以是主从库)的时候,我们可以利用注解在mapper接口上标注数据源,从而来实现多个数据源在运行时的动态切换. 实现原理 在Spring 2.0.1中引入了Abstract ...
随机推荐
- Hive-2.3.6 安装
本安装依赖Haddop2.8安装 https://www.cnblogs.com/xibuhaohao/p/11772031.html 一.下载Hive与MySQL jdbc 连接驱动 apache- ...
- Distributed PostgreSQL on a Google Spanner Architecture – Storage Layer
转自:https://blog.yugabyte.com/distributed-postgresql-on-a-google-spanner-architecture-storage-layer/ ...
- C int类型的数组在内存中的地址示例
#include <stdio.h> int main(void){ int age[5] = {5,6,7,20,99}; return 0; } //转换后 /*(gdb) p &am ...
- 多路选择器,加法器原理及verilog实现
1.数据选择器是指经过选择,把多个通道的数据传到唯一的公共数据通道上.实现数据选择功能的逻辑电路称为数据选择器,它的作用相当于多个输入的单刀多掷开关.本例程以四选一数据选择器(电平触发)为例. 四选一 ...
- codevs 4028 EZ系列
4028 EZ系列之愤怒的一天 题目描述 Description 有一天,在某某教学楼某某课室某某课桌旁,某某某大声尖叫起来. 在那一瞬间,勇敢的丁畅大大站了出来,向某某某讨好,结果被揍得半死. ...
- 如何用elasticsearch构架亿级数据采集系统(第1集:非生产环境windows安装篇)
(一)做啥的? 基于Elasticsearch,可以为实现,大数据量(亿级)的实时统计查询的方案设计,提供底层数据框架. 本小节jacky会在非生产环境下,在 window 系统下,给大家分享着部分的 ...
- 判断是否是合法的IP地址
ipv4 import re #简单的匹配给定的字符串是否是ip地址,下面的例子它不是IPv4的地址,但是它满足正则表达式 if re.match(r"^(?:[0-9]{1,3}\.){3 ...
- GO语言Error处理
Go语言没有提供像Java.C#.Python语言中的try...catch异常处理方式,而是通过函数返回值逐层往上抛.好处就是避免漏掉本应处理的错误.坏处是代码啰嗦. 错误与异常区别 错误指的是可能 ...
- django celery 异步执行任务遇到的坑
部署后,任务没有持久化,所有用supervisor 进行进程管理 安装 pip install supervisor 创建 配置文件 [program:testplatform-flower] com ...
- SSH项目中使用struts-tags报错According to TLD or attribute directive in tag file, attribute test does not accept any expressions
在运行struts2标签页面时报错,代码如下:<%@ page language="java" pageEncoding="gbk"%><%@ ...