当前位置: 首页 > news >正文

Spring 多数据源方法级别注解实现

Spring框架提供了多种数据源管理方式,其中多数据源管理是其中之一。多数据源管理允许应用程序使用多个数据源,而不是只使用一个数据源,从而提高了应用程序的灵活性和可靠性。

多数据源管理的主要目的是让应用程序能够在不同的数据库之间切换,以满足不同的业务需求。例如,如果应用程序需要访问一个高可用性的数据库,但是该数据库当前不可用,那么应用程序可以自动切换到备用数据库,以保证业务的连续性。

在多数据源管理中,Spring框架通过使用JNDI(Java命名和目录接口)来管理数据源。JNDI允许应用程序在运行时动态查找数据源,并且可以使用不同的数据源来访问不同的数据库。

在多数据源管理中,还需要考虑数据源的配置和管理。这包括创建数据源、配置数据源、管理连接池和处理连接泄漏等问题。Spring框架提供了多种数据源配置和管理的工具和技术,例如Spring JDBC Template、Spring Data JPA和Spring Boot等。

总之,多数据源管理是Spring框架中非常重要的一部分,它可以提高应用程序的灵活性和可靠性,从而更好地满足业务需求。

废话少说,直接开干!

下面详细地介绍一下多数据源的实现过程,同时实现一个方法级别的注解,便于灵活使用。

1, Pom配置

引入mysql,spring-boot-starter-jdbc

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId><version>2.4.2</version>
</dependency>
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql-connector.version}</version>
</dependency>
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${mybatis-plus.version}</version>
</dependency>

2,配置数据源

可以在项目里,或者在apollo,nacos等配置中心上

#数据源master
spring.dynamic.datasource.master.url = jdbc:mysql://127.0.0.1:3306/aaa?characterEncoding=utf-8&rewriteBatchedStatements=true&tinyInt1isBit=false&allowMultiQueries=true
spring.dynamic.datasource.master.username = root
spring.dynamic.datasource.master.password = xxxxxxxx
spring.dynamic.datasource.master.driver-class-name = com.mysql.cj.jdbc.Driver
spring.dynamic.datasource.master.type = com.zaxxer.hikari.HikariDataSource#数据源slave
spring.dynamic.datasource.slave.url = jdbc:mysql://127.0.0.1:3306/bbb?characterEncoding=utf-8&rewriteBatchedStatements=true&tinyInt1isBit=false&allowMultiQueries=true
spring.dynamic.datasource.slave.username = root
spring.dynamic.datasource.slave.password = yyyyyyyy
spring.dynamic.datasource.slave.driver-class-name = com.mysql.cj.jdbc.Driver
spring.dynamic.datasource.slave.type = com.zaxxer.hikari.HikariDataSource

3,读取数据源配置

3.1 先看整个项目结构

3.2 新增读取数据源配置类

import lombok.Data;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;import java.util.Map;@Data
@ConfigurationProperties(prefix = "spring.dynamic")
public class DSProperties {private Map<String, DataSourceProperties> datasource;
}

 3.3 新增线程和数据源mapping的类


import com.xxx.gateway.common.Constant;
import lombok.extern.slf4j.Slf4j;@Slf4j
public class DynamicDataSourceHolder {/*** 保存动态数据源名称*/private static final ThreadLocal<String> DYNAMIC_DATASOURCE_KEY = new ThreadLocal<>();/*** 设置/切换数据源,决定当前线程使用哪个数据源*/public static void setDynamicDataSourceKey(String key){log.info("数据源切换为:{}",key);DYNAMIC_DATASOURCE_KEY.set(key);}/*** 获取动态数据源名称,默认使用mater数据源*/public static String getDynamicDataSourceKey(){String key = DYNAMIC_DATASOURCE_KEY.get();return key == null ? Constant.MASTER_DATASOURCE : key;}/*** 移除当前数据源*/public static void removeDynamicDataSourceKey(){log.info("移除数据源:{}",DYNAMIC_DATASOURCE_KEY.get());DYNAMIC_DATASOURCE_KEY.remove();}
}

3.4 继承路由多数据源的抽象类,并实现determineCurrentLookupKey方法

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {String dataBaseType = DynamicDataSourceHolder.getDynamicDataSourceKey();return dataBaseType;}
}

4,根据配置重写数据源和事务管理器

注意: 下面这个注解里的basePackages是你项目的路径

@MapperScan(basePackages = {"com.xxx.gateway.mapper"},
sqlSessionFactoryRef = "SqlSessionFactory")


