SpringBoot动态数据源配置
SpringBoot动态数据源配置
序:数据源动态切换流程图如下:

1:pom.xml文件依赖声明
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2:application.yml配置文件
配置多个数据源信息
server:
port: 9000
dynamic:
datasource:
ds1: # 数据源1
jdbc-url: jdbc:mysql://localhost:3306/test1?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
ds2: # 数据源2
jdbc-url: jdbc:mysql://localhost:3306/test2?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=GMT%2B8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
3:数据源的配置到Spring管理
/**
* description 数据源配置类
*
* @author liekkas 2021/08/21 12:33
*/
@Configuration
public class DynamicDataSourceConfig {
@Bean(name = "dataSource1")
@ConfigurationProperties(prefix = "dynamic.datasource.ds1")
public DataSource dateSource1() {
return DataSourceBuilder.create().build();
}
@Bean(name = "dataSource2")
@ConfigurationProperties(prefix = "dynamic.datasource.ds2")
public DataSource dateSource2() {
return DataSourceBuilder.create().build();
}
@Bean
@Primary
public DynamicRoutingDataSource dynamicDataSource() {
DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource();
//设置默认的数据源
dynamicRoutingDataSource.setDefaultTargetDataSource(dateSource1());
Map<Object, Object> targetDataSources = new HashMap<>(4);
targetDataSources.put("dataSource1", dateSource1());
targetDataSources.put("dataSource2", dateSource2());
//设置数据源
dynamicRoutingDataSource.setTargetDataSources(targetDataSources);
dynamicRoutingDataSource.afterPropertiesSet();
DynamicDataSourceContextHolder.dataSourceSet.addAll(targetDataSources.keySet());
return dynamicRoutingDataSource;
}
}
注意:@ConfigurationProperties注解的prefix的值和配置文件application.yml必须要保持一致
3:配置数据源上下文
我们需要新建一个数据源上下文,用户记录当前线程使用的数据源是什么,以及记录所有注册成功的数据源的集合。对于线程级别的私有变量,我们首先ThreadLocal来实现。
/**
* description 设置当前线程数据源上下文类
*
* @author liekkas 2021/08/21 12:37
*/
public class DynamicDataSourceContextHolder {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);
/**
* 存储已经注册的数据源的key
*/
public static Set<Object> dataSourceSet = new HashSet<>();
/**
* 线程级别的私有变量
*/
private static final ThreadLocal<String> HOLDER = new ThreadLocal<>();
public static String getDataSourceRouterKey() {
return HOLDER.get();
}
public static void setDataSourceRouterKey(String dataSourceRouterKey) {
logger.info("当前数据源正在切换中......");
HOLDER.set(dataSourceRouterKey);
}
/**
* 设置数据源之前一定要先移除
*/
public static void removeDataSourceRouterKey() {
HOLDER.remove();
}
/**
* 判断指定dataSource当前是否存在
*
* @param dataSource dataSource
* @return boolean
*/
public static boolean isExistDataSource(Object dataSource) {
return dataSourceSet.contains(dataSource);
}
}
4:动态数据源路由
第三步我们以及新建了数据源上下文,用于存储我们当前线程的数据源,那么怎么通知spring用当前的数据源,spring提供一个接口,名为AbstractRoutingDataSource的抽象类,我们只需要重写determineCurrentLookupKey方法就可以,这个方法看名字就知道,就是返回当前线程的数据源,那我们只需要从我们刚刚的数据源上下文中取出当前数据源即可
/**
* description 数据源动态路由
*
* @author liekkas 2021/08/21 12:39
*/
public class DynamicRoutingDataSource extends AbstractRoutingDataSource {
private static final Logger logger = LoggerFactory.getLogger(DynamicRoutingDataSource.class);
@Override
protected Object determineCurrentLookupKey() {
String dataSourceName = DynamicDataSourceContextHolder.getDataSourceRouterKey();
logger.info("当前数据源是:{}", dataSourceName);
return DynamicDataSourceContextHolder.getDataSourceRouterKey();
}
}:
5:数据源动态切换注解
/**
* description 动态切换数据源注解
* 该注解可以同时作用于类和方法上,如果类和方法注解同时存在,则方法的注解优先级高于类上的注解优先级
* 也就是说类和方法同时存在该注解的话,则会使用方法的数据源
*
* @author liekkas 2021/08/21 12:40
*/
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String value() default "dataSource1";
}:
6:利用aop实现数据源动态切换
/**
* description 动态切换数据源切面
*
* @author liekkas 2021/08/21 12:42
*/
@Aspect
@Component
@Order(-1)
public class DynamicDataSourceAspect {
private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);
/**
* 同时拦截标注DataSource的注解类上或者方法
*/
@Pointcut(value = "@within(com.liekkas.config.datasource.DataSource) || @annotation(com.liekkas.config.datasource.DataSource)")
public void datasource() {
}
@Before("datasource()")
public void changeDataSource(JoinPoint joinPoint) {
Class<?> klass = joinPoint.getTarget().getClass();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
DataSource dataSource;
if (method.isAnnotationPresent(DataSource.class)) {
dataSource = method.getAnnotation(DataSource.class);
} else {
dataSource = klass.getAnnotation(DataSource.class);
}
//判断注解中的数据源是否在已注册的数据源中
if (DynamicDataSourceContextHolder.isExistDataSource(dataSource.value())) {
DynamicDataSourceContextHolder.setDataSourceRouterKey(dataSource.value());
logger.info("数据源设置为 > {}", dataSource.value());
} else {
throw new RuntimeException("数据源[" + dataSource.value() + "]不存在");
}
}
@After("datasource()")
public void restoreDataSource() {
DynamicDataSourceContextHolder.removeDataSourceRouterKey();
}
}
7:测试
7.1:controller
/**
* description
*
* @author liekkas 2021/01/09 15:47
*/
@RestController
public class PersonController {
@Resource
private PersonService personService;
@GetMapping("person")
public List<Person> findPerson() {
return personService.findPerson();
}
@GetMapping("student")
public List<Student> findStudent() {
return personService.findStudent();
}
}
7.2: service
public interface PersonService {
/**
* findPerson
*
* @return list
*/
List<Person> findPerson();
/**
* findStudent
*
* @return list
*/
List<Student> findStudent();
}
7.3: serviceImpl
@Service
@DataSource("dataSource1")
public class PersonServiceImpl implements PersonService {
@Resource
private PersonMapper personMapper;
@Override
public List<Person> findPerson() {
return personMapper.findPerson();
}
@Override
@DataSource("dataSource2")
public List<Student> findStudent() {
return personMapper.findStudent();
}
}
由于篇幅关系,mapper以及实体类不再描述
测试接口调用


