IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> springboot实现主从数据库动态切换(注解式) -> 正文阅读

[Java知识库]springboot实现主从数据库动态切换(注解式)

1. 创建一个管理数据源key值的类RoutingDataSourceHolder?

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
?
public class RoutingDataSourceHolder {
? ? private static Logger logger = LogManager.getLogger();
?
? ? private static final ThreadLocal<String> dataSources = new ThreadLocal<>();
?
? ? //一个事务内用同一个数据源
? ? public static void setDataSource(String dataSourceName) {
? ? ? ? if (dataSources.get() == null) {
? ? ? ? ? ? dataSources.set(dataSourceName);
? ? ? ? ? ? logger.info("设置数据源:{}", dataSourceName);
? ? ? ? }
? ? }
?
? ? public static String getDataSource() {
? ? ? ? return dataSources.get();
? ? }
?
? ? public static void clearDataSource() {
? ? ? ? dataSources.remove();
? ? }
}

代码设置了一个事务内使用同一个数据源。

2. 创建一个类继承AbstractRoutingDataSource

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
?
public class RoutingDataSource extends AbstractRoutingDataSource {
? ? private Logger logger = LogManager.getLogger();
?
? ? @Override
? ? protected Object determineCurrentLookupKey() {
? ? ? ? String dataSource = RoutingDataSourceHolder.getDataSource();
? ? ? ? logger.info("使用数据源:{}", dataSource);
? ? ? ? return dataSource;
? ? }
}

重写 determineCurrentLookupKey方法,返回要使用的数据源key值。?

以上两个类解决了动态数据源key值的问题,下面处理初始化targetDataSources对象。

3. 配置主从数据库类DataSourceConfigurer

1. DataSourceConfigurer

import com.alibaba.druid.pool.DruidDataSource;
import com.custom.common.utils.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;
?
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
?
/**
?* 配置主从数据库
?*/
@Configuration
public class DataSourceConfigurer {
? ? private Logger logger = LogManager.getLogger();
?
? ? public final static String MASTER_DATASOURCE = "masterDataSource";
? ? public final static String SLAVE_DATASOURCE = "slaveDataSource";
?
? ? @Bean(MASTER_DATASOURCE)
? ? @ConfigurationProperties(prefix = "spring.datasource")
? ? public DruidDataSource masterDataSource(DataSourceProperties properties) {
? ? ? ? DruidDataSource build = properties.initializeDataSourceBuilder().type(DruidDataSource.class).build();
? ? ? ? logger.info("配置主数据库:{}", build);
? ? ? ? return build;
? ? }
?
? ? @Bean(SLAVE_DATASOURCE)
? ? @ConfigurationProperties(prefix = "spring.slave-datasource")
? ? public DruidDataSource slaveDataSource(DataSourceProperties properties) {
? ? ? ? DruidDataSource build = properties.initializeDataSourceBuilder().type(DruidDataSource.class).build();
? ? ? ? logger.info("配置从数据库:{}", build);
? ? ? ? return build;
? ? }
?
? ? /**
? ? ?* Primary 优先使用该Bean
? ? ?* DependsOn 先执行主从数据库的配置
? ? ?* Qualifier 指定使用哪个Bean
? ? ?*
? ? ?* @param masterDataSource
? ? ?* @param slaveDataSource
? ? ?* @return
? ? ?*/
? ? @Bean
? ? @Primary
? ? @DependsOn(value = {MASTER_DATASOURCE, SLAVE_DATASOURCE})
? ? public DataSource routingDataSource(@Qualifier(MASTER_DATASOURCE) DruidDataSource masterDataSource,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? @Qualifier(SLAVE_DATASOURCE) DruidDataSource slaveDataSource) {
? ? ? ? if (StringUtils.isBlank(slaveDataSource.getUrl())) {
? ? ? ? ? ? logger.info("没有配置从数据库,默认使用主数据库");
? ? ? ? ? ? return masterDataSource;
? ? ? ? }
? ? ? ? Map<Object, Object> map = new HashMap<>();
? ? ? ? map.put(DataSourceConfigurer.MASTER_DATASOURCE, masterDataSource);
? ? ? ? map.put(DataSourceConfigurer.SLAVE_DATASOURCE, slaveDataSource);
? ? ? ? RoutingDataSource routing = new RoutingDataSource();
? ? ? ? //设置动态数据源
? ? ? ? routing.setTargetDataSources(map);
? ? ? ? //设置默认数据源
? ? ? ? routing.setDefaultTargetDataSource(masterDataSource);
? ? ? ? logger.info("主从数据库配置完成");
? ? ? ? return routing;
? ? }
}

