栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

一、分析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
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;


@Configuration
@Component
public class DynamicDataSourceConfig {
 
 @Bean
 @ConfigurationProperties("spring.datasource.druid.master-db")
 public DataSource myMasterDataSource(){
  return DruidDataSourceBuilder.create().build();
 }

 
 @Bean
 @ConfigurationProperties("spring.datasource.druid.slave-db")
 public DataSource mySlaveDataSource(){
  return DruidDataSourceBuilder.create().build();
 }
 
 @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.*;

@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;


@Aspect
@Component
@Slf4j
public class DataSourceAspect {
 private static final Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);

 
 @Pointcut("@annotation(com.josen.mydemo20201105.annotation.MyDataSource)")
 public void dataSourcePointCut() {
 }

 
 @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();
}



 
  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数据

接口返回数据

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

总结

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/130624.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号