本文简单的介绍一下基于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与动态多数据源切换的更多相关文章

  1. 170714、springboot编程之多数据源切换(动态)

    (1)新建maven java project; 新建一个maven project,取名为:spring-boot-multi-ds (2)在pom.xml添加依赖包: 在pom.xml文件中加入依 ...

  2. springboot2.0动态多数据源切换

    摘要:springboot1.x到springboot2.0配置变化有一点变化,网上关于springboot2.0配置多数据源的资料也比较少,为了让大家配置多数据源从springboot1.x升级到s ...

  3. springboot动态多数据源切换

    application-test.properties #datasource -- mysql multiple.datasource.master.url=jdbc:mysql://localho ...

  4. 170713、springboot编程之多数据源切换

    我们在开发过程中可能需要用到多个数据源,我们有一个项目(MySQL)就是和别的项目(SQL Server)混合使用了.其中SQL Server是别的公司开发的,有些基本数据需要从他们平台进行调取,那么 ...

  5. springboot下的多数据源切换

    今天在考虑如果分公司也用系统了,怎么办,是单独的数据库,还是一起使用?所以就想到了切换数据源来实现,但是发现,只是读写分离,还要再改一下,根据用户地域来切换数据源,今天先照着例子做一下. 看了好多文章 ...

  6. springboot中实现多数据源

    springboot中实现多数据源 1.什么场景需要多数据源 业务读写分离 业务分库 业务功能模块拆分多库 2.常见的多数据源的方案 按照数据源分别把mapper和entity放到不同的package ...

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

    SpringBoot学习笔记:动态数据源切换 数据源 Java的javax.sql.DataSource接口提供了一种处理数据库连接的标准方法.通常,DataSource使用URL和一些凭据来建立数据 ...

  8. Spring-Boot 多数据源配置+动态数据源切换+多数据源事物配置实现主从数据库存储分离

    一.基础介绍 多数据源字面意思,比如说二个数据库,甚至不同类型的数据库.在用SpringBoot开发项目时,随着业务量的扩大,我们通常会进行数据库拆分或是引入其他数据库,从而我们需要配置多个数据源. ...

  9. SpringBoot框架:通过AOP和自定义注解完成druid连接池的动态数据源切换(三)

    一.引入依赖 引入数据库连接池的依赖--druid和面向切面编程的依赖--aop,如下所示: <!-- druid --> <dependency> <groupId&g ...

随机推荐

  1. Java同步和异步过程中消息语言国际化处理策略

    在Java后端做消息内容的语言国际化处理时可以通过Spring中MessageSource接口的来实现,但是MessageSource接口需要用到Locale对象, 而Locale类又是根据前端传过来 ...

  2. UVA12325-注意提高效率的思路

    题目大意:你有一个体积为N的箱子和两种数量无限的宝物.宝物1的体积为S1,价值为V1:宝物2的体积为S2,价值为V2.输入均为32位带符号的整数.你的任务是最多能装多少价值的宝物?   方法:其实也没 ...

  3. sql server针对字符串型数字排序(针对此字符串的长度不一致)

    对于不规则的字符串数字排序,无法按照数字大的大小排序的原因是,字符串数字在数据库中按照ASCII码排序,从字符的第一个数字对比,首先就会将为首个数字相同的排在一起,在从这些字符串里面对比第二个数字,如 ...

  4. C++ lvalue(左值)和rvalue(右值)

    lvalue(左值)和rvalue(右值) 昨天写代码遇见一个这样的错误:{ "cannot bind non-const lvalue reference of type 'int& ...

  5. unity一些操作汇总

    设置父物体高度自适应子物体:父物体添加ContentSizeFitter,设置Horizeontal Fit和Vertical Fit为Preferred Size. ScrollView设置Cone ...

  6. Educational Codeforces Round 65 (Rated for Div. 2)B. Lost Numbers(交互)

    This is an interactive problem. Remember to flush your output while communicating with the testing p ...

  7. Python中numpy模块的简单使用

    # encoding:utf-8 import numpy as np data1 = np.array([1, 2, 3, 4, 5]) print(data1) data2 = np.array( ...

  8. [国家集训队] Crash的数字表格 - 莫比乌斯反演,整除分块

    考虑到\(lcm(i,j)=\frac{ij}{gcd(i,j)}\) \(\sum_{i=1}^n\sum_{j=1}^m\frac{ij}{gcd(i,j)}\) \(\sum_{d=1}^{n} ...

  9. Javascript 利用 switch 语句进行范围判断

    ; switch (true) { ): alert("less than five"); break; ): alert("between 5 and 8") ...

  10. -bash: mysqld: command not found

    网址:https://blog.csdn.net/zq199692288/article/details/78863737