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

news2024/11/26 5:27:08

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 {
    @Override
    protected 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,各个数据源可以重复,因为不会校验是否重复。

码字不易,记得点赞哟

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/784843.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringCache 框架使用以及序列化和缓存过期时间问题的解决

目录 为什么使用Spring Cache 如何使用Spring Cache 1 加依赖 2 开启缓存 3 加缓存注解 序列化以及过期时间等问题 解决方案&#xff1a;自定义序列化方式 1.自定义序列化方式并设置白名单 2.配置并设置缓存的过期时间 为什么使用Spring Cache 缓存有诸多的好处&#x…

YOLOv5(v7.0)网络修改实践二:把单分支head改为YOLOX的双分支解耦合head(DecoupleHead)

前面研究了一下YOLOX的网络结构&#xff0c;在YOLOv5(tag7.0)集成了yolox的骨干网络&#xff0c;现在继续下一步集成YOLOX的head模块。YOLOX的head模块是双分支解耦合网络&#xff0c;把目标置信度的预测和目标的位置预测分成两条支路&#xff0c;并验证双分支解耦合头性能要优…

为什么视频画质会变差,如何提升视频画质清晰度。

在数字时代&#xff0c;视频已经成为我们生活中不可或缺的一部分。然而&#xff0c;随着视频的传输和处理过程中的多次压缩&#xff0c;画质损失逐渐凸显&#xff0c;影响了我们对影像的真实感受。为了让视频画质更加清晰、逼真&#xff0c;我们需要采取一些措施来保护和修复视…

设计模式大白话——观察者模式

文章目录 一、概述二、示例三、模式定义四、其他 一、概述 ​ 与其叫他观察者模式&#xff0c;我更愿意叫他叫 订阅-发布模式 &#xff0c;这种模式在我们生活中非常常见&#xff0c;比如&#xff1a;追番了某个电视剧&#xff0c;当电视剧有更新的时候会第一时间通知你。当你…

Zia和ChatGPT如何协同工作?

有没有集成ChatGPT的CRM系统推荐&#xff1f;Zoho CRM已经正式与ChatGPT集成。下面我们将从使用场景、使用价值和使用范围等方面切入讲述CRMAI的应用和作用。 Zia和ChatGPT如何协同工作&#xff1f; Zia和ChatGPT是不同的人工智能模型&#xff0c;在CRM中呈现出共生的关系。 …

荧光信号采集的2种方法

本文介绍荧光信号采集的2种方法。 荧光信号采集是生化类医疗设备&#xff08;PCR&#xff0c;荧光免疫分析仪&#xff09;常用的功能&#xff0c;针对不同类型的激发光和发射光采用的荧光信号采集方法也不一样。 1.基本概念 1)发光原理 当待测物质受到外接某一波长的入射光…

生物信息学_玉泉路_课堂笔记_02 第二章 序列比对和序列数据库搜索

&#x1f345; 课程&#xff1a;生物信息学_玉泉路_课堂笔记 中科院_2022秋季课 第一学期 &#x1f345; 个人笔记使用 &#x1f345; 2023/7/5 一、上周回顾 二、生物信息学的展望 —— 精准医学的发展 生物信息学的开拓者 三、双序列比对的常用算法 序列比对的需求 使用 b…

基于包围框回归的目标检测网络原理及Tensorflow实现

对象检测是对图像内的对象进行分类和定位。 换句话说&#xff0c;它是图像分类和对象定位的结合。 构建用于图像分类的机器学习模型更简单&#xff0c;我在我的一篇文章中对此进行了描述。 然而&#xff0c;图像分类器无法准确判断对象在图像内的位置。 为了实现这一目标&#…

opencv对相机进行畸变矫正,及从矫正后的图像坐标反求原来的对应坐标

1.背景 目前有个项目&#xff0c;需要用到热成像相机。但是这个热成像相机它的畸变比较厉害&#xff0c;因此需要用标定板进行标定&#xff0c;从而消除镜头畸变。 同时需要实现用户用鼠标点击矫正后的画面后&#xff0c;显示用户点击位置的像素所代表的温度。 2.难点 消除镜…

1-1 AUTOSAR是什么?

目录 一、AUTOSAR组织 二、AUTOSAR的版本 三、AUTOSAR的理念 四、AUTOSAR的动机 五、AUTOSAR目标 六、AUTOSAR的优势 6.1 商业优势 6.2 技术优势 一、AUTOSAR组织 AUTOSAR&#xff08;Automotive Open System Architecture&#xff09;是一个全球性的汽车行业标准化组织…

【流形和流形空间(姿态)】

定义 流形&#xff08;Manifold&#xff09;是一种广义的曲面概念&#xff0c;用于描述局部上类似于欧几里德空间的空间。简而言之&#xff0c;流形是一个局部与欧几里德空间同胚&#xff08;homeomorphic&#xff09;的空间&#xff0c;但并不一定是全局上同胚的。&#xff0…

Springboot项目打包war配置详解

Springboot项目打包war配置详解 1. 排除内置tomcat依赖2. 添加servlet依赖3. 修改打包方式4. 修改主启动类5. 完整pom.xml6. 效果图 1. 排除内置tomcat依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter…

Keil系列教程08_Configuration(二)

1写在前面 本文接着上一篇文章《Keil系列教程07_Configuration&#xff08;一&#xff09;》讲述的工程目标选项的后三项配置&#xff1a;Shortcut Keys快捷键、Text Completion代码完形、Other其他。 这后面三部分内容在该系列教程其它也会牵涉&#xff0c;也是一些常用、重要…

【数字信号处理】带通采样定理及其MATLAB仿真

目录 一、带通采样定理1.1 内容1.2 公式推导 二、MATLAB信号仿真2.1 信号仿真实验2.2 MATLAB代码 三、总结参考 一、带通采样定理 按照奈奎斯特采样定理(低通采样)&#xff0c;采样频率 f s f_{s} fs​ 要大于等于信号中最高频率 f m a x f_{max} fmax​ 的2倍&#xff0c;才…

excel导入,错误数据excel导出

要实现功能&#xff0c;非空校验&#xff0c;如果为空 则下载错误excel&#xff0c;说明错误原因 导入实体 Data AllArgsConstructor NoArgsConstructor public class ExcelDto implements Serializable{/*** 用户名*/ExcelProperty("用户名")private String user…

JVM系统优化实践(22):GC生产环境案例(五)

您好&#xff0c;这里是「码农镖局」CSDN博客&#xff0c;欢迎您来&#xff0c;欢迎您再来&#xff5e; 除了Tomcat、Jetty&#xff0c;另一个常见的可能出现OOM的地方就是微服务架构下的一次RPC调用过程中。笔者曾经经历过的一次OOM就是基于Thrift框架封装出来的一个RPC框架导…

SAP中文界面乱码问题的分析处理实例

近期遇到用户反映主题问题&#xff0c;如下图这样的中文乱码在SAP的菜单和功能界面中随处可见。 经过一系分析检查发现导致问题的原因在于操作系统中的区域格式语言设置&#xff0c;这里用户设置是中文&#xff0c;简体汉字&#xff0c;香港特别行政区。将其变更为中文&#xf…

了解Unity编辑器之组件篇Video(二)

Video Player组件&#xff1a;用于在游戏中播放视频的组件。它提供了一系列属性来控制视频的播放、显示和交互。 1.Source&#xff08;视频源&#xff09;&#xff1a;用于指定视频的来源。可以选择两种不同的视频源类型&#xff1a; &#xff08;1&#xff09;Vieo Clip&#…

如何看待现在的 IT 行业,秋招来临,新人们又该怎么应对?

如何看待现在的 IT 行业&#xff0c;秋招来临&#xff0c;新人们又该怎么应对&#xff1f; 前言24届校招将开启easy模式&#xff1f;IT行业相比其他专业还有优势吗&#xff1f;IT行业真的不看学历吗&#xff1f;面试老不过&#xff0c;我是不是太菜了&#xff1f;怎么看待35岁裁…

【docker】docker部署mysql

目录 一、步骤二、说明三、步骤 一、步骤 1.搜索mysql镜像 2.拉取mysql镜像 3.创建容器 4.操作容器中的mysql 二、说明 1.容器内的网络服务和外部机器不能直接通信 2.容器中部署的mysql端口3306不能被外部机器和宿主机直接通信 3.外部机器和宿主机之间可以直接通信 4.宿主机和…