基于SpringBoot多数据源解决方案

news2025/1/31 8:15:14

最近在学习SpringBoot的时候,需要同时用两个不同的数据库连接服务,在网上学习了之后,下文以连接一个MySQL数据库和一个SqlServer数据库为例。

配置数据源连接信息

在配置文件中,配置对应的数据库连接信息,相比于单数据源时连接信息的url属性在多数据源时应该为jdbc-url,请注意下图红色部分:

image-20250127112939923

application.yml

spring:
	datasource:
        webproject:
          type: mysql
          driver-class-name: com.mysql.cj.jdbc.Driver
          jdbc-url: jdbc:mysql://localhost:3306/webproject?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
          username: 
          password: 
        workcontent:
          type: sqlServer
          driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
          jdbc-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工厂实例

image-20250127115339264

创建数据源DataSource

@ConfigurationProperties 用于注入application.yml中配置的数据库连接信息

@Primary 默认指定当前数据库,多数据源下需要配置@Primary,不然SpringBoot会找不到数据源注入,后续步骤最好也加上@Primary
在这里插入图片描述

创建Sql工厂,注册DataSource

@Qualifier 用于选择注入的bean对象

image-20250127121153606

创建事务管理器

image-20250127121313062

创建Sql模版对象

image-20250127121424311

具体配置类信息如下:

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")
    @Primary
    public DataSource dataSource() {
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "workContentSqlSessionFactory")
    @Primary
    public 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")
    @Primary
    public DataSourceTransactionManager transactionManager(@Qualifier("workContentDataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean(name = "workContentSqlSessionTemplate")
    @Primary
    public 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的SqlSessionFactorybean.setMapperLocations()里面的路径对应上,对于Mapper.xml里面的配置这里就不具体说明了。

image-20250127141023803

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 {
    @Autowired
    public 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);//消息响应
    }
}

结果:

image-20250127143701257

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2284459.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通过案例研究二项分布和泊松分布之间关系(2)

通过案例研究二项分布和泊松分布之间关系 2. 汽车出事故的概率p与保险公司盈利W之间的关系3.通过遗传算法多次迭代计算控制p为多少时公司盈利最大(1) 计算过程(2) 结果及分析(计算过程详见附录二程序) 4.改变思路求解固定p为0.01时,保险费用如何设置公司可获得最大利润(1)计算过…

RISC-V读书笔记4

目录 乘法与除法 RV32F 和 RV32D&#xff1a;单精度和双精度浮点数 原子操作 压缩指令 向量 乘法与除法 RV32M属于扩展的指令&#xff0c;主要扩展的就是便捷的乘法和除法指令。 除法&#xff1a; 商 (被除数− 余数) 除数 被除数 除数 商 余数 余数 被除数− (商 …

【Uniapp-Vue3】request各种不同类型的参数详解

一、参数携带 我们调用该接口的时候需要传入type参数。 第一种 路径名称?参数名1参数值1&参数名2参数值2 第二种 uni.request({ url:"请求路径", data:{ 参数名:参数值 } }) 二、请求方式 常用的有get&#xff0c;post和put 三种&#xff0c;默认是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…

基于微信小程序的电子商城购物系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

2000-2020年各省第二产业增加值占GDP比重数据

2000-2020年各省第二产业增加值占GDP比重数据 1、时间&#xff1a;2000-2020年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;行政区划代码、地区名称、年份、第二产业增加值占GDP比重 4、范围&#xff1a;31省 5、指标解释&#xff1a;第二产业增加值占GDP比重…

【Docker】Docker入门了解

文章目录 Docker 的核心概念Docker 常用命令示例&#xff1a;构建一个简单的 C 应用容器1. 创建 C 应用2. 创建 Dockerfile3. 构建镜像4. 运行容器 Docker 优势学习 Docker 的下一步 **一、Docker 是什么&#xff1f;****为什么 C 开发者需要 Docker&#xff1f;** **二、核心概…

java求职学习day18

常用的设计原则和设计模式 1 常用的设计原则&#xff08;记住&#xff09; 1.1 软件开发的流程 需求分析文档、概要设计文档、详细设计文档、编码和测试、安装和调试、维护和升级 1.2 常用的设计原则 &#xff08;1&#xff09;开闭原则&#xff08;Open Close Principle…

初阶2 类与对象

本章重点 上篇1.面向过程和面向对象初步认识2.类的引入---结构体3.类的定义3.1 语法3.2 组成3.3 定义类的两种方法&#xff1a; 4.类的访问限定符及封装4.1 访问限定符4.2封装---面向对象的三大特性之一 5.类的作用域6.类的实例化7.类对象模型7.1 如何计算类对象的大小 8.this指…

