springboot整合多数据源的配置以及动态切换数据源,注解切换数据源

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 RDS PostgreSQL,高可用系列 2核4GB
简介: springboot整合多数据源的配置以及动态切换数据源,注解切换数据源

在许多应用程序中,可能需要使用多个数据库或数据源来处理不同的业务需求。Spring Boot提供了简便的方式来配置和使用多数据源,使开发人员能够轻松处理多个数据库连接。如果你的项目中可能需要随时切换数据源的话,那我这篇文章可能能帮助到你

ℹ️:这里对于pom文件中坐标的引入我就不多赘言了

配置文件

1️⃣:properties文件中

# 数据源配置
spring.datasource.mysql.primary.url=jdbc:mysql://127.0.0.1:3351/tally_book?characterEncoding=utf8&serverTimezone=UTC
spring.datasource.mysql.primary.username=root
spring.datasource.mysql.primary.password=123456
spring.datasource.mysql.primary.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据源配置
spring.datasource.mysql.slave1.url=jdbc:mysql://127.0.0.1:3351/dingding_mid?characterEncoding=utf8&serverTimezone=UTC
spring.datasource.mysql.slave1.username=root
spring.datasource.mysql.slave1.password=123456
spring.datasource.mysql.slave1.driver-class-name=com.mysql.cj.jdbc.Driver

上面的配置文件中我只写了两个源,而且都是mysql 的,primary和slave1就是区分

2️⃣:配置类实现多数据源配置

package com.todoitbo.tallybookdasmart.config;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.todoitbo.tallybookdasmart.multiDataSource.DataSourceType;
import com.todoitbo.tallybookdasmart.multiDataSource.DynamicDataSource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.*;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
 * @author xiaobo
 * @date 2023/5/19
 */
@Configuration
@Slf4j
public class MultiDataSourceConfig {
    @Bean
    public PlatformTransactionManager platformTransactionManager(DataSource dynamicDataSource) {
        return new DataSourceTransactionManager(dynamicDataSource);
    }
    @Bean
    @Primary
    @DependsOn("primaryDataSource")
    public DataSource dynamicDataSource(@Qualifier(DataSourceType.PRIMARY) DataSource primaryDataSource,
                                        @Qualifier(DataSourceType.SECOND) DataSource secondDataSource) {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 1.设置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(primaryDataSource);
        // 2.配置多数据源
        Map<Object, Object> map = new HashMap<>();
        map.put(DataSourceType.PRIMARY, primaryDataSource);
        map.put(DataSourceType.SECOND, secondDataSource);
        // 3.存放数据源集
        dynamicDataSource.setTargetDataSources(map);
        return dynamicDataSource;
    }
    @Bean(name = DataSourceType.PRIMARY)
    @ConfigurationProperties(prefix = "spring.datasource.mysql.primary")
    public DataSource primaryDataSource() {
        log.info("主数据库连接池创建中.......");
        return DruidDataSourceBuilder.create().build();
    }
    @Bean(name = DataSourceType.SECOND)
    @ConfigurationProperties(prefix = "spring.datasource.mysql.slave1")
    public DataSource secondDataSource() {
        log.info("second数据库连接池创建中.......");
        return DruidDataSourceBuilder.create().build();
    }
}

3️⃣:自定义注解实现,可使用自定义注解来切换数据源

package com.todoitbo.tallybookdasmart.multiDataSource;
import java.lang.annotation.*;
/**
 * description: 自定义注解,标记数据源
 *
 * @author bo
 * @version 1.0
 * @date 2023/5/19 08:45
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface DataSource {
    String value() default DataSourceType.PRIMARY;
}

4️⃣:定义一个切面类

这段代码是一个切面类DataSourceAspect,用于在方法调用前后切换数据源。以下是代码的解释:

  1. @Aspect:指定该类为切面类,用于定义切面的切入点和增强逻辑。
  2. @Order(value=1):指定切面的执行顺序,数值越小优先级越高。
  3. @Component:将该切面类声明为Spring的组件,使其可以被自动扫描并装配到Spring容器中。
  4. @Pointcut(value = "execution(* com.todoitbo.tallybookdasmart.service.*.*(..)) || execution(* com.todoitbo.tallybookdasmart.*.*(..))"):定义切入点表达式,指定需要切入的目标方法。
  5. @Around("dataSourcePointCut()"):定义环绕通知,表示在目标方法执行前后执行切面逻辑。
  6. public Object around(ProceedingJoinPoint joinPoint) throws Throwable:环绕通知方法,包含切面逻辑。
  7. 在方法中通过反射获取目标方法的注解信息,判断是否存在@DataSource注解,并获取注解中设置的数据源名称。
  8. 调用DataSourceContextHolder.setDataSource(dataSource)方法,将获取到的数据源名称设置到当前线程的上下文中。
  9. 调用joinPoint.proceed()方法,继续执行目标方法。
  10. finally块中调用DataSourceContextHolder.clearDataSourceType()方法,清除当前线程中存储的数据源信息。
package com.todoitbo.tallybookdasmart.multiDataSource;
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.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
 * @author xiaobo
 */
