Springboot 配置动态多数据源(Mybatis-plus)

2023-10-30

前言:在项目中需要用到动态切换多数据源,查阅Mybatis-plus文档得知可以通过@DS注解,但该方法主要针对不同内容的数据源,而目前场景是相同内容的数据库需要在运行时根据请求头动态切换,因此文档方法不适用。

注意,不要使用dynamic-datasource-spring-boot-starter依赖包。

一、动态多数据源的场景

  1. 应用不拆,数据库拆
  2. 读写分离

二、动态多数据源的实现

网上文章非常多,大体思路都差不多,笔者在这里不重复放置代码了,例如:《springboot 中动态切换数据源》
不过目前找到的文章方法在项目整合了Mybatis-plus的情况下基本都有问题,以下是这几天遇到的问题和解决方案。

三、问题与解决方案

1. 找不到事务管理器

No qualifying bean of type 'org.springframework.transaction.TransactionManager

可以参照《mybatis-plus多数据源事务报错 》,在Springboot中,每一个事务管理器都需要对应一个datasource,而多数据源操作时,需要在@Transactional注解中指定事务管理器,或者配置默认事务管理器。
不过笔者在《springboot动态切换多个数据源》中发现,可以直接将自定义的dynamicDataSource作为dataSource传给事务管理器,并设置为默认事务管理器,这样就不用配置多个事务管理器了,代码如下:

    @Bean(name = "platformTransactionManager")
    @Primary
    public PlatformTransactionManager platformTransactionManager() {
        PlatformTransactionManager transactionManager = new DataSourceTransactionManager(dynamicDataSource());
        return transactionManager;
    }

2. DataSource没有初始化

DataSource router not initialized

这个问题比较奇特,具体解决方法是在stack overflow中看到的。在自定义的DynamicDataSource类的实现方法中添加如下一行代码:

dataSource.afterPropertiesSet();

该方法目的是让Bean设置好所有属性后再执行初始化操作。

3. 找不到数据源

Invalid bound statement (not found)

这个问题是最坑的,具体原因参照《mybatis升级为mybatis-plus踩到的坑》,而网上很多多数据源文章都没有提到这一点。解决方法也很简单,就是将配置的SqlSessionFactory改为MybatisSqlSessionFactoryBean。

    @Bean
    public MybatisSqlSessionFactoryBean SqlSessionFactory()
            throws Exception {
        MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dynamicDataSource()); // dynamicDataSource()为自己定义的实现方法
        sqlSessionFactoryBean.setMapperLocations(
                new PathMatchingResourcePatternResolver()
                        .getResources("classpath*:Mapper/*.xml") // 填写自己的XML路径
        );
        return sqlSessionFactoryBean;
    }

