Spring 多数据源搭建

目录

前言

正文 

1.Druid 介绍和使用 

2.其他多数据源解决方案 

总结


前言

对于复杂的业务和项目,可能在一个单体项目中存在需要连接多个数据库的情况。这时,就会使用到多数据源,实际中遇到的可能性比较大。


正文 

如果一个项目中需要连接 db1 ,同时一部分业务需要获取 db2 中的数据,则需要连接第二个数据源。这里一个数据源对应一个数据库,这两个数据库可能是部署在同一台服务器上,也可能不在同一台服务器上,通过配置 jdbc-url 来区别。

在配置上需要配置不同的 datasource 才能实现不同的数据源连接。

1.Druid 介绍和使用 

Druid 是阿里旗下开源的数据库连接池,提供强大的监控和扩展功能,包括数据库性能健康,获取 SQL 日志的能力。除此之外,也可以和 MyBaties 配合用于多数据源搭建。 

pom.xml依赖如下

        <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.23</version></dependency>

为了测试多数据源,创建一个主库 test_spring_master,一个从库 test_spring_slave,创建 SQL 如下:

create database test_spring_master default character set utf8;
create database test_spring_slave default character set utf8;

配置文件内容如下:

spring:datasource:master: #主数据元username: masterpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://localhost:3306/test_spring_master?useTimezone=true&serverTimezone=UTCinitialSize: 5minIdle: 5maxActive: 20slave: #第二个数据源username: slavepassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverjdbc-url: jdbc:mysql://localhost:3306/test_spring_slave?useTimezone=true&serverTimezone=UTCinitialSize: 5minIdle: 5maxActive: 20

针对两个数据源配置,需要编写两个配置类,一个是主数据源配置类,另一个是从数据源配置类,分别命名为 MasterDataSourceConfig 和 SlaveDataSourceConfig,代码如下: 

package org.example.config;import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;import javax.sql.DataSource;@Configuration
@MapperScan(basePackages = MasterDataSourceConfig.PACKAGE_NAME, sqlSessionFactoryRef = "masterSqlSessionFactory")
public class MasterDataSourceConfig {//定位到包类路径static final String PACKAGE_NAME = "org.example.mapper.master";//设置 mapper.xml位置。//不存在 *.xml 这种模糊匹配,必须准确的名称static final String MAPPER_LOCATION="classpath:org/example/mapper/master/MySinger.xml";@Value("${spring.datasource.master.jdbc-url}")private String url;@Value("${spring.datasource.master.username}")private String user;@Value("${spring.datasource.master.password}")private String password;@Value("${spring.datasource.master.driver-class-name}")private String driverClass;/*** 获得主数据源* @return*/@Bean(name = "masterDataSource")public DataSource masterDataSource(){DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(driverClass);//设置驱动dataSource.setUrl(url);dataSource.setUsername(user);dataSource.setPassword(password);return dataSource;}/*** 将 masterDataSource 注入到 masterTransactionManger* @return 管理数据库实物*/@Bean(name = "masterTransactionManger")public DataSourceTransactionManager masterTransactionManger(){//使用了 @Bean,则可以直接依赖注入进去return new DataSourceTransactionManager(masterDataSource());}@Bean(name = "masterSqlSessionFactory")@Primary //如果有多个相同类型的Bean 优先使用本Beanpublic SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource){final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();sessionFactory.setDataSource(masterDataSource);sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResource(MasterDataSourceConfig.MAPPER_LOCATION));try {return sessionFactory.getObject();} catch (Exception e) {e.printStackTrace();}return null;}}

补充: 