至此数据源动态切换已经完成。
小结:数据源动态切换主要原理就是实现了Spring提供的AbstractRoutingDataSource类,然后我们就可以根据自己的需要利用aop和自定义注解,ThreadLocal来实现动态切换使用哪一个数据源了。
SpringBoot动态数据源配置的更多相关文章
- Springboot+Druid 动态数据源配置监控
一.引入maven依赖,使用 starter 与原生 druid 依赖配置有所不同 <dependency> <groupId>com.alibaba</groupId& ...
- SpringBoot动态数据源
1.原理图 2.创建枚举类 /** * 存数据源key值 */ public enum DataSourceKey { master,salve,migration } 3.创建自定义注解类 /** ...
- Druid动态数据源配置
上文已经讲了单个数据源的Druid的配置(http://www.cnblogs.com/nbfujx/p/7686634.html) Druid动态数据源配置 主要是继承AbstractRouting ...
- Spring Boot2.x 动态数据源配置
原文链接: Spring Boot2.x 动态数据源配置 基于 Spring Boot 2.x.Spring Data JPA.druid.mysql 的动态数据源配置Demo,适合用于数据库的读写分 ...
- Springboot 多数据源配置,结合tk-mybatis
一.前言 作为一个资深的CRUD工程师,我们在实际使用springboot开发项目的时候,难免会遇到同时使用多个数据库的情况,比如前脚刚查询mysql,后脚就要查询sqlserver. 这时,我们很直 ...
- Spring-Boot 多数据源配置+动态数据源切换+多数据源事物配置实现主从数据库存储分离
一.基础介绍 多数据源字面意思,比如说二个数据库,甚至不同类型的数据库.在用SpringBoot开发项目时,随着业务量的扩大,我们通常会进行数据库拆分或是引入其他数据库,从而我们需要配置多个数据源. ...
- Springboot多数据源配置--数据源动态切换
在上一篇我们介绍了多数据源,但是我们会发现在实际中我们很少直接获取数据源对象进行操作,我们常用的是jdbcTemplate或者是jpa进行操作数据库.那么这一节我们将要介绍怎么进行多数据源动态切换.添 ...
- springboot+多数据源配置
作者:纯洁的微笑 出处:http://www.ityouknow.com/ 起多数据源,一般都来解决那些问题呢,主从模式或者业务比较复杂需要连接不同的分库来支持业务.我们项目是后者的模式,网上找了很多 ...
- Spring Boot + Mybatis多数据源和动态数据源配置
文章转自 https://blog.csdn.net/neosmith/article/details/61202084 网上的文章基本上都是只有多数据源或只有动态数据源,而最近的项目需要同时使用两种 ...
- 基于注解实现SpringBoot多数据源配置
1.功能介绍 在实际的开发中,同一个项目中使用多个数据源是很常见的场景.最近在学习的过程中使用注解的方式实现了一个Springboot项目多数据源的功能.具体实现方式如下. 2.在applicatio ...
随机推荐
- 开源相机管理库Aravis例程学习(四)——multiple-acquisition-signal
目录 简介 例程代码 函数说明 g_main_loop_new g_main_loop_run g_main_loop_quit g_signal_connect arv_stream_set_emi ...
- Stable Diffusion中的embedding
Stable Diffusion中的embedding 嵌入,也称为文本反转,是在 Stable Diffusion 中控制图像样式的另一种方法.在这篇文章中,我们将学习什么是嵌入,在哪里可以找到它们 ...
- 修改element,vant,mint等ui框架的样式
vant和mint移动端常见,引入单独的css文件,在main.js中引入下即可,直接在对应的vue文件的css通过控制台查看中修改也行,再不济加!important element: 1.vue框架 ...
- gorm 返回的 *DB 说明
RecordNotFound 跟在查询的后面(Find/First),bool true:没有查到记录 false:查到记录 Error 跟在修改(create/update)的后面,如果错误就会报错 ...
- 密码学—仿射密码Python程序
文章目录 仿射密码 加密算法 解密算法 仿射密码 古典密码,且属于单表加密. 加密算法 仿射密码公式 c = m×k + b mod 26 c是密文,m是明文,m作为26字母中的明文,因此计算出来的密 ...
- Spring源码阅读 ------------------- SpringFrameWork 5.2 术语理解(三)
一.一定要理解的概念 1.控制反转 对象A和对象B,对象A中需要new 一个对象B,但是,现在需要对象A,不在自己内部new 对象B,把new 对象B的权限交给第三方(IOC框架),操作的过程,就是控 ...
- AIRIOT物联网低代码平台如何配置OPC UA驱动?
AIRIOT物联网低代码平台支持驱动类型丰富,驱动包括但不限于通用驱动信息,无线驱动信息,行业驱动信息及各个厂商驱动.驱动稳定性强,经受住各个大型项目考验,持续稳定运行. AIRIOT物联网低代码平台 ...
- WPF摄像头使用(WPFMediaKit)
添加WPFMediaKit引用 使用WPFMediaKit操作摄像头需要安装WPFMediaKit相关的Nuget包.选中需要进行摄像头操作的项目,然后通过Nuget安装即可. 页面代码 引入命名空间 ...
- 全网最全100个AI工具导航网站合集
随着ChatGPT年前的爆火,人工智能也变成当今最热门的领域之一,它正在改变着我们的生活和工作方式.无论你是想要学习人工智能的基础知识,还是想要利用人工智能来提升你的业务效率和创新能力,都需要找到合适 ...
- 分享5款.NET开源免费的Redis客户端组件库
前言 今天大姚给大家分享5款.NET开源.免费的Redis客户端组件库,希望可以帮助到有需要的同学. StackExchange.Redis StackExchange.Redis是一个基于.NET的 ...