import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;import java.util.HashMap;
import java.util.Map;@Configuration
@EnableConfigurationProperties(DSProperties.class)
@MapperScan(basePackages = {"com.xxx.gateway.mapper"},sqlSessionFactoryRef = "SqlSessionFactory")
public class DynamicDataSourceConfig {@Bean(name = "dynamicDataSource")public DynamicDataSource DataSource(DSProperties dsProperties) {Map targetDataSource = new HashMap<>(8);dsProperties.getDatasource().forEach((k, v) -> {targetDataSource.put(k, v.initializeDataSourceBuilder().build());});DynamicDataSource dataSource = new DynamicDataSource();dataSource.setTargetDataSources(targetDataSource);return dataSource;}/*** 创建动态数据源的SqlSessionFactory,传入的是动态数据源* @Primary这个注解很重要,如果项目中存在多个SqlSessionFactory,这个注解一定要加上*/@Primary@Bean("SqlSessionFactory")public SqlSessionFactory sqlSessionFactoryBean(DynamicDataSource dynamicDataSource) throws Exception {SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();sqlSessionFactoryBean.setDataSource(dynamicDataSource);org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();configuration.setMapUnderscoreToCamelCase(true);configuration.setDefaultFetchSize(100);configuration.setDefaultStatementTimeout(30);sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml"));sqlSessionFactoryBean.setConfiguration(configuration);return sqlSessionFactoryBean.getObject();}/*** 重写事务管理器,管理动态数据源*/@Primary@Bean(value = "transactionManager")public PlatformTransactionManager annotationDrivenTransactionManager(DynamicDataSource dataSource) {return new DataSourceTransactionManager(dataSource);}
}

5,注解实现

5.1 新增注解定义 

import java.lang.annotation.*;@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DS {String value() default Constant.MASTER_DATASOURCE;
}

5.2 新增注解实现

import com.xxx.gateway.config.DynamicDataSourceHolder;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.Objects;@Aspect
@Order(1)
@Component
@Slf4j
public class DsAspect {@Pointcut("@annotation(com.xgd.gateway.aspect.DS)")public void dsPointCut() {}//环绕通知@Around("dsPointCut()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable{String key = getDefineAnnotation(joinPoint).value();DynamicDataSourceHolder.setDynamicDataSourceKey(key);try {return joinPoint.proceed();} finally {DynamicDataSourceHolder.removeDynamicDataSourceKey();}}/*** 先判断方法的注解,后判断类的注解,以方法的注解为准* @param joinPoint* @return*/private DS getDefineAnnotation(ProceedingJoinPoint joinPoint){MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();DS dataSourceAnnotation = methodSignature.getMethod().getAnnotation(DS.class);if (Objects.nonNull(methodSignature)) {return dataSourceAnnotation;} else {Class<?> dsClass = joinPoint.getTarget().getClass();return dsClass.getAnnotation(DS.class);}}
}

6, 通过注解使用多数据源

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xxx.gateway.aspect.DS;
import com.xxx.gateway.domain.User;
import org.apache.ibatis.annotations.Param;import java.util.List;public interface UserMapper extends BaseMapper<User> {public User queryByCode(@Param("code") String code);@DS(value="master")public User queryByCodeNoPass(@Param("code") String code);@DS(value="slave1")public List<String> queryBanks();
}

注意:

1,@DS注解里的value值是可以随意的,只要和配置中心里spring.dynamic.datasource.后面的一致就可以。