  • @MapperScan 注解用于指定需要扫描的 Mapper 接口所在的包名,它通常与 @Configuration 注解一起使用,表示该注解所标注的类是一个配置类。basePackages 属性用于指定需要扫描的包名,多个包名可以用逗号隔开。例如,@MapperScan(basePackages = "com.example.mapper") 表示需要扫描 com.example.mapper 包以及其子包中的 Mapper 接口。
  • 此外,@MapperScan 注解还有一个 sqlSessionFactoryRef 属性,用于指定使用的 SqlSessionFactory 的名称,即在 Spring IoC 容器中定义的 SqlSessionFactory Bean 的名称。在多个数据源的情况下,可以通过该属性为不同的 Mapper 接口指定不同的 SqlSessionFactory,以使用不同的数据源。
  • 也就是说,@MapperScan 注解将指定包下的 MyBatis Mapper 接口与 SqlSessionFactory 关联起来。为了让 @MapperScan 注解知道要关联哪个 SqlSessionFactory 实例,需要通过 sqlSessionFactoryRef 属性指定 SqlSessionFactory 的 Bean 名称。

可以看到  masterSqlSessionFactory 方法上有 @Primary 注解,说明这时主库。而从数据源配置类和主数据源配置类比较类似,唯一不同就是一些参数和注解不同。

package org.example.config;import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
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;@Configuration
@MapperScan(basePackages = SlaveDataSourceConfig.PACKAGE_NAME, sqlSessionFactoryRef = "slaveSqlSessionFactory")
public class SlaveDataSourceConfig {//定位到包类路径static final String PACKAGE_NAME = "org.example.mapper.slave";//设置 mapper.xml位置。static final String MAPPER_LOCATION="classpath:org/example/mapper/slave/MyStore.xml";@Value("${spring.datasource.slave.jdbc-url}")private String url;@Value("${spring.datasource.slave.username}")private String user;@Value("${spring.datasource.slave.password}")private String password;@Value("${spring.datasource.slave.driver-class-name}")private String driverClass;/*** 获得主数据源* @return*/@Bean(name = "slaveDataSource")public DataSource slaveDataSource(){DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(driverClass);//设置驱动dataSource.setUrl(url);dataSource.setUsername(user);dataSource.setPassword(password);return dataSource;}/*** 将 masterDataSource 注入到 masterTransactionManger* @return 管理数据库实物*/@Bean(name = "slaveTransactionManager")public DataSourceTransactionManager slaveTransactionManager(){//使用了 @Bean,则可以直接依赖注入进去return new DataSourceTransactionManager(slaveDataSource());}@Bean(name = "slaveSqlSessionFactory")public SqlSessionFactory slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource slaveDataSource) throws Exception {final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();sessionFactory.setDataSource(slaveDataSource);sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResource(SlaveDataSourceConfig.MAPPER_LOCATION));return sessionFactory.getObject();}}

为了进一步实验,需要在两个库中分别创建表,于是主库创建 m_singers 歌手表,在从数据库中创建 m_stores 商店表。

use test_spring_master;
create table `m_singers`(`id` int(11) unsigned not null auto_increment comment '歌手主键',`name` varchar(80) not null comment '歌手名',`age` int(3) default null comment '年龄',primary key (`id`)
)engine InnoDB default character set=utf8;
use test_spring_slave;
create table `m_stores`(`id` int(11) unsigned not null  auto_increment comment '主键',`name` varchar(100) NOT NULL  comment '店名',`space` int(6) not null  comment '单位平方米',`description` varchar(300) not null comment '简介',primary key (`id`)
)engine InnoDB DEFAULT character set=utf8;

创建用户并授予权限

create  user 'master'@'localhost' identified by'123456';
show grants for 'master'@'localhost';
create  user 'slave'@'localhost' identified by'123456';
GRANT ALL PRIVILEGES ON test_spring_slave.m_stores TO 'slave'@'localhost';
GRANT ALL PRIVILEGES ON test_spring_master.m_singers TO 'master'@'localhost';

 继续分别创建对应的实体类 

MyStore.java

package org.example.entity;import lombok.Data;import java.io.Serializable;@Data
public class MyStore implements Serializable {// 主键private Integer id;// 名称private String name;// 地区private int space;// 描述private String description;
}

MySinger.java 

package org.example.entity;import lombok.Data;import java.io.Serializable;@Data
public class MySinger implements Serializable {private Integer id;private String name;private int age;
}

对应的 Dao 文件:

MyStore。 

package org.example.mapper.slave;import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.example.entity.MyStore;@Mapper
public interface MyStoreDao {int insert(MyStore record);int updateByPrimaryKey(MyStore record);MyStore findByName(@Param("name") String name);
}

MySinger。 

package org.example.mapper.master;import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.example.entity.MySinger;@Mapper
public interface MySingerDao {int deleteByPrimaryKey(Integer id);int insert(MySinger record);int insertSelective(MySinger record);MySinger selectByPrimaryKey(Integer id);int updateByPrimaryKeySelective(MySinger record);int updateByPrimaryKey(MySinger record);MySinger findByName(@Param("name") String name);
}

剩下的 controller,service 部分基本就是一般流程了,就不继续向下写了。 最后自己进行测试:

完成 。

2.其他多数据源解决方案 

其他解决方案也很多,如使用多个 Bean、自实现动态 DataSource 等,其中有一种方法非常简单高效 ,就是使用第三方的依赖包,动态加载不同的数据源,推荐使用 dynamic-datasource-spring-boot-starter。

dynamic-datasource-spring-boot-starter 是国内开发者维护的多数据源解决方案插件被作者称为一个基于 springboot 的快速继承多数据源的启动器。它的优点主要有支持数据源分组,适用于多种场景,纯粹多库、读写分离、一主多从混合模式。

POM.xml依赖

