SpringBoot多数据源(主从数据源)配置

🎶前言

学习springboot配置多数据源,先回顾一下springboot配置单数据源的方式
SpringBoot配置mybatis-mysql数据源

🔠主从数据源搭建

项目依赖

本次记录多数据源配置主要是通过druid + mybatis plus + aop的形式实现的,mybatis plus是一个很方便的数据库操作框架,自己也有实现多数据源的jar包,这里没有使用她封装的方法,主要是学习所以是自行实现了一遍简单的多数据源配置和动态切换数据源。

<!-- mybatis-plus多数据源配置jar -->
<dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId>
</dependency>

使用到的依赖

<dependencies><!-- druid数据连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId></dependency><!-- mybatis plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId></dependency><!-- spring-aop --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><!-- springboot配置依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><!-- mysql驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency>
</dependencies>

Yml文件配置数据源

这里可以看到数据源配置属性路径并非spring.datasource, 这里主要是想通过学习spring-boot配置文件自动装配, 来获取配置并初始化数据源。

utmost:# 主从数据源配置datasource:dynamic:master:driverClassName: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.92.10:3306/utmost_01?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8userName: rootpassword: rootslave:enabled: true  # 是否启用从数据源driverClassName: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://192.168.92.10:3306/utmost_02?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8userName: rootpassword: root
@Getter
@Setter
@ConfigurationProperties(prefix = UtmostDataSourceProperties.PREFIX)
public class UtmostDataSourceProperties {/*** 配置前缀*/public static final String PREFIX = "utmost.datasource.dynamic";/*** master数据源配置前缀*/public static final String MASTER_PREFIX = "utmost.datasource.dynamic.master";/*** slave数据源配置前缀*/public static final String SLAVE_PREFIX = "utmost.datasource.dynamic.slave";/*** 设置默认数据库, 默认master*/public String primary = "master";/*** 设置启用数据源, 默认true*/public boolean enabled = true;/*** 主数据源*/public SingleDataSourceProperty master;/*** 从数据源*/public SingleDataSourceProperty slave;
}
@Data
@Accessors(chain = true)
public class SingleDataSourceProperty {/*** JDBC driver*/private String driverClassName;/*** JDBC 数据库地址*/private String url;/*** JDBC 用户名*/private String userName;/*** JDBC 用户密码*/private String password;
}

怎么通过UtmostDataSourceProperties类来获取属性, 主要通过@ConfigurationProperties注解实现, 前面依赖中引入了spring-boot-configuration-processor依赖, 在使用yml配置数据源时就会出现一定的提示作用。这是因为在打包编译的时候会生成一个spring-configuration-metadata.json文件,这里就不赘述了,先了解到这是springboot帮助我们生成的作用于提示的文件就可以了。
image.png

mybatis-plus配置

# mybatis-plus 配置
mybatis-plus:configuration:# 开启驼峰map-underscore-to-camel-case: true# 关闭一级缓存local-cache-scope: statement# 关闭二级缓存cache-enabled: false# sql xml文件映射路径mapper-locations: classpath*:/mapper/*.xml# MyBaits 别名包扫描路径,通过该属性可以给包中的类注册别名type-aliases-package: com.zy.utmost.entity

数据源装配

数据源配置类

