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 {
    @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.mfbz.cn/a/44240.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【RabbitMQ(day1)】RabbitMQ的概述和安装

入门RabbitMQ 一、RabbitMQ的概述二、RabbitMQ的安装三、RabbitMQ管理命令行四、RabbitMQ的GUI界面 一、RabbitMQ的概述 MQ&#xff08;Message Queue&#xff09;翻译为消息队列&#xff0c;通过典型的【生产者】和【消费者】模型&#xff0c;生产者不断向消息队列中生产消息&…

SIGIR 2023 | 语音让对话推荐更easy,火山语音联合新加坡科学研究院发布业内首个语音对话推荐数据集

近年来&#xff0c;推荐系统在工业界取得了巨大成功&#xff0c;甚至成为互联网发展中不可或缺的增长引擎&#xff0c;基于此研究者们也在积极探索推荐系统的新形态&#xff0c;其中对话推荐系统&#xff08;Conversational Recommender System&#xff0c;简称CRS&#xff09;…

Mac上安装sshfs

目录 写在前面安装使用参考完 写在前面 1、本文内容 Mac上安装sshfs 2、平台 mac 3、转载请注明出处&#xff1a; https://blog.csdn.net/qq_41102371/article/details/130156287 安装 参考&#xff1a;https://ports.macports.org/port/sshfs/ 通过port安装 点击啊insta…

Qt/C++音视频开发49-多级连保存和推流设计(同时保存到多个文件/推流到多个平台)

一、前言 近期遇到个用户需要多级联的保存和推流&#xff0c;在ffmpegsave多线程保存类中实现这个功能&#xff0c;越简单越好&#xff0c;就是在推流的同时&#xff0c;能够开启自动转储功能&#xff0c;一边推流的同时一边录像保存到本地视频文件。最初设想的一个方案是new两…

【MySQL】之复合查询

【MySQL】之复合查询 基本查询多表查询笛卡尔积自连接子查询单行子查询多行子查询多列子查询在from子句中使用子查询 合并查询小练习 基本查询 查询工资高于500或岗位为MANAGER的雇员&#xff0c;同时还要满足他们的姓名首字母为大写的J按照部门号升序而雇员的工资降序排序使用…

源码对接微软Azure OpenAI 规范注意点

众所周知&#xff0c;我们是访问不通OpenAI官方服务的&#xff0c;但是我们可以自己通过代理或者使用第三方代理访问接口 现在新出台的规定禁止使用境外的AI大模型接口对境内客户使用&#xff0c;所以我们需要使用国内的大模型接口 国内的效果真的很差&#xff0c;现在如果想合…

深圳湾晚霞下的职场分享:723深圳COC社区活动回顾

文章目录 深圳湾晚霞下的职场分享&#xff1a;723深圳COC社区活动回顾前言人物观察架构师李肯连续创业者石云升鸿蒙布道师坚果 职友分享个人分享 后记最后分享一波&#xff1a;深圳湾晚霞美图&#xff01; 深圳湾晚霞下的职场分享&#xff1a;723深圳COC社区活动回顾 前言 ​…

vue项目入口和个文件之间的关系

vue项目入口和个文件之间的关系 1、代码的执行顺序和引入关系 1、代码的执行顺序和引入关系

时钟分频器

文章目录 一、8分频二、n倍时钟分频器 一、8分频 8倍时钟分频器是一种电路或设备&#xff0c;用于将输入时钟信号的频率分成原来的1/8。它可以在数字电子系统中用于将高频时钟信号降低到较低的频率&#xff0c;以满足特定的系统需求。 在这个电路中&#xff0c;CLK是输入的时钟…

【wxWidgets】剪贴板和拖放操作

【wxWidgets】剪贴板和拖放操作 使用剪贴板传输数据时应用程序间的一种交互方式 剪贴板和拖放操作在wxWidgets中共享了一些类来实现数据的传输 数据对象 wxDataObject类时剪贴板操作和拖放操作的核心&#xff0c;该类实例代表了拖放操作中鼠标拖拽的事物和剪贴板中拷贝和粘贴…

10分钟搭建链路追踪平台

随着项目越来越多&#xff0c;相互调用越来越复杂&#xff0c;搭建一个可视化的链路追踪平台显得尤为重要&#xff0c;今天给大家介绍的是zipkin&#xff0c;一个轻量级的零侵入的链路追踪平台&#xff0c;看我怎么10分钟给大家搭建出来。 1&#xff0c;介绍 zipkin官网&…

机器人制作开源方案 | 智能垃圾桶

1. 功能说明 智能垃圾桶是一种利用物联网技术和智能感知能力的智能设备&#xff0c;旨在提高垃圾分类和处理的效率。通常具备以下特点和功能&#xff1a; ① 智能感知&#xff1a;智能垃圾桶配备各种传感器&#xff0c;如压力传感器、红外线传感器等&#xff0c;可以实时感知…

FPGA开发:按键消抖

按键是FPGA开发板上的重要交互元件&#xff0c;因为按键的内部的结构设计&#xff0c;在按下和松开按键时&#xff0c;按键会无法避免地产生机械抖动&#xff0c;因此要对按键输入进行特殊处理&#xff0c;否则可能会因为机械抖动产生意外的重复触发。 按键消抖有很多方法&…

超详细,自动化测试allure测试报告实战(总结)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 allure可以输出非…

python机器学习(四)线性代数回顾、多元线性回归、多项式回归、标准方程法求解、线性回归案例

回顾线性代数 矩阵 矩阵可以理解为二维数组的另一种表现形式。A矩阵为三行两列的矩阵&#xff0c;B矩阵为两行三列的矩阵&#xff0c;可以通过下标来获取矩阵的元素&#xff0c;下标默认都是从0开始的。 A i j : A_{ij}: Aij​:表示第 i i i行&#xff0c;第 j j j列的元素。…

记录--关于前端的音频可视化-Web Audio

这里给大家分享我在网上总结出来的一些知识&#xff0c;希望对大家有所帮助 背景 最近听音乐的时候&#xff0c;看到各种动效&#xff0c;突然好奇这些音频数据是如何获取并展示出来的&#xff0c;于是花了几天功夫去研究相关的内容&#xff0c;这里只是给大家一些代码实例&…

数字孪生:未来科技的新前沿

数字孪生作为一项新兴的研究方向&#xff0c;正逐渐成为科技界的焦点。它是将现实世界中的实体、系统或过程通过数字化手段进行建模、仿真和分析&#xff0c;形成与实体相对应的数字化副本。数字孪生的发展为我们带来了无限的想象空间&#xff0c;以及解决现实问题的新途径。 在…

如何有效跟踪你的计费时间?

对于自由职业者、小型企业和远程团队来说&#xff0c;时间跟踪是必需的。了解自己在单个项目或任务上投入了多少时间&#xff0c;可以帮助他们有效管理资源和优化工作流程。 然而&#xff0c;在向客户收费时&#xff0c;时间跟踪多了一层复杂性&#xff1a;不仅需要跟踪所花费…

在Win11的WSL子系统Ubuntu上安装Gnome桌面环境

目录 1. 使用 WSL 在 Win11 上安装 Linux 2. 安装Ubuntu 22.04默认Gnome桌面环境 2.1更新Ubuntu 22.04软件包 2.2 安装Ubuntu桌面环境 2.3 重启服务 2.4 重启Ubuntu 22.04系统 2.5 登录Gnome桌面环境 在Win11上安装ubuntu版linux系统并实现默认Gnome桌面环境&#xff08…

Vue中TodoList案例_总结

完整项目&#xff1a; App.vue <template><div id"root"><div class"todo-container"><div class"todo-wrap"><MyHeader :addTodo"addTodo"/><MyList :todos"todos" :checkTodo"che…