@Aspect
@Order(value=1)
@Component
@Slf4j
public class DataSourceAspect {
    /** 定义切入点表达式*/
    @Pointcut(value = "execution(* com.todoitbo.tallybookdasmart.service.*.*(..)) || execution(* com.todoitbo.tallybookdasmart.*.*(..))")
    public void dataSourcePointCut() {
    }
    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object target = joinPoint.getTarget();
        String method = joinPoint.getSignature().getName();
        Class<?> classz = target.getClass();
        Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();
        try {
            // 使用反射获取目标类中指定方法名和参数类型的方法对象
            Method m = classz.getMethod(method, parameterTypes);
            // 设置默认的数据源名称
            String dataSource = DataSourceType.PRIMARY;
            // 判断方法是否被@DataSource注解标记。
            if (m.isAnnotationPresent(DataSource.class)) {
                // 通过getAnnotation()方法获取方法上的@DataSource注解对象。
                DataSource ds = m.getAnnotation(DataSource.class);
                // 获取注解对象中设置的数据源名称
                dataSource = ds.value();
            }
            // 将获取到的数据源名称设置到当前线程的上下文中
            DataSourceContextHolder.setDataSource(dataSource);
            // 继续执行目标方法
            return joinPoint.proceed();
        } finally {
            DataSourceContextHolder.clearDataSourceType();
        }
    }
}

5️⃣:存储和获取当前线程数据源的上下文工具类

这段代码是一个用于存储和获取当前线程数据源的上下文工具类。它使用了Netty的FastThreadLocal来实现线程本地的快速存取。

  1. 创建FastThreadLocal对象:在类中定义了一个名为CONTEXT_HOLDERFastThreadLocal对象,用于存储当前线程的数据源信息。
  2. 设置数据源:setDataSource方法用于将数据源名称设置到当前线程的上下文中。通过调用CONTEXT_HOLDER.set(dataSource),将数据源名称存储在当前线程中。
  3. 获取数据源:getDataSource方法用于从当前线程的上下文中获取数据源名称。通过调用CONTEXT_HOLDER.get(),可以获取当前线程的数据源名称。
  4. 清除数据源:clearDataSourceType方法用于清除当前线程中存储的数据源信息。通过调用CONTEXT_HOLDER.remove(),可以清除当前线程中的数据源信息。
package com.todoitbo.tallybookdasmart.multiDataSource;
import io.netty.util.concurrent.FastThreadLocal;
/**
 * description: 存储和获取当前线程数据源的上下文工具类
 *
 * @author bo
 * @version 1.0
 * @date 2023/5/19 08:44
 */
public class DataSourceContextHolder {
    /**
     * 创建FastThreadLocal对象,存储当前线程的数据源信息
     */
    private static final FastThreadLocal<String> CONTEXT_HOLDER = new FastThreadLocal<String>();
    /**
     * 设置数据源
     */
    public static void setDataSource(String dataSource) {
        CONTEXT_HOLDER.set(dataSource);
    }
    /**
     * 获取数据源
     */
    public static String getDataSource() {
        return CONTEXT_HOLDER.get();
    }
    /**
     * 清除数据源
     */
    public static void clearDataSourceType() {
        CONTEXT_HOLDER.remove();
    }
}

6️⃣:数据源类型

package com.todoitbo.tallybookdasmart.multiDataSource;
/**
 * @author xiaobo
 */
public class DataSourceType {
    public static final String PRIMARY = "primaryDataSource";
    public static final String SECOND = "secondDataSource";
}

7️⃣:根据当前线程中的数据源上下文获取对应的数据源

package com.todoitbo.tallybookdasmart.multiDataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
 * description: 根据当前线程中的数据源上下文获取对应的数据源。
 *
 * @author bo
 * @version 1.0
 * @date 2023/5/19 08:46
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDataSource();
    }
}

具体实现

在service的实现类的方法上加入注解即可

package com.todoitbo.tallybookdasmart.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.todoitbo.tallybookdasmart.entity.TbConfig;
import com.todoitbo.tallybookdasmart.mapper.TbConfigMapper;
import com.todoitbo.tallybookdasmart.multiDataSource.DataSource;
import com.todoitbo.tallybookdasmart.multiDataSource.DataSourceType;
import com.todoitbo.tallybookdasmart.service.ITbConfigService;
import com.todoitbo.tallybookdasmart.service.base.BaseServiceImpl;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
 * (TbConfig)服务
 *
 * @author bo
 * @since 2023-04-18 21:13:14
 */