该问题的起源,也是因为导入了Mybatis plus才出现的,导入mybatis-plus-boot-starter依赖包,可以看到有一个MybatisPlusAutoConfiguration类。
在这里插入图片描述
点进去,发现有一个这样的Bean,以及注解TODO 使用 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBean,以下都是源码。所以可以知道,在Mybatis plus框架中,使用的都是封装后的MybatisSqlSessionFactoryBean。

    @Bean
    @ConditionalOnMissingBean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        // TODO 使用 MybatisSqlSessionFactoryBean 而不是 SqlSessionFactoryBean
        MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setVfs(SpringBootVFS.class);
        if (StringUtils.hasText(this.properties.getConfigLocation())) {
            factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
        }
        applyConfiguration(factory);
        if (this.properties.getConfigurationProperties() != null) {
            factory.setConfigurationProperties(this.properties.getConfigurationProperties());
        }
        if (!ObjectUtils.isEmpty(this.interceptors)) {
            factory.setPlugins(this.interceptors);
        }
        if (this.databaseIdProvider != null) {
            factory.setDatabaseIdProvider(this.databaseIdProvider);
        }
        if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
            factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
        }
        if (this.properties.getTypeAliasesSuperType() != null) {
            factory.setTypeAliasesSuperType(this.properties.getTypeAliasesSuperType());
        }
        if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
            factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
        }
        if (!ObjectUtils.isEmpty(this.typeHandlers)) {
            factory.setTypeHandlers(this.typeHandlers);
        }
        if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
            factory.setMapperLocations(this.properties.resolveMapperLocations());
        }

        // TODO 对源码做了一定的修改(因为源码适配了老旧的mybatis版本,但我们不需要适配)
        Class<? extends LanguageDriver> defaultLanguageDriver = this.properties.getDefaultScriptingLanguageDriver();
        if (!ObjectUtils.isEmpty(this.languageDrivers)) {
            factory.setScriptingLanguageDrivers(this.languageDrivers);
        }
        Optional.ofNullable(defaultLanguageDriver).ifPresent(factory::setDefaultScriptingLanguageDriver);

        // TODO 自定义枚举包
        if (StringUtils.hasLength(this.properties.getTypeEnumsPackage())) {
            factory.setTypeEnumsPackage(this.properties.getTypeEnumsPackage());
        }
        // TODO 此处必为非 NULL
        GlobalConfig globalConfig = this.properties.getGlobalConfig();
        // TODO 注入填充器
        if (this.applicationContext.getBeanNamesForType(MetaObjectHandler.class,
            false, false).length > 0) {
            MetaObjectHandler metaObjectHandler = this.applicationContext.getBean(MetaObjectHandler.class);
            globalConfig.setMetaObjectHandler(metaObjectHandler);
        }
        // TODO 注入主键生成器
        if (this.applicationContext.getBeanNamesForType(IKeyGenerator.class, false,
            false).length > 0) {
            IKeyGenerator keyGenerator = this.applicationContext.getBean(IKeyGenerator.class);
            globalConfig.getDbConfig().setKeyGenerator(keyGenerator);
        }
        // TODO 注入sql注入器
        if (this.applicationContext.getBeanNamesForType(ISqlInjector.class, false,
            false).length > 0) {
            ISqlInjector iSqlInjector = this.applicationContext.getBean(ISqlInjector.class);
            globalConfig.setSqlInjector(iSqlInjector);
        }
        // TODO 设置 GlobalConfig 到 MybatisSqlSessionFactoryBean
        factory.setGlobalConfig(globalConfig);
        return factory.getObject();
    }

4. jdbc not connection问题
该问题主要需要检查配置文件,尤其是url要改成jdbc-url,格式如下

spring:
  datasource:
    database01: 
      jdbc-url: 
      username: 
      password: 
      driver-class-name: 
    database02:
      jdbc-url: 
      username: 
      password: 
      driver-class-name: 
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Springboot 配置动态多数据源(Mybatis-plus) 的相关文章