蓝桥杯模拟算法:多项式输出

P1067 [NOIP2009 普及组] 多项式输出 - 洛谷 | 计算机科学教育新生态 这道题是一道模拟题&#xff0c;我们需要分情况讨论&#xff0c;我们需要做一下分类讨论 #include <iostream> #include <cstdlib> using namespace std;int main() {int n;cin >> n;for…

深度剖析C++17中的std::optional:处理可能缺失值的利器

文章目录 一、基本概念与设计理念二、构建与初始化&#xff08;一&#xff09;默认构造&#xff08;二&#xff09;值初始化&#xff08;三&#xff09;使用std::make_optional&#xff08;四&#xff09;使用std::nullopt 三、访问值&#xff08;一&#xff09;value()&#x…

MySQL用户授权、收回权限与查看权限

【图书推荐】《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;》-CSDN博客 《MySQL 9从入门到性能优化&#xff08;视频教学版&#xff09;&#xff08;数据库技术丛书&#xff09;》(王英英)【摘要 书评 试读】- 京东图书 (jd.com) MySQL9数据库技术_夏天又到了…

【Maui】注销用户,采用“手势”点击label弹窗选择

文章目录 前言一、问题描述二、解决方案三、软件开发&#xff08;源码&#xff09;3.1 方法一&#xff1a;前端绑定3.2 方法二&#xff1a;后端绑定3.3 注销用户的方法 四、项目展示 前言 .NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架&#xff0c;用于使用 C# 和 XAML 创…

如何将xps文件转换为txt文件?xps转为pdf,pdf转为txt,提取pdf表格并转为txt

文章目录 xps转txt方法一方法二 pdf转txt整页转txt提取pdf表格&#xff0c;并转为txt 总结另外参考XPS文件转换为TXT文件XPS文件转换为PDF文件PDF文件转换为TXT文件提取PDF表格并转为TXT示例代码&#xff08;部分&#xff09; 本文测试代码已上传&#xff0c;路径如下&#xff…

Object类(2)

大家好&#xff0c;今天我们继续来看看Object类中一些成员方法&#xff0c;这些方法在实际中有很大的用处&#xff0c;话不多说&#xff0c;来看。 注&#xff1a;所有类都默认继承Object类的&#xff0c;所以可调用Object类中的方法&#xff0c;如equals&#xff0c;也可以发生…

BGP分解实验·11——路由聚合与条件性通告(3)

续接上&#xff08;2&#xff09;的实验。其拓扑如下&#xff1a; 路由聚合的负向也就是拆分&#xff0c;在有双出口的情况下&#xff0c;在多出口做流量分担是优选方法之一。 BGP可以根据指定来源而聚合路由&#xff0c;在产生该聚合路由的范围内的条目注入到本地BGP表后再向…

MOS的体二极管能通多大电流

第一个问题&#xff1a;MOS导通之后电流方向可以使任意的&#xff0c;既可以从D到S&#xff0c;也可以从S到D。 第二个问题&#xff1a;MOS里面的体二极管电流可以达到几百安培&#xff0c;这也就解释了MOS选型的时候很少考虑体二极管的最大电流&#xff0c;而是考虑DS之间电流…

C语言【基础篇】之流程控制——掌握三大结构的奥秘

流程控制 &#x1f680;前言&#x1f99c;顺序结构&#x1f4af; 定义&#x1f4af;执行规则 &#x1f31f;选择结构&#x1f4af;if语句&#x1f4af;switch语句&#x1f4af;case穿透规则 &#x1f914;循环结构&#x1f4af;for循环&#x1f4af;while循环&#x1f4af;do -…

Node.js下载安装及环境配置教程 (详细版)

Node.js&#xff1a;是一个基于 Chrome V8 引擎的 JavaScript 运行时&#xff0c;用于构建可扩展的网络应用程序。Node.js 使用事件驱动、非阻塞 I/O 模型&#xff0c;使其非常适合构建实时应用程序。 Node.js 提供了一种轻量、高效、可扩展的方式来构建网络应用程序&#xff0…

新型人工智能“黑帽”工具:GhostGPT带来的威胁与挑战

生成式人工智能的发展既带来了有益的生产力转型机会&#xff0c;也提供了被恶意利用的机会。 最近&#xff0c;Abnormal Security的研究人员发现了一个专门为网络犯罪创建的无审查AI聊天机器人——GhostGPT&#xff0c;是人工智能用于非法活动的新前沿&#xff0c;可以被用于网…