> 文章列表 > SpingBoot使用Mybatis-Plus操作多数据源,同时操作sqlserver和mysql

SpingBoot使用Mybatis-Plus操作多数据源,同时操作sqlserver和mysql

你是不是曾经在开发中遇到过需要同时操作多个数据库的情况?比如要从MySQL中查数据,再去SQL Server中捞数据,最后还得把处理好的数据塞回MySQL?别急,这篇文章就是你的救星!

需求场景:假设你正在开发一个小工具,需要从MySQL的表中获取一个字段,然后用这个字段去SQL Server中查询相关数据,最后把处理好的数据再塞回MySQL的另一张表里。听起来像不像在玩“数据搬运工”游戏?

难点一:首先,你得配置多个数据源。MySQL和SQL Server属于不同的数据库类型,配置起来可不是一键搞定的。其次,你得能动态切换数据源,不然每次都得手动改配置,那简直是要命。

解决办法:这里推荐使用MyBatis-Plus结合若依框架的AOP动态切换数据源的方式。通过自定义注解和AOP切面,你可以轻松实现数据源的动态切换。比如,你可以在方法上加上@DataSource(DataSourceType.SLAVE),程序就会自动切换到SQL Server去执行查询。

思考拓展:其实,多数据源的操作不仅仅局限于MySQL和SQL Server的组合。随着微服务架构的普及,越来越多的系统需要跨数据库访问数据。比如,你可能会遇到需要操作MongoDB、Redis等多种数据库的情况。那么,如何在保持代码简洁的同时,灵活应对这些复杂的数据源配置,就成了一个值得深入探讨的话题。

总之,多数据源操作虽然看起来复杂,但只要掌握了合适的工具和方法,其实也没那么可怕。毕竟,技术嘛,不就是用来解决问题的吗?

SpingBoot使用Mybatis-Plus操作多数据源,同时操作sqlserver和mysql

目录

需求场景

需求逻辑:

难点:

说明:

代码

pom.xml依赖只贴sqlserver的

文件目录

yml配置文件 

DataSource自定义注解

DataSourceAspect类文件

DruidConfig类

DruidProperties类

DynamicDataSource

DynamicDataSourceContextHolder

spring工具类

DataSourceType 

如何使用

 遇到的报错

问题一

问题二

问题三

问题四

 总结


需求场景

在学校或者自己练习的demo,基本都是配置一个数据源即可,基本都是使用MySQL,可是在工作中经常会出现很多不一样的场景和需求。

这里说一下我的需求:我需要从mysql数据库中通过一个字段去sqlserver数据库中读取数据,然后封装数据再录入到mysql中库中对应的表

需求逻辑:

  1. 首先需要查mysql的表,从表中获取字段
  2. 其次要用这个字段去sqlserver中查询我们想要的数据
  3. 最后解析数据封装,录入到mysql的另一个表中

难点:

  1. 如何配置多数据源
  2. 配置了多数据源如何动态的去查询数据

说明:

    尝试了很多方法,发现都不行,各种报错,在文章的后面会把我部署过程中的报错全部展现一下,方便大家排错,最后用的若依框架中的aop动态切换数据源的方式

后台手册 | RuoYi使用若依快速构建web应用程序http://doc.ruoyi.vip/ruoyi/document/htsc.html#%E5%A4%9A%E6%95%B0%E6%8D%AE%E6%BA%90

代码

pom.xml依赖只贴sqlserver的

  <!-- SQL Server --><dependency><groupId>com.microsoft.sqlserver</groupId><artifactId>mssql-jdbc</artifactId><version>9.2.1.jre15</version></dependency>

文件目录

yml配置文件 

这里的数据源分为master 和 slave  如果你有三个四个五个数据源在这里都可以配置,只要在后面的config类配置中进行配置即可 

#dev环境 mysql 7.0
spring:application:name: data-spiderdatasource:type: com.alibaba.druid.pool.DruidDataSource# 配置多数据源master:Url: jdbc:mysql://localhost:3306/库名?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=falseusername: rootpassword: root#driver-class-name: com.mysql.cj.jdbc.Driverslave:enabled: trueUrl: jdbc:sqlserver://localhost:1433;DatabaseName=testusername: sapassword: 123456driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriverdruid:inital-size: 10#最大连接数max-active: 50#最小连接数min-idle: 10#获取链接等待超时时间max-wait: 5000pool-prepared-statements: true #是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。max-pool-prepared-statement-per-connection-size: 20validation-query: SELECT 1validation-query-timeout: 20000test-on-borrow: false  #申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。test-on-return: false #归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。test-while-idle: true #建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。time-between-eviction-runs-millis: 60000  #配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒min-evictable-idle-time-millis: 300000  #一个连接在池中最小生存的时间,单位是毫秒max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位是毫秒#StatViewServlet配置。(因为暴露的监控信息比较敏感,支持密码加密和访问ip限定)web-stat-filter:enableed: truestat-view-servlet:enabled: trueurl-pattern: /druid/*#可以增加访问账号密码【去掉注释就可以】#login-username: admin#login-password: adminfilter:stat:enabled: true# 慢SQL 记录log-slow-sql: trueslow-sql-millis: 1000merge-sql: truewall:config:multi-statement-allow: true

DataSource自定义注解

通过注解的方式动态切换数据源,很灵活

package com.hhubrain.common.annotation;import com.hhubrain.model.enums.DataSourceType;import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/* 自定义多数据源切换注解 优先级:先方法,后类,如果方法覆盖了类上的数据源类型,以方法的为准,否则以类上的为准 @author */
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource
{/* 切换数据源名称*/public DataSourceType value() default DataSourceType.MASTER;
}

DataSourceAspect类文件

aop切面思想

package com.hhubrain.common.aop;import java.util.Objects;import com.hhubrain.common.annotation.DataSource;
import com.hhubrain.datasource.DynamicDataSourceContextHolder;
import com.hhubrain.utils.StringUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/ 多数据源处理* * @author*/
@Aspect
@Order(1)
@Component
public class DataSourceAspect
{protected Logger logger = LoggerFactory.getLogger(getClass());//这里的地址就是上面的DataSource注解的位置@Pointcut("@annotation(com.hhubrain.common.annotation.DataSource)"+ "|| @within(com.hhubrain.common.annotation.DataSource)")public void dsPointCut(){}@Around("dsPointCut()")public Object around(ProceedingJoinPoint point) throws Throwable{DataSource dataSource = getDataSource(point);if (StringUtils.isNotNull(dataSource)){DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());}try{return point.proceed();}finally{// 销毁数据源 在执行方法之后DynamicDataSourceContextHolder.clearDataSourceType();}}/* 获取需要切换的数据源*/public DataSource getDataSource(ProceedingJoinPoint point){MethodSignature signature = (MethodSignature) point.getSignature();DataSource dataSource = AnnotationUtils.findAnnotation(signature.getMethod(), DataSource.class);if (Objects.nonNull(data