@Service
public class TbConfigServiceImpl extends BaseServiceImpl<TbConfigMapper,TbConfig> implements ITbConfigService {
    @Resource
    protected TbConfigMapper mapper;
    @Override
    @DataSource(DataSourceType.SECOND)
    public List<TbConfig> testList() {
        return mapper.selectList(new QueryWrapper<>());
    }
}

效果图:

⚠️:这里只是想展示他确实是走了从数据源了

相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
3月前
|
缓存 Java API
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的配置
本文介绍了在Spring Boot中配置Swagger2的方法。通过创建一个配置类,添加`@Configuration`和`@EnableSwagger2`注解,使用Docket对象定义API文档的详细信息,包括标题、描述、版本和包路径等。配置完成后,访问`localhost:8080/swagger-ui.html`即可查看接口文档。文中还提示了可能因浏览器缓存导致的问题及解决方法。
126 0
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的配置
|
3月前
|
Java 关系型数据库 数据库
微服务——SpringBoot使用归纳——Spring Boot事务配置管理——Spring Boot 事务配置
本文介绍了 Spring Boot 中的事务配置与使用方法。首先需要导入 MySQL 依赖,Spring Boot 会自动注入 `DataSourceTransactionManager`,无需额外配置即可通过 `@Transactional` 注解实现事务管理。接着通过创建一个用户插入功能的示例,展示了如何在 Service 层手动抛出异常以测试事务回滚机制。测试结果表明,数据库中未新增记录,证明事务已成功回滚。此过程简单高效,适合日常开发需求。
170 0
|
2月前
|
人工智能 缓存 自然语言处理
保姆级Spring AI 注解式开发教程,你肯定想不到还能这么玩!
这是一份详尽的 Spring AI 注解式开发教程,涵盖从环境配置到高级功能的全流程。Spring AI 是 Spring 框架中的一个模块,支持 NLP、CV 等 AI 任务。通过注解(如自定义 `@AiPrompt`)与 AOP 切面技术,简化了 AI 服务集成,实现业务逻辑与 AI 基础设施解耦。教程包含创建项目、配置文件、流式响应处理、缓存优化及多任务并行执行等内容,助你快速构建高效、可维护的 AI 应用。
|
3月前
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于注解的整合
本文介绍了Spring Boot集成MyBatis的两种方式:基于XML和注解的形式。重点讲解了注解方式,包括@Select、@Insert、@Update、@Delete等常用注解的使用方法,以及多参数时@Param注解的应用。同时,针对字段映射不一致的问题,提供了@Results和@ResultMap的解决方案。文章还提到实际项目中常结合XML与注解的优点,灵活使用两者以提高开发效率,并附带课程源码供下载学习。
75 0
|
3月前
|
Java 数据库连接 数据库
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——MyBatis 介绍和配置
本文介绍了Spring Boot集成MyBatis的方法,重点讲解基于注解的方式。首先简述MyBatis作为持久层框架的特点,接着说明集成时的依赖导入,包括`mybatis-spring-boot-starter`和MySQL连接器。随后详细展示了`properties.yml`配置文件的内容,涵盖数据库连接、驼峰命名规范及Mapper文件路径等关键设置,帮助开发者快速上手Spring Boot与MyBatis的整合开发。
188 0
|
3月前
|
缓存 Java 应用服务中间件
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——依赖导入和Thymeleaf相关配置
在Spring Boot中使用Thymeleaf模板,需引入依赖`spring-boot-starter-thymeleaf`,并在HTML页面标签中声明`xmlns:th=&quot;http://www.thymeleaf.org&quot;`。此外,Thymeleaf默认开启页面缓存,开发时建议关闭缓存以实时查看更新效果,配置方式为`spring.thymeleaf.cache: false`。这可避免因缓存导致页面未及时刷新的问题。
91 0
|
SQL Java
springboot自定义注解收集操作日志
springboot自定义注解收集操作日志
367 0
springboot自定义注解收集操作日志
|
SQL Java
springboot高级功能(四)业务实战,自定义注解收集操作日志
springboot高级功能(四)业务实战,自定义注解收集操作日志
496 0
|
1月前
|
JavaScript 前端开发 Java
制造业ERP源码,工厂ERP管理系统,前端框架:Vue,后端框架:SpringBoot
这是一套基于SpringBoot+Vue技术栈开发的ERP企业管理系统,采用Java语言与vscode工具。系统涵盖采购/销售、出入库、生产、品质管理等功能,整合客户与供应商数据,支持在线协同和业务全流程管控。同时提供主数据管理、权限控制、工作流审批、报表自定义及打印、在线报表开发和自定义表单功能,助力企业实现高效自动化管理,并通过UniAPP实现移动端支持,满足多场景应用需求。
176 1
|
2月前
|
前端开发 Java 关系型数据库
基于Java+Springboot+Vue开发的鲜花商城管理系统源码+运行
基于Java+Springboot+Vue开发的鲜花商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的鲜花商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。技术学习共同进步
231 7