@Slf4j
@Configuration
@AllArgsConstructor
@EnableConfigurationProperties(UtmostDataSourceProperties.class)
@ConditionalOnProperty(prefix = UtmostDataSourceProperties.PREFIX, name = "enabled", havingValue = "true", matchIfMissing = true)
public class UtmostDataSourceAutoConfiguration {private final UtmostDataSourceProperties utmostDataSourceProperties;/*** 主数据源(这里配置写的繁琐一点, 可根据个人喜好进行简化.)** @return DataSource*/@Bean(name = "masterDataSource")@ConfigurationProperties(UtmostDataSourceProperties.MASTER_PREFIX)public DataSource masterDataSource() {SingleDataSourceProperty master = utmostDataSourceProperties.master;DruidDataSource druidDataSource = new DruidDataSource();druidDataSource.setName(DataSourceConstants.MASTER);druidDataSource.setDriverClassName(master.getDriverClassName());druidDataSource.setUrl(master.getUrl());druidDataSource.setUsername(master.getUserName());druidDataSource.setPassword(master.getPassword());return druidDataSource;}/*** 从 数据源(这里配置写的繁琐一点, 可根据个人喜好进行简化.)** @return DataSource*/@Bean(name = "slaveDataSource")@ConfigurationProperties(UtmostDataSourceProperties.SLAVE_PREFIX)@ConditionalOnProperty(prefix = UtmostDataSourceProperties.SLAVE_PREFIX, name = "enabled", havingValue = "true")public DataSource slaveDataSource() {SingleDataSourceProperty slave = utmostDataSourceProperties.slave;DruidDataSource druidDataSource = new DruidDataSource();druidDataSource.setName(DataSourceConstants.SLAVE);druidDataSource.setDriverClassName(slave.getDriverClassName());druidDataSource.setUrl(slave.getUrl());druidDataSource.setUsername(slave.getUserName());druidDataSource.setPassword(slave.getPassword());return druidDataSource;}/*** 动态数据源** @return DynamicDataSource*/@Bean@Primarypublic DynamicDataSource dynamicDataSource() {Map<Object, Object> targetMap = new HashMap<>(2);targetMap.put(DataSourceConstants.MASTER, masterDataSource());targetMap.put(DataSourceConstants.SLAVE, slaveDataSource());DynamicDataSource dynamicDataSource = new DynamicDataSource(masterDataSource(), targetMap);log.info("动态数据源装配完成...");return dynamicDataSource;}
}

动态数据源实现类

通过继承重写AbstractRoutingDataSource 数据源路由来实现数据源动态切换的功能.

public class DynamicDataSource extends AbstractRoutingDataSource {/*** 使用线程切换数据源*/private static ThreadLocal<String> contextHandler = new ThreadLocal<>();/*** 数据源key集合*/private static List<Object> dataSourceKeys = new ArrayList<>();/*** 配置数据源** @param defaultDataSource   主数据源* @param targetDataSourceMap 其他数据源集合*/public DynamicDataSource(DataSource defaultDataSource, Map<Object, Object> targetDataSourceMap) {super.setDefaultTargetDataSource(defaultDataSource);super.setTargetDataSources(targetDataSourceMap);super.afterPropertiesSet();// 初始化所有数据源的keyaddAllDataSourceKeys(targetDataSourceMap.keySet());}public static void setDataSourceKeys(String key) {contextHandler.set(key);}public static ThreadLocal<String> getDataSourceKeys() {return contextHandler;}public static void removeDataSourceKeys() {contextHandler.remove();}public static boolean containsDataSourceKeys(String key) {return dataSourceKeys.contains(key);}public static boolean addAllDataSourceKeys(Collection<? extends Object> keys) {return dataSourceKeys.addAll(keys);}@Overrideprotected Object determineCurrentLookupKey() {return contextHandler.get();}
}

数据源切换注解

/*** 注解命名主要是为了好记所以直接使用了DataSource, 在使用时会发现有很* 多类都是以DataSource命名, 使用时需要注意.* @author yanzy*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {String value() default "";
}

数据源切换实现

/*** 实现注解织入切换数据源** @author yanzy* @date 2021/6/10 23:38* @since v1.0*/
@Aspect
@Order(-10)
@Component
public class DataSourceAspect {private Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);/*** - @within扫描类注解, @annotation扫描方法上注解* 定义切面*/@Pointcut("@annotation(com.zy.utmost.annotation.DataSource) || @within(com.zy.utmost.annotation.DataSource)")public void annotationPointCut() {}/*** 前置事件 (方法前切换数据源)* @param point 织入点* @param dataSource 注解实例*/@Before("annotationPointCut() && @annotation(dataSource))")public void beforeMethod(JoinPoint point, DataSource dataSource) {switchDataSource(point, dataSource);}/*** 前置事件 (类前切换数据源)* @param point 织入点* @param dataSource 注解实例*/@Before("annotationPointCut() && @within(dataSource))")public void beforeClass(JoinPoint point, DataSource dataSource) {switchDataSource(point, dataSource);}private void switchDataSource(JoinPoint point,DataSource dataSource) {String key = dataSource.value();if (!DynamicDataSource.containsDataSourceKeys(key)) {logger.debug("数据源切换失败: [{}] - 数据源不存在, 自动使用默认数据源.", key);} else {DynamicDataSource.setDataSourceKeys(key);logger.debug("数据源切换成功: [{}] - 已切换至 - [{}] - 数据源.", point.getSignature().getName(), key);}}/*** 后置增强 (方法/类执行完毕后,将数据源切回默认)** @param point 织入点*/@After("annotationPointCut()")public void after(JoinPoint point) {if (null != DynamicDataSource.getDataSourceKeys()) {DynamicDataSource.removeDataSourceKeys();logger.debug("数据源切换成功: 切换为主数据源.");}}
}