 2,如果方法上没有配置数据源,那么就是走默认的master数据源,所以项目里必须至少配置了master数据源。

3,各个数据源可以重复,因为不会校验是否重复。

码字不易,记得点赞哟

相关文章:

Spring 多数据源方法级别注解实现

Spring框架提供了多种数据源管理方式&#xff0c;其中多数据源管理是其中之一。多数据源管理允许应用程序使用多个数据源&#xff0c;而不是只使用一个数据源&#xff0c;从而提高了应用程序的灵活性和可靠性。 多数据源管理的主要目的是让应用程序能够在不同的数据库之间切换&…...

Redis在云服务器上的安装与客户端连接配置

文章目录 Redis1.Redis的安装2.设置远程连接3.客户端连接3.1 客户端下载 Redis 1.Redis的安装 yum 安装 redis&#xff0c;使用以下命令&#xff0c;直接将 redis 安装到 linux 服务器&#xff1a; yum -y install redis 启动 redis使用以下命令&#xff0c;以后台运行方式启…...

​语言模型输出端共享Embedding的重新探索

©PaperWeekly 原创 作者 | 苏剑林 单位 | 科学空间 研究方向 | NLP、神经网络 预训练刚兴起时&#xff0c;在语言模型的输出端重用 Embedding 权重是很常见的操作&#xff0c;比如 BERT、第一版的 T5、早期的 GPT&#xff0c;都使用了这个操作&#xff0c;这是因为当模型…...

Spring中事务失效的8中场景

1. 数据库引擎不支持事务 这里以 MySQL为例&#xff0c;MyISAM引擎是不支持事务操作的&#xff0c;一般要支持事务都会使用InnoDB引擎&#xff0c;根据MySQL 的官方文档说明&#xff0c;从MySQL 5.5.5 开始的默认存储引擎是 InnoDB&#xff0c;之前默认的都是 MyISAM&#xff…...

安卓——转场动画

先创建一个名为anim的包 往里面写入两个xml页 为淡入淡出的效果 淡入效果 <alpha xmlns:android="http://schemas.android.com/apk/res/android"android:interpolator="@android:anim/accelerate_decelerate_interpolator"android:fromAlpha...

多位数码管动态扫描显示变化数据(数码管右移1)

/*----------------------------------------------- 内容&#xff1a;多位数码管分别显示不同数字&#xff0c;这种扫描显示方式成为动态扫描&#xff0c;并不停变化赋值 ------------------------------------------------*/ #include<reg52.h> //包含头文件&#xff0…...

充分了解java阻塞队列机制

多线程基础 1.阻塞队列1.1 什么是 阻塞队列1.2 阻塞队列的特点 1.3 阻塞队列常用方法1.3.1 抛出异常:add、remove、element1.3.2 返回结果但是不抛出异常offer、poll、peek1.3.3 阻塞put和take1.3.4 小结 1.4 常见的阻塞队列1.4.1 ArrayListBlockingQueue1.4.2 LinkedBlockingQ…...

安装使用LangChain时的报错解决

刚刚装了LangChain但是引入各种包都报错&#xff0c;原因貌似为 Python3.7 不支持 LangChain&#xff0c;需要开启一个新的Python3.10环境&#xff0c;再重新安装即可正常运行。 创建新的python环境 conda create -n new_env python3.10 重新安装 pip install langchain 这是当…...

【MySQL】库的操作

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《零基础入门MySQL》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录 &#x1f449;库…...

Java设计模式之工厂模式

什么是工厂模式 工厂模式&#xff08;Factory Pattern&#xff09;是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 工厂模式提供了一种将对象的实例化过程封装在工厂类中的方式。通过使用工厂模式&#xff…...

正则表达式-速成教程

正则表达式-速成教程 今天遇到一枚程序媛在群里吐槽&#xff0c;并附了截图&#xff1b;然后无意中看到她的一个正则与她的注释描述不一致&#xff0c;就提醒了一下。顺带着给了个速成教程&#xff0c;在这里把这个速成教程贴出来&#xff0c;一是为了自己备份&#xff1b;二是…...

C语言中的数组(详解)

C语言中的数组&#xff08;详解&#xff09; 一、一维数组1.一维数组的创建2.数组的初始化3.一维数组的使用4.一维数组在内存中的存储二、二维数组1.二维数组的创建2.二维数组的初始化3.二维数组的使用4.二维数组在内存中的存储三、数组越界四、数组作为函数参数1.冒泡排序2.数…...

【App管理04-Bug修正 Objective-C语言】

一、咱们刚才已经把这个给大家做完了吧 1.这个Label怎么显示到上面去了, 我们现在是把它加到我们的控制器的View里面吧 我们看一下这个坐标是怎么算的,来,我们找一个坐标, 咱们的坐标,是不是用这个View的frame,减的吧 来,咱们在这里,输出一下这个Frame,看一下吧 在…...

黑客自学笔记(网络安全)

一、黑客是什么 原是指热心于计算机技术&#xff0c;水平高超的电脑专家&#xff0c;尤其是程序设计人员。但后来&#xff0c;黑客一词已被用于泛指那些专门利用电脑网络搞破坏或者恶作剧的家伙。 二、学习黑客技术的原因 其实&#xff0c;网络信息空间安全已经成为海陆空之…...

action=store_true和store_false理解及实战测试

store_true 是指带触发 action 时为真&#xff0c;不触发则为假&#xff0c; 即默认 False &#xff0c;传参 则 设置为 True store_false 则与之相反 以代码为例&#xff1a; import sys import argparse def parse_args():parser argparse.ArgumentParser(descriptionrun …...

Android 通用带箭头提示窗

简介 自定义PopupWindow, 适用于提示类弹窗。 使用自定义Drawable设置带箭头的背景&#xff0c;测试控件和弹窗的尺寸&#xff0c;自动设置弹窗的显示位置&#xff0c;让箭头指向锚点控件的中间位置&#xff0c;且根据锚点控件在屏幕的位置&#xff0c;自动适配弹窗显示位置。…...

隧道安全监测解决方案

隧道安全监测 解决方案 一、监测目的 通过监控量测&#xff0c;实现信息化施工&#xff0c;不仅能及时掌握隧道实际的地质情况&#xff0c;掌握隧道围岩、支护衬砌结构的受力特征和变形情况&#xff0c;据此可以尽早发现塌方、大变形等灾害征兆&#xff0c;及时采取措施&…...

3 Linux基础篇-VMware和Linux的安装

3 Linux基础篇-VMware和Linux的安装 文章目录 3 Linux基础篇-VMware和Linux的安装3.1 安装VMware和CentOS3.1.1 VM安装3.1.2 Centos7.6的安装步骤 3.3 虚拟机基本操作3.4 安装VMtools3.5 设置共享文件夹 学习视频来自于B站【小白入门 通俗易懂】2021韩顺平 一周学会Linux。可能…...

什么是预处理器指令,常用的预处理器指令有哪些?什么是运算符,C 语言中的运算符有哪些?

1.什么是预处理器指令&#xff0c;常用的预处理器指令有哪些&#xff1f; 预处理器指令是一种用于在源代码编译之前进行预处理的特殊指令。它们通过在程序编译之前对源代码进行处理&#xff0c;可以在编译阶段之前进行一些文本替换、条件编译等操作&#xff0c;从而对源代码进…...

新功能 – Cloud WAN:托管 WAN 服务

我很高兴地宣布&#xff0c;我们推出了 Amazon Cloud WAN&#xff0c;这是一项新的网络服务&#xff0c;它可以轻松构建和运营连接您的数据中心和分支机构以及多个 Amazon 区域中的多个 VPC 的广域网&#xff08;WAN&#xff09;。 亚马逊云科技开发者社区为开发者们提供全球的…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析

今天聊的内容&#xff0c;我认为是AI开发里面非常重要的内容。它在AI开发里无处不在&#xff0c;当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗"&#xff0c;或者让翻译模型 "将这段合同翻译成商务日语" 时&#xff0c;输入的这句话就是 Prompt。…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…...

代码规范和架构【立芯理论一】(2025.06.08)

1、代码规范的目标 代码简洁精炼、美观&#xff0c;可持续性好高效率高复用&#xff0c;可移植性好高内聚&#xff0c;低耦合没有冗余规范性&#xff0c;代码有规可循&#xff0c;可以看出自己当时的思考过程特殊排版&#xff0c;特殊语法&#xff0c;特殊指令&#xff0c;必须…...

华为OD机试-最短木板长度-二分法(A卷,100分)

此题是一个最大化最小值的典型例题&#xff0c; 因为搜索范围是有界的&#xff0c;上界最大木板长度补充的全部木料长度&#xff0c;下界最小木板长度&#xff1b; 即left0,right10^6; 我们可以设置一个候选值x(mid)&#xff0c;将木板的长度全部都补充到x&#xff0c;如果成功…...

git: early EOF

macOS报错&#xff1a; Initialized empty Git repository in /usr/local/Homebrew/Library/Taps/homebrew/homebrew-core/.git/ remote: Enumerating objects: 2691797, done. remote: Counting objects: 100% (1760/1760), done. remote: Compressing objects: 100% (636/636…...

绕过 Xcode?使用 Appuploader和主流工具实现 iOS 上架自动化

iOS 应用的发布流程一直是开发链路中最“苹果味”的环节&#xff1a;强依赖 Xcode、必须使用 macOS、各种证书和描述文件配置……对很多跨平台开发者来说&#xff0c;这一套流程并不友好。 特别是当你的项目主要在 Windows 或 Linux 下开发&#xff08;例如 Flutter、React Na…...

加密通信 + 行为分析:运营商行业安全防御体系重构

在数字经济蓬勃发展的时代&#xff0c;运营商作为信息通信网络的核心枢纽&#xff0c;承载着海量用户数据与关键业务传输&#xff0c;其安全防御体系的可靠性直接关乎国家安全、社会稳定与企业发展。随着网络攻击手段的不断升级&#xff0c;传统安全防护体系逐渐暴露出局限性&a…...