        <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>3.5.1</version></dependency>

数据源配置

spring:datasource:dynamic:primary: master #设置默认的数据源或者数据数组,默认只即为 masterstrict: false #设置严格模式,默认false不启动。启动后在未匹配到指定数据源时会抛出异常,不启动则使用默认数据源。datasource:master:url: jdbc:mysql://xx.xx.xx.xx:3306/dynamicusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver #3.2.0开始支持 SPI 可省略此配置slave_1:url: jdbc:mysql://xx.xx.xx.xx:3307/dynamicusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driverslave_2:url: ENC(xxxxx) #内置加密,使用详情请看文档。username: ENC(xxxxx)password: ENC(xxxxx)driver-class-name: com.mysql.cj.jdbc.Driverscherma: db/schema.sql #配置生效,自动初始化表格data: db/data.sql #配置生效,自动初始化数据continue-on-error: true #默认true ,初始化失败是否separator: "," #sql 默认分隔符#这些配置会配置一个默认库 master, 一个组 slave 下有两个子库 slave_1, slave_2

配置解释:

在这段配置中:

spring.datasource.dynamic 是 dynamic-datasource-spring-boot-starter 的配置前缀。

  • primary: master 设置主数据源为 “master”。

  • strict: false 为非严格模式。如果请求的数据源不存在,系统会自动回退到主数据源,而不是抛出异常。

  • datasource: 下面列出了所有的数据源,每一个数据源都有一个名字(在这个例子中是 master, slave_1, slave_2),和对应的数据源详情(url, username, password, driver-class-name)。

每个具体的数据源(比如 “master”)下:

  • url: 数据库的 JDBC URL。

  • username: 数据库用户名。

  • password: 数据库密码。ENC(xxx) 是一个加密格式,能够保护你的敏感信息不被明文展示。

