Springboot项目实现Mysql多数据源切换的完整实例

一、分析AbstractRoutingDataSource抽象类源码

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

@Nullable
private Map<Object, Object> targetDataSources; // 目标数据源
@Nullable
private Object defaultTargetDataSource; // 默认目标数据源
@Nullable
private Map<Object, DataSource> resolvedDataSources; // 解析的数据源
@Nullable
private DataSource resolvedDefaultDataSource; // 解析的默认数据源

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

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

我们需要关注的是Map<Object, Object> targetDataSources和Map<Object, DataSource> 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 依赖

<dependencies>
 <!-- AOP -->
 <dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
 </dependency>
 <!-- druid -->
 <dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>druid-spring-boot-starter</artifactId>
  <version>1.1.10</version>
 </dependency>
 <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.3</version>
 </dependency>
 <dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <scope>runtime</scope>
 </dependency>
 <dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <optional>true</optional>
 </dependency>
</dependencies>

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<String> contextHolder = new ThreadLocal<>();

 public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> 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<Object, Object> 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<Permission> findAll();
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.josen.mydemo.mapper.PermissionMapper">
 <select id="findAll" resultType="com.josen.mydemo20201105.pojo.Permission">
  select * from t_permission;
 </select>
</mapper>

Service层

@Service
public class PermissionService {
 @Autowired
 private PermissionMapper permissionMapper;
	// 切换从库数据源
 @MyDataSource(name = "slave-db")
 public List<Permission> findSlaveAll(){
  return permissionMapper.findAll();
 }
	// 默认数据源
 public List<Permission> findAll(){
  return permissionMapper.findAll();
 }
}

Controller层

@RestController
@RequestMapping("/permission")
public class PermissionController {
 @Autowired
 private PermissionService permissionService;

 // 测试获取默认master-db数据
 @GetMapping("/master")
 public List<Permission> handlerFindAll() {
  List<Permission> list = permissionService.findAll();
  return list;
 }

 // 测试获取指定slave-db数据
 @GetMapping("/slave")
 public List<Permission> handlerFindAll2() {
  List<Permission> list = permissionService.findSlaveAll();
  return list;
 }
}

C - 测试数据源切换

Mysql数据

接口返回数据

Demo源码地址:https://gitee.com/taco-gigigi/multiple-data-sources

总结

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

(0)