搭建完测试

测试使用的crud类就不附上代码了, 直接使用spring-boot-test来给数据库插入数据, 验证一下数据源是否可以正常切换.

数据库

首先这里使用了两个数据库, 都创建了utmost这个数据库实例并创建有sys_user表.
image.png

service实现类(方法增加注解切换数据源)

@Service
public class SysUserServiceImpl implements SysUserService {@Resourceprivate SysUserMapper sysUserMapper;// 主库插入@Overridepublic Integer insertUserMaster(SysUser sysUser) {return sysUserMapper.insert(sysUser);}// 从库插入@Override@DataSource(value = DataSourceConstants.SLAVE)public Integer insertUserSlave(SysUser sysUser) {return sysUserMapper.insert(sysUser);}
}

📣测试类

@SpringBootTest
public class SysUserTableTest {@Autowiredprivate SysUserService sysUserService;@Testpublic void insertByMaster() {SysUser sysUser = new SysUser();sysUser.setUserName("admin");sysUser.setLoginName("admin");sysUser.setPassword("admin");sysUserService.insertUserMaster(sysUser);}@Testpublic void insertBySlave() {SysUser sysUser = new SysUser();sysUser.setUserName("admin");sysUser.setLoginName("admin");sysUser.setPassword("admin");sysUserService.insertUserSlave(sysUser);}
}

测试主库插入, 不标注注解时, 默认使用主库.
image.png
image.png

测试从库插入, 调用使用了切换数据源注解得到方法
image.png
image.png

类标注注解测试

@Service
@DataSource(value = DataSourceConstants.SLAVE)
public class SysUserServiceImpl implements SysUserService {@Resourceprivate SysUserMapper sysUserMapper;@Overridepublic Integer insertUserMaster(SysUser sysUser) {return sysUserMapper.insert(sysUser);}@Overridepublic Integer insertUserSlave(SysUser sysUser) {return sysUserMapper.insert(sysUser);}
}

调用insertUserMaster, 验证数据源是否切换成功.
image.png
image.png

💯总结

好啦~ 以上主要记录一下自己在学习过程中是如何配置多数据源的(严格来说代码中的写法应该是主从数据源 嘿嘿)。
一般主从数据源主要是为了做读写分离的,后面学习总结完读写分离操作后在进行分享记录啦~~

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

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

相关文章

(转)HttpURLConnection与 HttpClient 区别

