SpringBoot与动态多数据源切换
本文简单的介绍一下基于SpringBoot框架动态多数据源切换的实现,采用主从配置的方式,配置master、slave两个数据库。
一、配置主从数据库

spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
druid:
# 主库数据源
master:
url: jdbc:mysql://localhost:3306/practice?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: 123456
# 从库数据源
slave:
# 从数据源开关/默认关闭
enabled: true
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: 123456

二、创建数据源枚举类

public enum DataSourceType {
/**
* 主库
*/
MASTER, /**
* 从库
*/
SLAVE
}

三、数据源切换处理
创建一个数据源切换处理类,有对数据源变量的获取、设置和清空的方法。其中的ThreadLocal用于保存某个线程共享变量。详细的ThreadLocal的相关了解,可以查看地址:https://www.cnblogs.com/slivelove/p/10950527.html

public class DynamicDataSourceContextHolder {
public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class); /**
* 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>(); /**
* 设置数据源变量
* @param dataSourceType
*/
public static void setDataSourceType(String dataSourceType){
log.info("切换到{}数据源", dataSourceType);
CONTEXT_HOLDER.set(dataSourceType);
} /**
* 获取数据源变量
* @return
*/
public static String getDataSourceType(){
return CONTEXT_HOLDER.get();
} /**
* 清空数据源变量
*/
public static void clearDataSourceType(){
CONTEXT_HOLDER.remove();
}
}

四、继承AbstractRoutingDataSource
动态切换数据源主要依靠AbstractRoutingDataSource。创建一个AbstractRoutingDataSource的子类,重写determineCurrentLookupKey方法,用于决定使用哪一个数据源。这里主要用到AbstractRoutingDataSource的两个属性defaultTargetDataSource和targetDataSources。defaultTargetDataSource默认目标数据源,targetDataSources(map类型)存放用来切换的数据源。

public class DynamicDataSource extends AbstractRoutingDataSource { public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
super.setDefaultTargetDataSource(defaultTargetDataSource);
super.setTargetDataSources(targetDataSources);
// afterPropertiesSet()方法调用时用来将targetDataSources的属性写入resolvedDataSources中的
super.afterPropertiesSet();
} /**
* 根据Key获取数据源的信息
*
* @return
*/
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceType();
}
}

五、注入数据源

@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.druid.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
} @Bean
@ConfigurationProperties("spring.datasource.druid.slave")
@ConditionalOnProperty(prefix = "spring.datasource.druid.slave", name = "enabled", havingValue = "true")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
} @Bean(name = "dynamicDataSource")
@Primary
public DynamicDataSource dataSource(DataSource masterDataSource, DataSource slaveDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);
targetDataSources.put(DataSourceType.SLAVE.name(), slaveDataSource);
return new DynamicDataSource(masterDataSource, targetDataSources);
}
}

六、自定义多数据源切换注解
设置拦截数据源的注解,可以设置在具体的类上,或者在具体的方法上。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
/**
* 切换数据源名称
*/
DataSourceType value() default DataSourceType.MASTER;
}

七、AOP拦截类的实现
通过拦截上面的注解,在其执行之前处理设置当前执行SQL的数据源的信息,CONTEXT_HOLDER.set(dataSourceType)这里的数据源信息从我们设置的注解上面获取信息,如果没有设置就是用默认的数据源的信息。