  • driver-class-name: JDBC 驱动类型。如果你依赖的版本在3.2.0以上,驱动可以被自动识别,这个配置也可以省略。

scherma: db/schema.sql 和 data: db/data.sql 是你自定义的SQL语句文件。在 Spring Boot 中可以通过这种方式定义 SQL 文件的路径,然后在应用启动的时候自动执行这些 SQL 文件。

强调一下,这只是个示例,你需要根据具体的数据库信息(如类型、地址、端口、用户名、密码等)去做相应的调整和修改。例如,替换 xx.xx.xx.xx 为实际的数据库服务器地址,并且替换 username 和 password 为正确的数据库访问凭证等。

最后使用 @DS 注解,采取就近原则,方法上的注解优先于类上的注解,代码如下 : 

package org.example.service;import com.baomidou.dynamic.datasource.annotation.DS;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;import java.util.List;@Service
@DS("slave")
public class UserServiceImpl implements UserService{@Autowiredprivate JdbcTemplate jdbcTemplate;public List selectAll(){return jdbcTemplate.queryForList("select * from user");}@Override@DS("slave_1")public List selectByCondition(){return jdbcTemplate.queryForList("select * from user where age > 10");}}


总结

多数据源搭建提供了灵活性、性能优化、隔离性和安全性方面的优势。它使应用程序能够更好地适应不同的需求,并在处理数据时提供更好的性能和可扩展性。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java企业财务管理系统springboot+jsp

1、基本内容 &#xff08;1&#xff09;搭建基础环境&#xff0c;下载JDK、开发工具eclipse/idea。 &#xff08;2&#xff09;通过HTML/CSS/JS搭建前端框架。 &#xff08;3&#xff09;下载MySql数据库&#xff0c;设计数据库表&#xff0c;用于存储系统数据。 &#xff08;4…

从0开始学习JavaScript--JavaScript 单元测试

JavaScript单元测试是保障代码质量和可维护性的关键步骤之一。通过编写和运行单元测试&#xff0c;开发者可以确保代码在不断迭代的过程中依然具有正确的行为。本文将深入探讨JavaScript单元测试的核心概念、工具使用和最佳实践&#xff0c;并通过丰富的示例代码演示其实际应用…

leetcode-141-环形链表(C语言实现)

题目&#xff1a; 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的…

学习笔记8——JUC入门基础知识

学习笔记系列开头惯例发布一些寻亲消息 链接&#xff1a;https://baobeihuijia.com/bbhj/contents/3/199561.html 进程和线程:进程是资源分配的最小单位&#xff0c;线程是CPU调度的最小单位 进程和线程的主要区别&#xff08;总结&#xff09;_进程和线程的区别-CSDN博客进程…

生物教师个人简历(精选21篇)

以下21篇简历内容以生物教师招聘需求为背景制作&#xff0c;大家可以灵活借鉴&#xff0c;希望能帮助大家在众多候选人中脱颖而出。 生物教师个人简历下载&#xff08;在线制作&#xff09;&#xff1a;百度幻主简历或huanzhuv.com 生物老师简历1&#xff1a; 求职意向 求职…

STM32——USART串口协议

文章目录 一.半双工&#xff0c;全双工和单工区别二.通信协议基础知识三.串口通信区分四.串口通信的基础知识五.USART串口外设六.USART框图七.USART基本结构八.数据模式九.单片机通过串口发送一个字节&#xff0c;数组,字符串和数字到电脑上9.1 接线9.2 代码 十.printf函数的移…

wordpress安装之Linux ftp传输

工欲善其事,必先利其器。 最近准备在自己的服务器上搭建一个个人技术分享的平台。 因为我发现现在网络上的工具呀&#xff0c;还有一些问题的解答总是模棱两可&#xff0c;所以我打算自己做一个。 首先呢&#xff0c;我们需要有一个linxu的系统当服务器&#xff0c;然后呢&a…

Grammarly premium语法检测工具使用方法,及删除检测记录

科研写作神器&#xff1a;Grammarly—语法&#xff0c;标点&#xff0c;单词拼写错误修改。 一、背景 在写英文论文时&#xff0c;作为母语不是英语的我们&#xff0c;不可避免的存在语法错误或笔误&#xff0c;这时就需要Grammarly语法修改软件帮助我们进行修正&#xff0c…

shell编程系列(8)-使用sed处理文本

文章目录 引言sed用法详解在文本中定位打印文本替换文本删除文本新增文本 结语 引言 在日常工作学习中我们都会遇到要编辑文本的场景&#xff0c;例如我们要用vim或者nano等命令去编辑代码&#xff0c;处理文本文件等&#xff0c;这些命令的特点都是需要我们进行交互式的实时处…

企业存货库存综合分析全流程图

上期我们谈到了 诊断存货管理的4大维度&#xff0c;今天我们进一步全方位、全周期的分析企业内存货的问题。 企业存货是企业用于生产或销售的货品&#xff0c;是企业价值增值变现的载体&#xff0c;但是如果一旦没有产生交易&#xff0c;存货就很有可能带来损失。存货伴随着企业…

【力扣周赛】第 369 场周赛(⭐记忆化搜索 树形DP)

文章目录 竞赛链接Q1&#xff1a;2917. 找出数组中的 K-or 值竞赛时代码——按题意模拟 Q2&#xff1a;2918. 数组的最小相等和竞赛时代码——分类讨论 Q3&#xff1a;2919. 使数组变美的最小增量运算数⭐⭐⭐竞赛时代码——动态规划解法2——记忆化搜索 翻译成递推 Q4&#xf…

SQL Server 数据库,使用函数查询统计信息

4.1 在查询中使用函数 在前面章节已经学习了一些简单的增、删、改、查询的T-SOL.语句&#xff0c;但是为了更方便快捷地完 成大量的任务&#xff0c;SOLServer提供了一些内部函数&#xff0c;可以和SOLServer的SELECT语句联合使用&#xff0c;也可 以与UPDATE和INSERT一起使用&…