一、分析AbstractRoutingDataSource抽象类源码

关注import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource以下变量

@Nullable

private Map targetDataSources; // 目标数据源

@Nullable

private Object defaultTargetDataSource; // 默认目标数据源

@Nullable

private Map resolvedDataSources; // 解析的数据源

@Nullable

private DataSource resolvedDefaultDataSource; // 解析的默认数据源

这两组变量是相互对应的,在熟悉多实例数据源切换代码的不难发现,当有多个数据源的时候,一定要指定一个作为默认的数据源;

在这里也同理,当同时初始化多个数据源的时候,需要显式的调用setDefaultTargetDataSource方法指定一个作为默认数据源;

我们需要关注的是Map targetDataSources和Map resolvedDataSources;

targetDataSources是暴露给外部程序用来赋值的,而resolvedDataSources是程序内部执行时的依据,因此会有一个赋值的操作;

根据这段源码可以看出,每次执行时,都会遍历targetDataSources内的所有元素并赋值给resolvedDataSources;这样如果我们在外部程序新增一个新的数据源,都会添加到内部使用,从而实现数据源的动态加载。

继承该抽象类的时候,必须实现一个抽象方法:protected abstract Object determineCurrentLookupKey(),该方法用于指定到底需要使用哪一个数据源。

二、实现多数据源切换和动态数据源加载

A - 配置文件信息

application.yml文件

server:

port: 18080

spring:

datasource:

type: com.alibaba.druid.pool.DruidDataSource

druid:

# 主数据源

master-db:

driverClassName: com.mysql.jdbc.Driver

url: jdbc:mysql://192.168.223.129:13306/test_master_db?characterEncoding=utf-8

username: josen

password: josen

# 从数据源

slave-db:

driverClassName: com.mysql.jdbc.Driver

url: jdbc:mysql://192.168.223.129:13306/test_slave_db?characterEncoding=utf-8

username: josen

password: josen

mybatis:

mapper-locations: classpath:mapper/*.xml

logging:

path: ./logs/mydemo20201105.log

level:

com.josen.mydemo20201105: debug

maven 依赖

org.springframework.boot

spring-boot-starter-aop

com.alibaba

druid-spring-boot-starter

1.1.10

org.springframework.boot

spring-boot-starter-web

org.mybatis.spring.boot

mybatis-spring-boot-starter

2.1.3

mysql

mysql-connector-java

runtime

org.projectlombok

lombok

true

B - 编码实现

1、创建一个DynamicDataSource类,继承AbstractRoutingDataSource抽象类,实现determineCurrentLookupKey方法,通过该方法指定当前使用哪个数据源;

2、 在DynamicDataSource类中通过ThreadLocal维护一个全局数据源名称,后续通过修改该名称实现动态切换;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import javax.sql.DataSource;

import java.util.Map;

/**

* @ClassName DynamicDataSource

* @Description 设置动态数据源

* @Author Josen

* @Date 2020/11/5 14:28

**/

public class DynamicDataSource extends AbstractRoutingDataSource {

// 通过ThreadLocal维护一个全局唯一的map来实现数据源的动态切换

private static final ThreadLocal contextHolder = new ThreadLocal<>();

public DynamicDataSource(DataSource defaultTargetDataSource, Map targetDataSources) {

super.setDefaultTargetDataSource(defaultTargetDataSource);

super.setTargetDataSources(targetDataSources);

super.afterPropertiesSet();

}

/**

* 指定使用哪一个数据源

*/

@Override

protected Object determineCurrentLookupKey() {

return getDataSource();

}

public static void setDataSource(String dataSource) {

contextHolder.set(dataSource);

}

public static String getDataSource() {

return contextHolder.get();

}

public static void clearDataSource() {

contextHolder.remove();

}

}

3、创建DynamicDataSourceConfig类,引入application.yaml配置的多个数据源信息,构建多个数据源;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.Primary;

import org.springframework.stereotype.Component;

import javax.sql.DataSource;

import java.util.HashMap;

import java.util.Map;

/**

* @ClassName DynamicDataSourceConfig

* @Description 引入动态数据源,构建数据源

* @Author Josen

* @Date 2020/11/5 14:23

**/

@Configuration

@Component