相关推荐

  • Spring Boot + Mybatis多数据源和动态数据源配置方法

    网上的文章基本上都是只有多数据源或只有动态数据源,而最近的项目需要同时使用两种方式,记录一下配置方法供大家参考. 应用场景 项目需要同时连接两个不同的数据库A, B,并且它们都为主从架构,一台写库,多台读库. 多数据源 首先要将spring boot自带的DataSourceAutoConfiguration禁掉,因为它会读取application.properties文件的spring.datasource.*属性并自动配置单数据源.在@SpringBootApplication注解中添加ex

  • Spring Boot 整合mybatis 使用多数据源的实现方法

    前言 本篇教程偏向实战,程序猿直接copy代码加入到自己的项目中做简单的修修改改便可使用,而对于springboot以及mybatis不在此进行展开介绍,如有读者希望了解可以给我留言,并持续关注,我后续会慢慢更新.(黑色区域代码部分,安卓手机可手动向左滑动,来查看全部代码) 整合 其实整合很简单,如果是用gradle的话,在build.gradle文件里加入 compile('org.mybatis.spring.boot:mybatis-spring-boot-starter:1.3.1')

  • springboot + mybatis配置多数据源示例

    在实际开发中,我们一个项目可能会用到多个数据库,通常一个数据库对应一个数据源. 代码结构: 简要原理: 1)DatabaseType列出所有的数据源的key---key 2)DatabaseContextHolder是一个线程安全的DatabaseType容器,并提供了向其中设置和获取DatabaseType的方法 3)DynamicDataSource继承AbstractRoutingDataSource并重写其中的方法determineCurrentLookupKey(),在该方法中使用Da

  • springboot-mongodb的多数据源配置的方法步骤

    在日常工作中,我们可能需要连接多个MongoDB数据源,比如用户库user,日志库log.本章我们来记录连接多个数据源的步骤,以两个数据源为例,多个数据源类推. 1.pom.xml中引入mongodb的依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-mongodb</artifactId> </d

  • 详解SpringBoot和Mybatis配置多数据源

    目前业界操作数据库的框架一般是 Mybatis,但在很多业务场景下,我们需要在一个工程里配置多个数据源来实现业务逻辑.在SpringBoot中也可以实现多数据源并配合Mybatis框架编写xml文件来执行SQL.在SpringBoot中,配置多数据源的方式十分便捷, 下面开始上代码: 在pom.xml文件中需要添加一些依赖 <!-- Spring Boot Mybatis 依赖 --> <dependency> <groupId>org.mybatis.spring.b

  • Spring Boot 动态数据源示例(多数据源自动切换)

    本文实现案例场景: 某系统除了需要从自己的主要数据库上读取和管理数据外,还有一部分业务涉及到其他多个数据库,要求可以在任何方法上可以灵活指定具体要操作的数据库. 为了在开发中以最简单的方法使用,本文基于注解和AOP的方法实现,在spring boot框架的项目中,添加本文实现的代码类后,只需要配置好数据源就可以直接通过注解使用,简单方便. 一配置二使用 1. 启动类注册动态数据源 2. 配置文件中配置多个数据源 3. 在需要的方法上使用注解指定数据源 1.在启动类添加 @Import({Dyna

  • 解决spring boot 1.5.4 配置多数据源的问题

    spring boot 已经支持多数据源配置了,无需网上好多那些编写什么类的,特别麻烦,看看如下解决方案,官方的,放心! 1.首先定义数据源配置 #=====================multiple database config============================ #ds1 first.datasource.url=jdbc:mysql://localhost/test?characterEncoding=utf8&useSSL=true first.datasou

  • springboot下配置多数据源的方法

    一.springboot 简介 SpringBoot使开发独立的,产品级别的基于Spring的应用变得非常简单,你只需"just run". 我们为Spring平台及第三方库提 供开箱即用的设置,这样你就可以有条不紊地开始.多数Spring Boot应用需要很少的Spring配置. 你可以使用SpringBoot创建Java应用,并使用 java -jar 启动它或采用传统的war部署方式.我们也提供了一个运行"spring 脚本"的命令行工具. 二.传统的Dat

  • Spring Boot多数据源及其事务管理配置方法

    准备工作 先给我们的项目添加Spring-JDBC依赖和需要访问数据库的驱动依赖. 配置文件 spring.datasource.prod.driverClassName=com.mysql.jdbc.Driver spring.datasource.prod.url=jdbc:mysql://127.0.0.1:3306/prod spring.datasource.prod.username=root spring.datasource.prod.password=123456 spring

  • Springboot项目实现Mysql多数据源切换的完整实例

    一.分析AbstractRoutingDataSource抽象类源码 关注import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource以下变量 @Nullable private Map<Object, Object> targetDataSources; // 目标数据源 @Nullable private Object defaultTargetDataSource; // 默认目标数据源 @Null

  • SpringBoot AOP方式实现多数据源切换的方法

    最近在做保证金余额查询优化,在项目启动时候需要把余额全量加载到本地缓存,因为需要全量查询所有骑手的保证金余额,为了不影响主数据库的性能,考虑把这个查询走从库.所以涉及到需要在一个项目中配置多数据源,并且能够动态切换.经过一番摸索,完美实现动态切换,记录一下配置方法供大家参考. 设计总体思路 Spring-Boot+AOP方式实现多数据源切换,继承AbstractRoutingDataSource实现数据源动态的获取,在service层使用注解指定数据源. 步骤 一.多数据源配置 在applica

  • springboot 项目使用jasypt加密数据源的方法

    引入maven 坐标 <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifactId>jasypt-spring-boot-starter</artifactId> <version>3.0.4</version> </dependency> 配置密钥 #jasypt加密的盐值 jasypt.encryptor.password=jsuu

  • springboot集成@DS注解实现数据源切换的方法示例

    目录 启用@DS实现数据源切换 POM内添加核心jar包 yml配置 “核心”-使用@DS注解 最后 启用@DS实现数据源切换 POM内添加核心jar包         <dependency>             <groupId>com.baomidou</groupId>             <artifactId>dynamic-datasource-spring-boot-starter</artifactId>        

  • Docker compose部署SpringBoot项目连接MySQL及遇到的坑

    前面使用docker简单部署了下SpringBoot项目并介绍了什么是Dockerfile,如果是镜像比较多的情况下如何一键交付呢?Docker-compose!,理解了Docker-compose后面学习K8s就会好理解一些 一.安装docker-compose 环境Centos7 //下载docker-compose curl -L "https://get.daocloud.io/docker/compose/releases/download/1.27.3/docker-compose-

  • Spring MVC实现mysql数据库增删改查完整实例

    最近刚学了springmvc框架,感觉确实方便了不少,减少了大量的冗余代码.就自己做了个小项目练练手,这是个初级的springmvc应用的项目,没有用到mybatis,项目功能还算完善,实现了基本的增删改查的功能. 项目环境: -系统:win10 -开发环境:eclipseOxygenReleaseCandidate3(4.7) -jdk版本:java1.8(121) -mysql:5.7 -spring:4.0 -tomcat:8.5 用到的技术: springmvcspringjspjdbc

  • SpringBoot项目中的多数据源支持的方法

    1.概述 项目中经常会遇到一个应用需要访问多个数据源的情况,本文介绍在SpringBoot项目中利用SpringDataJpa技术如何支持多个数据库的数据源. 具体的代码参照该 示例项目 2.建立实体类(Entity) 首先,我们创建两个简单的实体类,分别属于两个不同的数据源,用于演示多数据源数据的保存和查询. Test实体类: package com.example.demo.test.data; import javax.persistence.Entity; import javax.pe

  • 如何在Java SpringBoot项目中配置动态数据源你知道吗

    目录 首先需要引入第三方依赖 只需要在配置文件中按照如下配置 创建如下两个数据库 entity mapper.xml mapper层 Service层 下面是两个测试方法 下面可以来看一下测试结果: 在我们工作中涉及到一些场景需要我们配置多数据源的操作,之前来说我们配置数据源需要写繁琐的配置类来配置我们的数据源,哪个是默认数据源等等,而现在我们可以使用"苞米豆"为我们提供的提供的第三方工具,只需要简单配置就可以实现多数据源之间的灵活切换了! 首先需要引入第三方依赖 <depend

  • Delphi实现图片滚动切换的完整实例代码

    本文以实例介绍了Delphi实现图片滚动切换的方法.该程序可以实现图像的滚动播出,并且通过本代码可控制窗口中的图像向上滚动,通过调节速度滚动条的值还可以调整滚动的速度:点击"停止"按钮,图像即停止滚动. 完整的功能代码如下: unit Unit1; interface uses Windows, Classes, Graphics, Forms, Controls, Menus, Dialogs, StdCtrls, ExtCtrls, SysUtils, ComCtrls, Butt

  • js实现百度联盟中一款不错的图片切换效果完整实例

    本文实例讲述了js实现百度联盟中一款不错的图片切换效果的方法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: <html> <title>js实现百度联盟中的一个不错的图片切换效果</title> <body> <script> var links = new Array(); links[1] = "http://www.baidu.com/"; links[2] = "http://www.jb5

随机推荐