随机推荐

  • 浅谈java中的锁

    JAVA中的锁 自旋锁 当一个线程要获取锁的时候 该锁被其他线程获取 那么该线程将循环等待 不判断该锁是否能够被成功获取 直到获取到该锁才会退出循环 自旋锁实现例子 通过CAS操作实现加锁与解锁逻辑 循环等待占用锁的线程解锁 自旋锁代码实现
  • Ubuntu 16.04系统安装jdk1.8

    笔者环境 主系统Win7 VMware Workstation 12 Player虚拟机 虚拟机系统ubuntu 16 04 desktop amd64 安装jdk jdk 8u102 linux 一 在Oracle官方网站下载JDK 1
  • 跨域问题之手机号码登录验证

    1 构建登录组件 router js 设置路由 import Vue from vue import Router from vue router Vue use Router const router new Router mode hi
  • javaweb jsp的认识

    1 java 程序片 1 在jsp中 之间的书写叫做java 程序片 一个jsp中页面中可以有多个java 程序片 在java 程序片声明的变量 在它们所在的jsp中的所有java 程序片及表达式中都有效 java 程序片中声明的变量 只在
  • 测试用例设计白皮书--判定表驱动分析方法

    测试用例设计白皮书 判定表驱动分析方法 Author Vince 来源 http blog csdn net vincetest 一 方法简介 1 定义 判定表是分析和表达多逻辑条件下执行不同操作的情况的工具 2 判定表的优点 能够将复杂的
  • 基于Java的飞机大战游戏的设计与实现论文

    源码下载 http www byamd xyz hui zong 1 摘 要 现如今 随着智能手机的兴起与普及 加上4G the 4th Generation mobile communication 第四代移动通信技术 网络的深入 越来越
  • 【华为OD机试真题 python】任务总执行时长【2022 Q4

    题目描述 任务总执行时长 任务编排服务负责对任务进行组合调度 参与编排的任务有两种类型 其中一种执行时长为taskA 另一种执行时长为taskB 任务一旦开始执行不能被打断 且任务可连续执行 服务每次可以编排num个任务 请编写一个方法 生
  • idea 没有java文件夹_IntelliJ IDEA右键文件夹没有Java Class文件的原因及解决方法

    问题 在项目里创建文件夹后 发现竟然不能新建class文件 问题详细如下图 原因分析 这里涉及到Sources的作用 Sources 一般用于标注类似 src 这种可编译目录 有时候我们项目当中 可能不单单是 src 目录为可编译的 很可能
  • 讨论scrapy-splash渲染不成功问题?

    url https wenshu court gov cn website wenshu 181107ANFZ0BXSK4 index html docId 75461a02d9714cec9322ab4500147439 由于scrapy
  • java基础类型知识

    原生类 基本数据类型 和强类型 引用类型 1 除了八个基本数据类型都是引用类型 但是八个基本数据类型可转换为包装类 基本数据类型没有方法 强类型有方法 装箱 基本数据类型转换为包装类 拆箱 包装类转换为基本数据类型 byte Byte sh
  • element-ui菜单栏切换页面高亮刷新后不显示问题

    最近做的一个vue的小项目 在点击菜单栏其他页面之后高亮会跟着过去 但是刷新后高亮又会重新跳到最初进入的页面 原因是这样的 element ui官网示例 default active activeIndex 这个控制高亮 然后在data中一
  • qt 比较两个字符串是否相等

    QString str QString fromLocal8Bit 球形 if str compare QString fromLocal8Bit 球形 0 或者 if str QString fromLocal8Bit 球形
  • GMAC接口(4)——编程指南

    Note 基于DWC ether qos控制器 RTL8211F PHY 1 硬件复位PHY 可选 通过GPIO接口 将复位信号 持续至少10ms的低电平脉冲 发送到PHY 2 初始化控制器 a 软复位 软件复位 gmac DMA Mode
  • Emacs 之查看帮助

    Emacs 之查看帮助 Table of Contents 1 Emacs 入门 1 1 查看简单的帮助 1 2 执行elisp代码 1 Emacs 入门 1 1 查看简单的帮助 C h f 查看函数的文档 describe functio
  • [人工智能-深度学习-81]:视觉 - 视频换脸软件大全

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 122504846 目录 第1名 ZAO
  • 腾讯云轻量2核4G5M服务器_CPU内存_流量_带宽_系统盘

    腾讯云轻量2核4G5M服务器 CPU内存流量带宽系统盘性能测评 轻量应用服务器2核4G5M带宽 免费500GB月流量 60GB系统盘SSD盘 5M带宽下载速度可达640KB 秒 流量超额按照0 8元每GB支付流量费 轻量2核4G5M服务器一
  • 华为ensp配置实验大全(免费&持续更新)

    点开一篇文章 分享 要收费 点开一篇文章 大全 但就两个实验 点开一篇文章 详细 但全截图 我忽略了最重要的东西 产品说明书 产品说明书优势 规范 详细 此文寻找官方手册中的配置实验 博客好文 基础实验为主 你知道的答案 不是下一次作业的答
  • OV7670循迹算法整理

    资源在这 http download csdn net download hello world12138 9910603 2016 7 3 1 今天发现一个比较尴尬的问题 之前摄像头采集到的图像和现实中我显示的图像的旋转方向根本就是反的
  • vivado路径最大时钟约束_【Vivado使用误区与进阶】XDC约束技巧之时钟篇

    Xilinx 的新一代设计套件Vivado中引入了全新的约束文件XDC 在很多规则和技巧上都跟上一代产品ISE中支持的UCF大不相同 给使用者带来许多额外挑战 Xilinx工具专家告诉你 其实用好XDC很容易 只需掌握几点核心技巧 并且时刻
  • Springboot 配置动态多数据源(Mybatis-plus)

    前言 在项目中需要用到动态切换多数据源 查阅Mybatis plus文档得知可以通过 DS注解 但该方法主要针对不同内容的数据源 而目前场景是相同内容的数据库需要在运行时根据请求头动态切换 因此文档方法不适用 注意 不要使用dynamic