public class DynamicDataSourceConfig {

/**

* 读取application配置,构建master-db数据源

*/

@Bean

@ConfigurationProperties("spring.datasource.druid.master-db")

public DataSource myMasterDataSource(){

return DruidDataSourceBuilder.create().build();

}

/**

* 读取application配置,构建slave-db数据源

*/

@Bean

@ConfigurationProperties("spring.datasource.druid.slave-db")

public DataSource mySlaveDataSource(){

return DruidDataSourceBuilder.create().build();

}

/**

* 读取application配置,创建动态数据源

*/

@Bean

@Primary

public DynamicDataSource dataSource(DataSource myMasterDataSource, DataSource mySlaveDataSource) {

Map targetDataSources = new HashMap<>();

targetDataSources.put("master-db",myMasterDataSource);

targetDataSources.put("slave-db", mySlaveDataSource);

// myMasterDataSource=默认数据源

// targetDataSources=目标数据源(多个)

return new DynamicDataSource(myMasterDataSource, targetDataSources);

}

}

4、 创建MyDataSource自定义注解,后续AOP通过该注解作为切入点,通过获取使用该注解存入不同的值动态切换指定的数据源;

import java.lang.annotation.*;

/**

* 自定义数据源注解

* 在需要切换数据源的Service层方法上添加此注解,指定数据源名称

*/

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface MyDataSource {

String name() default "";

}

5、 创建DataSourceAspectAOP切面类,以MyDataSource注解为切入点作拓展。在执行被@MyDataSource注解的方法时,获取该注解传入的name,并切换到指定数据源执行,执行完成后切换回默认数据源;

import com.josen.mydemo20201105.annotation.MyDataSource;

import com.josen.mydemo20201105.datasource.DynamicDataSource;

import lombok.extern.slf4j.Slf4j;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Pointcut;

import org.aspectj.lang.reflect.MethodSignature;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;

import javax.sql.DataSource;

import java.lang.reflect.Method;

/**

* @ClassName DataSourceAspect

* @Description Aop切面类配置

* @Author Josen

* @Date 2020/11/5 14:35

**/

@Aspect

@Component

@Slf4j

public class DataSourceAspect {

private static final Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);

/**

* 设置切入点

* 只有调用@MyDataSource注解的方法才会触发around

*/

@Pointcut("@annotation(com.josen.mydemo20201105.annotation.MyDataSource)")

public void dataSourcePointCut() {

}

/**

* 截取使用MyDataSource注解的方法,切换指定数据源

* 环绕切面:是(前置&后置&返回&异常)通知的结合体,更像是动态代理的整个过程

* @param point

*/

@Around("dataSourcePointCut()")

public Object around(ProceedingJoinPoint point) throws Throwable {

MethodSignature signature = (MethodSignature) point.getSignature();

Method method = signature.getMethod();

logger.info("execute DataSourceAspect around=========>"+method.getName());

// 1. 获取自定义注解MyDataSource,查看是否配置指定数据源名称

MyDataSource dataSource = method.getAnnotation(MyDataSource.class);

if(dataSource == null){

// 1.1 使用默认数据源

DynamicDataSource.setDataSource("master-db");

}else {

// 1.2 使用指定名称数据源

DynamicDataSource.setDataSource(dataSource.name());

logger.info("使用指定名称数据源=========>"+dataSource.name());

}

try {

return point.proceed();

} finally {

// 后置处理 - 恢复默认数据源

DynamicDataSource.clearDataSource();

}

}

}

6、 配置启动类

import org.mybatis.spring.annotation.MapperScan;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class}) // 不加载默认数据源配置

@MapperScan(basePackages = "com.josen.mydemo.mapper")

public class MydemoApplication {

public static void main(String[] args) {

SpringApplication.run(MydemoApplication.class, args);

}

}

7、 到这里基本上已经完成,剩下的就是测试是否正确切换数据源了;

Mapper层

@Repository

public interface PermissionMapper {

List findAll();

}

/p>

PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

select * from t_permission;

Service层

@Service

public class PermissionService {

@Autowired

private PermissionMapper permissionMapper;

// 切换从库数据源

@MyDataSource(name = "slave-db")

public List findSlaveAll(){

return permissionMapper.findAll();

}

// 默认数据源

public List findAll(){

return permissionMapper.findAll();

}

}

Controller层

@RestController

@RequestMapping("/permission")

public class PermissionController {

@Autowired

private PermissionService permissionService;

// 测试获取默认master-db数据

@GetMapping("/master")

public List handlerFindAll() {

List list = permissionService.findAll();

return list;

}

// 测试获取指定slave-db数据

@GetMapping("/slave")

public List handlerFindAll2() {

List list = permissionService.findSlaveAll();

return list;

}

}

C - 测试数据源切换

Mysql数据

接口返回数据

总结

到此这篇关于Springboot项目实现Mysql多数据源切换的文章就介绍到这了,更多相关Springboot项目Mysql多数据源切换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