@Aspect
@Order(1)
@Component
public class DataSourceAspect {
private Logger log = LoggerFactory.getLogger(getClass()); @Pointcut("@annotation(com.wlfu.common.annotation.DataSource)")
public void dsPointCut() { } @Around("dsPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
DataSource dataSource = method.getAnnotation(DataSource.class);
if (dataSource != null) {
DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());
}
try {
return point.proceed();
} finally {
// 销毁数据源 在执行方法之后
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
}

这里使用@Around,在调用目标方法前,进行aop拦截,通过解析注解上的值来切换数据源。在调用方法结束后,清除数据源。也可以使用@Before和@After来编写,原理一样,这里就不多说了。
八、使用切换数据源注解
设置拦截数据源的注解,可以设置在具体的类上,或者在具体的方法上。

@DataSource(value = DataSourceType.SLAVE)
@PostMapping("/list")
@ResponseBody
public TableDataInfo list() { }
SpringBoot与动态多数据源切换的更多相关文章
- 170714、springboot编程之多数据源切换(动态)
(1)新建maven java project; 新建一个maven project,取名为:spring-boot-multi-ds (2)在pom.xml添加依赖包: 在pom.xml文件中加入依 ...
- springboot2.0动态多数据源切换
摘要:springboot1.x到springboot2.0配置变化有一点变化,网上关于springboot2.0配置多数据源的资料也比较少,为了让大家配置多数据源从springboot1.x升级到s ...
- springboot动态多数据源切换
application-test.properties #datasource -- mysql multiple.datasource.master.url=jdbc:mysql://localho ...
- 170713、springboot编程之多数据源切换
我们在开发过程中可能需要用到多个数据源,我们有一个项目(MySQL)就是和别的项目(SQL Server)混合使用了.其中SQL Server是别的公司开发的,有些基本数据需要从他们平台进行调取,那么 ...
- springboot下的多数据源切换
今天在考虑如果分公司也用系统了,怎么办,是单独的数据库,还是一起使用?所以就想到了切换数据源来实现,但是发现,只是读写分离,还要再改一下,根据用户地域来切换数据源,今天先照着例子做一下. 看了好多文章 ...
- springboot中实现多数据源
springboot中实现多数据源 1.什么场景需要多数据源 业务读写分离 业务分库 业务功能模块拆分多库 2.常见的多数据源的方案 按照数据源分别把mapper和entity放到不同的package ...
- SpringBoot学习笔记:动态数据源切换
SpringBoot学习笔记:动态数据源切换 数据源 Java的javax.sql.DataSource接口提供了一种处理数据库连接的标准方法.通常,DataSource使用URL和一些凭据来建立数据 ...
- Spring-Boot 多数据源配置+动态数据源切换+多数据源事物配置实现主从数据库存储分离
一.基础介绍 多数据源字面意思,比如说二个数据库,甚至不同类型的数据库.在用SpringBoot开发项目时,随着业务量的扩大,我们通常会进行数据库拆分或是引入其他数据库,从而我们需要配置多个数据源. ...
- SpringBoot框架:通过AOP和自定义注解完成druid连接池的动态数据源切换(三)
一.引入依赖 引入数据库连接池的依赖--druid和面向切面编程的依赖--aop,如下所示: <!-- druid --> <dependency> <groupId&g ...
随机推荐
- ansible-yaml语法
1. 基础语法 - yaml结果通过空格来展示 - 数组使用"- "来表示 //注意:-后边要有一个空格 - 键值对使用": "来表示 //注意::后边要有一个 ...
- Cloud保存时提示消息是否保存,点是保存,点否不保存。
业务场景:保存时,检查上游的销售出库单数量,和发货通知单数量是否一致,不一致时提示信息,点是则保存,点否不保存. using System;using System.Collections.Gener ...
- Docker最全教程——从理论到实战(二十一)
前言 MySQL是目前最流行的开源的关系型数据库,MySQL的容器化之前有朋友投稿并且写过此块,本篇仅从笔者角度进行总结和编写. 目录 镜像说明 运行MySQL容器镜像 1.运行MySQL容器 ...
- selenium--find_element_by_xpath()方法汇总
一.从根目录/开始 有点像Linux的文件查看,/代表根目录,一级一级的查找,直接子节点,相当于css_selector中的>号/html/body/div/p 二.根据元素属性选择: 查找具体 ...
- Winform递归绑定树节点
/// <summary> /// 绑定树节点 /// </summary> /// <param name="pid"></param& ...
- 函数节流-歪说js
歪谈js 起因: 夜深人静,月朗星稀.'window.onresize 事件' 与 '浏览器'在大战300回合,console.log('1') 1s 十次,然后就结束了,一个悲伤的故事. 事实证明太 ...
- Mac下怎么更新nodejs
Windows下更新nodejs最简单粗暴的应该就是去官网下载安装包安装了,昨天项目中需要更新nodejs我便找了一下相关文章,发现有一个 n 模块可以管理node,n 是Node的一个模块,所以安装 ...
- Pi和e的积分
Evaluate integral $$\int_{0}^{1}{x^{-x}(1-x)^{x-1}\sin{\pi x}dx}$$ Well,I think we have $$\int_{0}^{ ...
- [HNOI2017] 大佬 - DP,BFS,Hash,单调性
这真的是一道综合题.然而感觉A得莫名其妙,交上去的时候其实非常虚,然后就莫名其妙地AC了? 首先我们考虑到,所有和怼有关的操作都是时刻无关的.也就是说,我们把这些操作拆散放到任何时候都对结果不会有影响 ...
- How to do high impact research + 实事求是
1. develop a strong publications record early, so do what you can to make that happen. 2. 粗读:abstrac ...