转自&#xff1a; HttpURLConnection与 HttpClient 区别/性能测试对比 - 尚码园HttpURLConnection与HttpClient随笔   目前在工做中遇到的须要各类对接接口的工做&#xff0c;须要用到HTTP的知识&#xff0c;工做完成后想要作一些笔记&#xff0c;原本知识打算把本身写的代码…

Raspkate - 基于.NET的可运行于树莓派的轻量型Web服务器

最近在业余时间玩玩树莓派&#xff0c;刚开始的时候在树莓派里写一些基于wiringPi库的C语言程序来控制树莓派的GPIO引脚&#xff0c;从而控制LED发光二极管的闪烁&#xff0c;后来觉得&#xff0c;是不是可以使用HTML5jQuery等流行的前端技术做一个简单的Web站点&#xff0c;让…

深入分析Java中的length和length()

转载自 深入分析Java中的length和length()在开始正文之前&#xff0c;请你快速回答如下问题&#xff1a;在不使用任何带有自动补全功能IDE的情况下&#xff0c;如何获取一个数组的长度&#xff1f;以及&#xff0c;如何获取一个字符串的长度&#xff1f;这个问题我问过不同水平…

归并排序示例

public class MergeSortMain {public static void main(String[] args) {int[] arr {5, 4, 6, 2, 3, 7, 9, 1, 8};sort(arr);print(arr);}static void sort(int[] arr) {sort(arr, 0, arr.length - 1);}/*** param arr 数组* param leftBound 左边界* param rightBoun…

windows监控txt写入_Windows的bug们

2020/9/26○Doriawinterwindows的锁屏界面我很喜欢&#xff0c;这个壁纸自动切换的模块叫windows聚焦&#xff0c;然而我某天开机时发现图片消失&#xff0c;变味了蓝色背景&#xff0c;如图&#xff1a;因为我的已经修好了&#xff0c;所以在网上找了一张一样的图&#xff0c;…

internet地址java表示

【README】 本文主要总结 internet地址相关知识&#xff0c;及相关的 java 类 &#xff1b; 0&#xff09;ip地址是什么 连接到internet的设备称为节点&#xff0c;计算机节点称为主机&#xff08;host&#xff09;&#xff0c;每个节点至少由一个唯一的数&#xff08;或数字…

微软也加入FB开放计算项目 发布交换机操作系统

微软3月10日宣布了一则震动技术业界的消息&#xff0c;它正式发布了基于Debian Linux的网络交换机操作系统。这款名为“SONiC”&#xff08;Software for Open Networking in the Cloud&#xff09;的软件以前仅供微软内部使用&#xff0c;主要运行在网络交换机上。 这条消息对…

Docker-Desktop储存路径更改

前言 Docker是一个非常好用的容器引擎, 使我们部署环境速度大幅度提升。但是windows版本的docker-desktop默认安装路径是C盘&#xff0c;这时候就有一个非常让人头疼的问题 -【C盘储存空间严重不足】。下面主要记录一下怎么解决这一个问题~ 原缓存路径 C:\Users${用户文件}\A…

深入解析String中的intern

转载自 深入解析String中的intern引言 在 JAVA 语言中有8中基本类型和一种比较特殊的类型String。这些类型为了使他们在运行过程中速度更快&#xff0c;更节省内存&#xff0c;都提供了一种常量池的概念。常量池就类似一个JAVA系统级别提供的缓存。 8种基本类型的常量池都是系统…

URLConnection-URL连接

【README】 本文介绍了 URLConnection java类&#xff0c;通过 URLConnection 如何获取网络资源&#xff1b;本文还梳理了涉及网络编程的java类的进化过程&#xff1b;从 URL -> URLConnection -> HttpURLConnection 或 HttpClient ; URL与URI的介绍&#xff0c; refer2…

excel趋势线公式导出_如何用Excel进行预测分析?

【面试题】一个社交APP, 它的新增用户次日留存、7日留存、30日留存分别是52%、25%、14%。请模拟出来&#xff0c;每天如果日新增6万用户&#xff0c;那么第30天&#xff0c;它的日活数会达到多少&#xff1f;请使用Excel进行分析。【分析思路】第1日(次日)留存用户数第1日新增用…

ASP.NET Core 行军记 -----第一步(艰辛的 MVC Hello World)

现在ASP.NET Core还在不断成长、更新中&#xff0c;说不定到了明天又换了个模样&#xff0c;就如同一个小孩&#xff0c;从蹒跚学步&#xff0c;到奔向未来。 所以我们可以相应的去理解更新中所发生的变化&#xff0c;包容它、呵护它&#xff0c;而不是盲目的指责与批评&#x…

记录一下SpringCloud-Gateway使用lb动态路由遇到的坑

前言 前一段时间&#xff0c;学习springcloud-alibaba时候&#xff0c;学习到gateway组件与nacos注册中西一起使用时遇到了一个问题在此记录一下&#xff0c;避免以后遇到又做无用功。 问题 通过gateway配置路由时&#xff0c;uri参数使用lb://xxxx无法映射到对应的服务地址…

Java程序创建Kafka Topic,以及数据生产消费,常用的命令

转自&#xff1a; Java程序创建Kafka Topic&#xff0c;以及数据生产消费&#xff0c;常用的命令_Zyy_z_的博客-CSDN博客_java kafka创建topicKafka简介&#xff1a; Kafka是一个分布式发布——订阅消息传递系统。Kafka快速、可扩展且耐用。它保留主题中的消息源。生产者将数据…

c++ 凸包 分治算法_三维凸包

缘起众所周知&#xff0c;二维凸包可以使用 Graham 扫描 内解决.所以本文来学习一下三维空间中凸包的一种直观算法——增量算法(increment algorithm)分析有一条叫 Willy 的苹果虫一直快乐的居住在一个苹果中&#xff0c;直到有一天有一只仓鼠想吃这个苹果&#xff0c;Willy 自…

深度分析Java的ClassLoader机制(源码级别)

转载自 深度分析Java的ClassLoader机制&#xff08;源码级别&#xff09;Java中的所有类&#xff0c;必须被装载到jvm中才能运行&#xff0c;这个装载工作是由jvm中的类装载器完成的&#xff0c;类装载器所做的工作实质是把类文件从硬盘读取到内存中&#xff0c;JVM在加载类的时…

.NET跨平台之Sake和KoreBuild

最近在了解Asp.net Core 1.0&#xff08;也可称为Asp.net5\Asp.net vNext)的跨平台&#xff0c;发现了两个新的新东西&#xff1a;Sake和KoreBuild&#xff08;或者已经出了很久&#xff09;。 通过国内某度查询资料大部分都是复制黏贴来的&#xff0c;几乎没有详细的介绍。 只…

Ubuntu系统安装准备

前言 最初学习程序开发时&#xff0c;大多数都会教linux系统的基础用法&#xff0c;但是很少以linux为操作系统开发程序&#xff0c;因为windows系统的简单便利为人的提供优秀的操作基础&#xff0c;成为首选系统。 突然心血来找想使用linux操作系统作基础进行程序代码开发。 …

(转)Kafka 消费者 Java 实现

转自&#xff1a; Kafka 消费者 Java 实现 - 简书应用程序使用 KafkaConsumer向 Kafka 订阅 Topic 接收消息&#xff0c;首先理解 Kafka 中消费者&#xff08;consumer&#xff09;和消费者组&#xff08;consumer group...https://www.jianshu.com/p/1f9e18e926f6据原文作者&…

docker export_docker使用简介

一 docker服务端和客户端declare -x DOCKER_HOST"192.168.2.162export DOCKER_HOST192.168.2.162docker -H tcp://192.168.2.162:2375 images二 docker daemon配置cat /etc/sysconfig/dockerOPTIONS--selinux-enabled --log-driverjournald --signature-verificationfalse…