基于SpringBoot多数据源解决方案
最近在学习SpringBoot的时候,需要同时用两个不同的数据库连接服务,在网上学习了之后,下文以连接一个MySQL数据库和一个SqlServer数据库为例。
配置数据源连接信息
在配置文件中,配置对应的数据库连接信息,相比于单数据源时连接信息的url属性在多数据源时应该为jdbc-url,请注意下图红色部分:

application.yml
spring:datasource:webproject:type: mysqldriver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://localhost:3306/webproject?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghaiusername: password: workcontent:type: sqlServerdriver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriverjdbc-url: jdbc:sqlserver://localhost:1433;databaseName=myDatabase;encrypt=true;trustServerCertificate=true;characterEncoding=utf8;username: password:
Maven配置数据库驱动
由于新版的官方Sqlserver驱动不支持TLSv1, TLSv1.1,我选择了较老的数据库驱动程序。
<!-- sqlserver驱动 -->
<dependency><groupId>com.microsoft.sqlserver</groupId><artifactId>mssql-jdbc</artifactId><version>7.4.1.jre11</version>
</dependency>
<!-- MYSQL驱动 -->
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version>
</dependency>
创建数据源配置类
以下文中的 WorkContentDataSourceConfig 为例,基本的流程如下:
配置类声明
配置要该数据源相关的Mapper所在的的包扫描和要注入的Sql工厂实例

创建数据源DataSource
@ConfigurationProperties 用于注入application.yml中配置的数据库连接信息
@Primary 默认指定当前数据库,多数据源下需要配置@Primary,不然SpringBoot会找不到数据源注入,后续步骤最好也加上@Primary

创建Sql工厂,注册DataSource
@Qualifier 用于选择注入的bean对象

创建事务管理器

创建Sql模版对象