mysql多数据源切换_Springboot项目实现Mysql多数据源切换的完整实例相关推荐

  1. log4jdbc oracle,通过weblogic配置log4jdbc数据源,在项目中使用该数据源,输出sql

    通过weblogic配置log4jdbc数据源,在项目中使用该数据源,输出sql 通过weblogic配置log4jdbc数据源,在项目中使用该数据源,输出sql 说一下配置: 服务器weblogic ...

  2. mysql多数据源事务_springboot项目多数据源及其事务

    多数据源: 1.数据源配置类 DB***Config.java(几个数据源写几个此类) 2.数据源对应数据库连接pom 3.启动类加注解 //关闭springBoot关于mybatis的一些自动注入( ...

  3. java方法嵌套数据源切换_SpringBoot AOP方式实现多数据源切换的方法

    最近在做保证金余额查询优化,在项目启动时候需要把余额全量加载到本地缓存,因为需要全量查询所有骑手的保证金余额,为了不影响主数据库的性能,考虑把这个查询走从库.所以涉及到需要在一个项目中配置多数据源,并 ...

  4. java项目2个数据源_springboot项目配置两个数据源的方法

    本篇文章给大家带来的内容是关于springboot项目配置两个数据源的方法,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. 本文主要介绍如何在一个springboot项目配置两个数据源 ...

  5. mysql 报500错误_java 项目开启mysql binlog参数后报500错误:

    问题: Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT and ...

  6. mybatis mysql 微信昵称表情_springboot+mybatis+druid+mysql 保存emoji表情

    最近在学习springboot,在练习做一个简单博客系统时遇到了保存emoji表情出错,搜索了半天才搞定.这里记录下来,希望能够帮助到自己或有需要的人不要踩坑. 第一步:首先你要保证你的mysql数据 ...

  7. 2 数据源配置_SpringBoot 整合Druid与配置数据源监控

    一:简绍 1.对于数据访问层,无论是SQL还是NOSQL 2.Spring Boot默认采用整合Spring Data的方式进行统一处理 3.添加大量自动配置,屏蔽了很多设置 1.官网描述 二:整合J ...

  8. android 听筒模式外放模式的切换,YY项目之Android 听筒 扬声器 切换

    废话不多说,直接上代码: private void setSpeakerphoneOn(boolean on) { if (on) { // 为true打开喇叭扩音器:为false关闭喇叭扩音器. a ...

  9. dao加service加mysql实例_SpringBoot项目整合mybatis的方法步骤与实例

    1. 导入依赖的jar包 springboot项目整合mybatis之前首先要导入依赖的jar包,配置pom.xml文件如下: xmlns:xsi="http://www.w3.org/20 ...

最新文章

  1. python神经网络训练数据_用Python从头开始实现一个神经网络
  2. 计算机应用基础任务化教程知识点,计算机应用基础任务化教程
  3. 20天减10斤 2020-10-21
  4. 十八、梯度下降与反向传播
  5. java aes pbe_JAVA对称加密算法PBE定义与用法实例分析
  6. mysql 中文字段排序( 按拼音首字母排序) 的查询语句
  7. DOTA中的设计模式
  8. pythonos文件目录方法_python12-OS模块(文件/目录方法)
  9. Android Theme.AppCompat 和 ThemeOverlay.AppCompat
  10. PLECS专家的教诲
  11. 《编程珠玑》——读书笔记1
  12. 为BIG网站建立历史记录页面
  13. db2 系统临时表空间
  14. 使用 Python 进行面部识别
  15. 节假日判断工具(Java)
  16. Cmake的重新编译
  17. word中如何批量更新题注(域)
  18. PetShop全版本(2.0-5.0)
  19. JAVAswing编写界面
  20. 人工智能课程必背知识点

热门文章

  1. navicat 连接oracle
  2. 使用java.util.LinkedList模拟实现内存页面置换算法--LRU算法
  3. python基础学习笔记(八)
  4. 轻松学习Ionic (二) 为Android项目集成Crosswalk(更新官方命令行工具)
  5. [杭电ACM]3336Count the string
  6. 转android项目开发 工作日志 2011.10.8--onConfigurationChanged屏幕改变事件
  7. 卸载Office 2007 Beta2后,Outlook 2003无法启动的解决方法
  8. Struts2拦截器属性excludeMethods、includeMethods配置无效之解决方法
  9. linux shell declare命令 声明shell变量 显示shell函数
  10. python3 报错 TypeError: load() got an unexpected keyword argument ‘encoding‘ 解决方法