设置初始化targetDataSources对象关键代码

Map<Object, Object> map = new HashMap<>();
map.put(DataSourceConfigurer.MASTER_DATASOURCE, masterDataSource);
map.put(DataSourceConfigurer.SLAVE_DATASOURCE, slaveDataSource);
RoutingDataSource routing = new RoutingDataSource();
//设置动态数据源
routing.setTargetDataSources(map);
//设置默认数据源
routing.setDefaultTargetDataSource(masterDataSource);

2. application.properties

# ----------------------------------------
# 主数据库
# ----------------------------------------
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/custom?useunicode=true&characterEncoding=utf8&serverTimezone=GMT%2b8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
?
# ----------------------------------------
# 从数据库
# ----------------------------------------
spring.slave-datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.slave-datasource.url=jdbc:mysql://127.0.0.1:3309/custom?useunicode=true&characterEncoding=utf8&serverTimezone=GMT%2b8
spring.slave-datasource.username=root
spring.slave-datasource.password=root
spring.slave-datasource.driver-class-name=com.mysql.cj.jdbc.Driver

一个配置类处理了targetDataSources对象的初始化.

那问题都处理了,那具体要怎么使用呢,关键就是在事务之前调用RoutingDataSourceHolder.setDataSource()方法就可以了。我们写一个aop实现吧。

4. 创建aop注解和类

1. DataSourceWith

import java.lang.annotation.*;
?
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface DataSourceWith {
? ? String key() default "";
}

2.DataSourceWithAspect

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
?
import java.lang.reflect.Method;
?
@Aspect
@Order(-1)// 保证该AOP在@Transactional之前运行
@Component
public class DataSourceWithAspect {
?
? ? /**
? ? ?* 使用DataSourceWith注解就拦截
? ? ?*/
? ? @Pointcut("@annotation(com.custom.configure.datasource.DataSourceWith)||@within(com.custom.configure.datasource.DataSourceWith)")
? ? public void doPointcut() {
?
? ? }
?
? ? /**
? ? ?* 方法前,为了在事务前设置
? ? ?*/
? ? @Before("doPointcut()")
? ? public void doBefore(JoinPoint joinPoint) {
? ? ? ? MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
? ? ? ? Method method = methodSignature.getMethod();
? ? ? ? // 获取注解对象
? ? ? ? DataSourceWith dataSource = method.getAnnotation(DataSourceWith.class);
? ? ? ? if (dataSource == null) {
? ? ? ? ? ? //方法没有就获取类上的
? ? ? ? ? ? dataSource = method.getDeclaringClass().getAnnotation(DataSourceWith.class);
? ? ? ? }
? ? ? ? String key = dataSource.key();
? ? ? ? RoutingDataSourceHolder.setDataSource(key);
? ? }
?
? ? @After("doPointcut()")
? ? public void doAfter(JoinPoint joinPoint) {
? ? ? ? RoutingDataSourceHolder.clearDataSource();
? ? }
?
}

3.使用和效果

@DataSourceWith在方法上或者类上都可以。?

    /**
? ? ?* 获取部门列表
? ? ?**/
? ? @DataSourceWith(key = DataSourceConfigurer.SLAVE_DATASOURCE)
? ? public List<Dept> findDeptTree() {
? ? ? ? logger.debug("获取部门树数据");
? ? ? ? List<Dept> deptList = new ArrayList<>();
? ? ? ? return deptList;
? ? }

结果:动态切换成功

原文链接:https://blog.csdn.net/m0_68615056/article/details/123738282

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-09-13 10:59:34  更:2022-09-13 11:03:03 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 12:50:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码