具体配置类信息如下:
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
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 javax.sql.DataSource;/*** @author yamu* @version 1.0* @description 工作库* @date 2025/1/23 10:49*/
@Configuration
@MapperScan(basePackages = {"org.cqw.baseproject.dao.workcontent"},sqlSessionFactoryRef = "workContentSqlSessionFactory")
public class WorkContentDataSourceConfig {@Bean(name = "workContentDataSource")@ConfigurationProperties(prefix = "spring.datasource.workcontent")@Primarypublic DataSource dataSource() {return DataSourceBuilder.create().build();}@Bean(name = "workContentSqlSessionFactory")@Primarypublic SqlSessionFactory sqlSessionFactory(@Qualifier("workContentDataSource") DataSource dataSource)throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(dataSource);bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/workcontent/*.xml"));return bean.getObject();}@Bean(name = "workContentTransactionManager")@Primarypublic DataSourceTransactionManager transactionManager(@Qualifier("workContentDataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}@Bean(name = "workContentSqlSessionTemplate")@Primarypublic SqlSessionTemplate sqlSessionTemplate(@Qualifier("workContentSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {return new SqlSessionTemplate(sqlSessionFactory);}
}
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;import javax.sql.DataSource;/*** @author yamu* @version 1.0* @description 项目库 * @date 2025/1/23 10:49*/
@Configuration
@MapperScan(basePackages = {"org.cqw.baseproject.dao.webproject"}, sqlSessionFactoryRef = "webProjectSqlSessionFactory")
public class WebProjectDataSourceConfig {@Bean(name = "webProjectDataSource")@ConfigurationProperties(prefix = "spring.datasource.webproject")public DataSource dataSource() {return DataSourceBuilder.create().build();}@Bean(name = "webProjectSqlSessionFactory")public SqlSessionFactory sqlSessionFactory(@Qualifier("webProjectDataSource") DataSource dataSource) throws Exception {SqlSessionFactoryBean bean = new SqlSessionFactoryBean();bean.setDataSource(dataSource);bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/webproject/*.xml"));return bean.getObject();}@Bean(name = "webProjectTransactionManager")public DataSourceTransactionManager transactionManager(@Qualifier("webProjectDataSource") DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}@Bean(name = "webProjectSqlSessionTemplate")public SqlSessionTemplate sqlSessionTemplate(@Qualifier("webProjectSqlSessionFactory") SqlSessionFactory sqlSessionFactory) {return new SqlSessionTemplate(sqlSessionFactory);}
}
创建实体
实体所在的包需要和创建配置类中的第一步的 配置类声明的@MapperScan的basePackages值相对应。
package org.cqw.baseproject.dao.webproject;
@Repository
public interface FunctionMenuDao {FunctionMenu queryById(int id);List<FunctionMenu> queryAll();
}
创建Mapper
由于有多个数据源,所以在/resources/mapper里面需要区分不同的数据库(即创建不同的文件夹,每个数据源扫描自己的mapper
.xml文件),如下图所示,注意和第三步创建Sql工厂,注册DataSource的SqlSessionFactory中bean.setMapperLocations()里面的路径对应上,对于Mapper.xml里面的配置这里就不具体说明了。

FunctionMenuMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="org.cqw.baseproject.dao.webproject.FunctionMenuDao"><select id="queryById" resultType="org.cqw.baseproject.entity.FunctionMenu">SELECT * FROM FunctionMenu WHERE id = #{id}</select><select id="queryAll" resultType="org.cqw.baseproject.entity.FunctionMenu">SELECT * FROM FunctionMenu WHERE isDel = '0' AND status = '1' ORDER BY depth, id</select>
</mapper>
测试
关于 Service 和 ServiceImpl 这里就省略了
FunctionMenuController
@RestController
@RequestMapping("/functionMenu")
public class FunctionMenuController {@Autowiredpublic FunctionMenuService functionMenuService;@GetMapping("getAllMenuList")public String getAllMenuList() {var data = functionMenuService.getAllMenuList();return R.success(data);//消息响应}
}
结果:
public FunctionMenuService functionMenuService;@GetMapping("getAllMenuList")public String getAllMenuList() {var data = functionMenuService.getAllMenuList();return R.success(data);//消息响应}
}
结果:

相关文章:
基于SpringBoot多数据源解决方案
最近在学习SpringBoot的时候,需要同时用两个不同的数据库连接服务,在网上学习了之后,下文以连接一个MySQL数据库和一个SqlServer数据库为例。 配置数据源连接信息 在配置文件中,配置对应的数据库连接信息,相比于单数…...
通过案例研究二项分布和泊松分布之间关系(2)
通过案例研究二项分布和泊松分布之间关系 2. 汽车出事故的概率p与保险公司盈利W之间的关系3.通过遗传算法多次迭代计算控制p为多少时公司盈利最大(1) 计算过程(2) 结果及分析(计算过程详见附录二程序) 4.改变思路求解固定p为0.01时,保险费用如何设置公司可获得最大利润(1)计算过…...
RISC-V读书笔记4
目录 乘法与除法 RV32F 和 RV32D:单精度和双精度浮点数 原子操作 压缩指令 向量 乘法与除法 RV32M属于扩展的指令,主要扩展的就是便捷的乘法和除法指令。 除法: 商 (被除数− 余数) 除数 被除数 除数 商 余数 余数 被除数− (商 …...
【Uniapp-Vue3】request各种不同类型的参数详解
一、参数携带 我们调用该接口的时候需要传入type参数。 第一种 路径名称?参数名1参数值1&参数名2参数值2 第二种 uni.request({ url:"请求路径", data:{ 参数名:参数值 } }) 二、请求方式 常用的有get,post和put 三种,默认是get请求。…...
大数据学习之SCALA分布式语言三
7.集合类 111.可变set一 112.可变set二 113.不可变MAP集合一 114.不可变MAP集合二 115.不可变MAP集合三 116.可变map一 package com . itbaizhan . chapter07 //TODO 2. 使用 mutable.Map 前导入如下包 import scala . collection . mutable // 可变 Map 集合 object Ma…...
深度解析:MyBatis-Plus实现分页查询的封装!
全文目录: 开篇语前言摘要概述什么是分页查询?为什么选择 MyBatis-Plus?本文目标 源码解析分页插件核心逻辑 使用案例分享1. 配置 MyBatis-Plus 分页插件2. 定义分页查询方法3. Controller 层调用 应用场景案例优缺点分析优点缺点 核心类方法…...
第05章 14 绘制人脸部的PolyData并使用小圆锥体来展现法线
在VTK中,绘制人脸部的PolyData并使用小圆锥体来展现法线是一个常见的任务。这个过程可以通过以下步骤实现: 读取人脸部的PolyData:可以使用VTK的读取模块读取一个包含人脸部的.vtk或.obj文件。计算法线:使用VTK的vtkPolyDataNorm…...
基于微信小程序的电子商城购物系统设计与实现(LW+源码+讲解)
专注于大学生项目实战开发,讲解,毕业答疑辅导,欢迎高校老师/同行前辈交流合作✌。 技术范围:SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:…...
2000-2020年各省第二产业增加值占GDP比重数据
2000-2020年各省第二产业增加值占GDP比重数据 1、时间:2000-2020年 2、来源:国家统计局、统计年鉴 3、指标:行政区划代码、地区名称、年份、第二产业增加值占GDP比重 4、范围:31省 5、指标解释:第二产业增加值占GDP比重…...
【Docker】Docker入门了解
文章目录 Docker 的核心概念Docker 常用命令示例:构建一个简单的 C 应用容器1. 创建 C 应用2. 创建 Dockerfile3. 构建镜像4. 运行容器 Docker 优势学习 Docker 的下一步 **一、Docker 是什么?****为什么 C 开发者需要 Docker?** **二、核心概…...
【C语言分支与循环结构详解】
目录 ---------------------------------------begin--------------------------------------- 一、分支结构 1. if语句 2. switch语句 二、循环结构 1. for循环 2. while循环 3. do-while循环 三、嵌套结构 结语 -----------------------------------------end----…...
java求职学习day18
常用的设计原则和设计模式 1 常用的设计原则(记住) 1.1 软件开发的流程 需求分析文档、概要设计文档、详细设计文档、编码和测试、安装和调试、维护和升级 1.2 常用的设计原则 (1)开闭原则(Open Close Principle…...
初阶2 类与对象
本章重点 上篇1.面向过程和面向对象初步认识2.类的引入---结构体3.类的定义3.1 语法3.2 组成3.3 定义类的两种方法: 4.类的访问限定符及封装4.1 访问限定符4.2封装---面向对象的三大特性之一 5.类的作用域6.类的实例化7.类对象模型7.1 如何计算类对象的大小 8.this指…...
蓝桥杯模拟算法:多项式输出
P1067 [NOIP2009 普及组] 多项式输出 - 洛谷 | 计算机科学教育新生态 这道题是一道模拟题,我们需要分情况讨论,我们需要做一下分类讨论 #include <iostream> #include <cstdlib> using namespace std;int main() {int n;cin >> n;for…...
Python的那些事第三篇:Python编程的“调味料”与“交流术”运算符与输入输出
运算符与输入输出:Python编程的“调味料”与“交流术” 在编程的世界里,Python不仅仅是一门语言,它更像是一位充满智慧的厨师,而运算符和输入输出则是它手中的“调味料”和“交流术”。没有这些工具,代码就会像没有加…...
如何利用AI工具来进行数据分析
利用AI工具进行数据分析可以显著提高效率和准确性,以下是详细步骤和方法: 1. 明确分析目标 在开始数据分析之前,首先需要明确分析的目标和问题。这包括确定需要解决的问题、期望的见解或结果,以及选择合适的AI工具和方法。 2. …...
深度剖析C++17中的std::optional:处理可能缺失值的利器
文章目录 一、基本概念与设计理念二、构建与初始化(一)默认构造(二)值初始化(三)使用std::make_optional(四)使用std::nullopt 三、访问值(一)value()&#x…...
MySQL用户授权、收回权限与查看权限
【图书推荐】《MySQL 9从入门到性能优化(视频教学版)》-CSDN博客 《MySQL 9从入门到性能优化(视频教学版)(数据库技术丛书)》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) MySQL9数据库技术_夏天又到了…...
每日一题 429. N 叉树的层序遍历
429. N 叉树的层序遍历 /*class Solution { public:vector<vector<int>> levelOrder(Node* root) {queue<Node*> que;que.push(root);vector<vector<int>> ans;if(root nullptr){return ans;}while(!que.empty()){int sizeQue que.size();vec…...
【Maui】注销用户,采用“手势”点击label弹窗选择
文章目录 前言一、问题描述二、解决方案三、软件开发(源码)3.1 方法一:前端绑定3.2 方法二:后端绑定3.3 注销用户的方法 四、项目展示 前言 .NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架,用于使用 C# 和 XAML 创…...
如何将xps文件转换为txt文件?xps转为pdf,pdf转为txt,提取pdf表格并转为txt
文章目录 xps转txt方法一方法二 pdf转txt整页转txt提取pdf表格,并转为txt 总结另外参考XPS文件转换为TXT文件XPS文件转换为PDF文件PDF文件转换为TXT文件提取PDF表格并转为TXT示例代码(部分) 本文测试代码已上传,路径如下ÿ…...
Object类(2)
大家好,今天我们继续来看看Object类中一些成员方法,这些方法在实际中有很大的用处,话不多说,来看。 注:所有类都默认继承Object类的,所以可调用Object类中的方法,如equals,也可以发生…...
BGP分解实验·11——路由聚合与条件性通告(3)
续接上(2)的实验。其拓扑如下: 路由聚合的负向也就是拆分,在有双出口的情况下,在多出口做流量分担是优选方法之一。 BGP可以根据指定来源而聚合路由,在产生该聚合路由的范围内的条目注入到本地BGP表后再向…...
无用的知识又增加了:is_assignable means?
std::pair的默认operator被delete掉了,取而代之的是两个enable_if版本。 为什么这么设计,我的理解是在std::map里,已经保存的元素的key值是不能被修改的,比如 注意,下面的代码会修改key值,编译时出现错误…...
MOS的体二极管能通多大电流
第一个问题:MOS导通之后电流方向可以使任意的,既可以从D到S,也可以从S到D。 第二个问题:MOS里面的体二极管电流可以达到几百安培,这也就解释了MOS选型的时候很少考虑体二极管的最大电流,而是考虑DS之间电流…...
C语言【基础篇】之流程控制——掌握三大结构的奥秘
流程控制 🚀前言🦜顺序结构💯 定义💯执行规则 🌟选择结构💯if语句💯switch语句💯case穿透规则 🤔循环结构💯for循环💯while循环💯do -…...
Node.js下载安装及环境配置教程 (详细版)
Node.js:是一个基于 Chrome V8 引擎的 JavaScript 运行时,用于构建可扩展的网络应用程序。Node.js 使用事件驱动、非阻塞 I/O 模型,使其非常适合构建实时应用程序。 Node.js 提供了一种轻量、高效、可扩展的方式来构建网络应用程序࿰…...
【PySide6拓展】QCalendarWidget 日历控件
文章目录 PySide6拓展:QCalendarWidget 日历控件前言什么是QCalendarWidget?特点: 如何使用QCalendarWidget?1. 基本用法2. 获取当前选中的日期 QCalendarWidget 常用函数分类介绍1. 日期选择与管理函数2. 日期格式与显示函数3. 日…...
新型人工智能“黑帽”工具:GhostGPT带来的威胁与挑战
生成式人工智能的发展既带来了有益的生产力转型机会,也提供了被恶意利用的机会。 最近,Abnormal Security的研究人员发现了一个专门为网络犯罪创建的无审查AI聊天机器人——GhostGPT,是人工智能用于非法活动的新前沿,可以被用于网…...
【文星索引】搜索引擎项目测试报告
目录 一、项目背景二、 项目功能2.1 数据收集与索引2.2 API搜索功能2.3 用户体验与界面设计2.4 性能优化与维护 三、测试报告3.1 功能测试3.2 界面测试3.3 性能测试3.4 兼容性测试3.5 自动化测试 四、测试总结4.1 功能测试方面4.2 性能测试方面4.3 用户界面测试方面 一、项目背…...
