java配置多数据源

三个数据库:master主库、back_one从库1、back_two从库2

1.application.yml配置三个数据库信息

spring:datasource:driver-class-name : com.mysql.jdbc.Driver# 主库master:jdbcUrl : jdbc:mysql://xxx:3306/master?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&serverTimezone=GMT%2b8&allowMultiQueries=trueusername : xxxpassword : xxxname :# 从库1slave:enabled: true  # 从数据源开关/默认关闭jdbcUrl: jdbc:mysql://xxx:3306/back_one?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=GMT%2b8username: xxxpassword: xxxname :# 从库2slave2:enabled: true  # 从数据源开关/默认关闭jdbcUrl: jdbc:mysql://xxx:3306/back_two?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&serverTimezone=GMT%2b8username: xxxpassword: xxxname :druid.maxActive : 150druid.initialSize : 5druid.minIdle : 5druid.maxWait : 60000druid.filters : statdruid.timeBetweenEvictionRunsMillis : 60000druid.minEvictableIdleTimeMillis : 300000druid.validationQuery : SELECT 1    #多个数据源时配置:select count(*) from dualdruid.testWhileIdle : truedruid.testOnBorrow : falsedruid.testOnReturn : falsedruid.poolPreparedStatements : falsedruid.maxPoolPreparedStatementPerConnectionSize : -1druid.timeBetweenLogStatsMillis : 0druid.keep-alive : truedruid.connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000;druid.stat.logSlowSql=true

注意:

一个数据源时:validationQuery: SELECT 1;

多个数据源时:validationQuery: select count(*) from dual

2.代码

(1)DataSource

import java.lang.annotation.*;@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {DataSourceType value() default DataSourceType.MASTER;}

(2)DataSourceAspect

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;@Aspect
@Order(1)
@Component
public class DataSourceAspect {@Pointcut("execution(public * com.xxx.mapper..*.*(..))")public void pointcut() {}@Before("pointcut()")public void doBefore(JoinPoint joinPoint){Method method = ((MethodSignature)joinPoint.getSignature()).getMethod();DataSource dataSource = method.getAnnotation(DataSource.class);  //获取方法上的注解if(dataSource == null){Class<?> clazz= joinPoint.getTarget().getClass().getInterfaces()[0];dataSource =clazz.getAnnotation(DataSource.class);  //获取类上面的注解if(dataSource == null) return;}if (dataSource != null) {DynamicDataSourceContextHolder.setDataSourceType(dataSource.value().name());}}@After("pointcut()")public void after(JoinPoint point) {//清理掉当前设置的数据源,让默认的数据源不受影响DynamicDataSourceContextHolder.clearDataSourceType();}
}

(3)DataSourceConfig

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Primary;import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;@Configuration
public class DataSourceConfig {//主数据源@Value("${spring.datasource.master.jdbcUrl}")private String masterJdbcUrl;@Value("${spring.datasource.master.username}")private String masterUsername;@Value("${spring.datasource.master.password}")private String masterPassword;@Value("${spring.datasource.driver-class-name}")private String masterDriverClassName;@Value("${spring.datasource.master.name}")private String name;//从数据源1@Value("${spring.datasource.slave.jdbcUrl}")private String slaveJdbcUrl1;@Value("${spring.datasource.slave.username}")private String slaveUsername1;@Value("${spring.datasource.slave.password}")private String slavePassword1;@Value("${spring.datasource.driver-class-name}")private String slaveDriverClassName1;@Value("${spring.datasource.slave.name}")private String slaveName;//从数据源2@Value("${spring.datasource.slave2.jdbcUrl}")private String slaveJdbcUrl2;@Value("${spring.datasource.slave2.username}")private String slaveUsername2;@Value("${spring.datasource.slave2.password}")private String slavePassword2;@Value("${spring.datasource.driver-class-name}")private String slaveDriverClassName2;@Value("${spring.datasource.slave2.name}")private String slaveName2;//其他相关配置@Value("${spring.datasource.druid.initialSize}")private int initialSize;@Value("${spring.datasource.druid.minIdle}")private int minIdle;@Value("${spring.datasource.druid.maxWait}")private int maxWait;@Value("${spring.datasource.druid.filters}")private String filters;@Value("${spring.datasource.druid.timeBetweenEvictionRunsMillis}")private int timeBetweenEvictionRunsMillis;@Value("${spring.datasource.druid.minEvictableIdleTimeMillis}")private int minEvictableIdleTimeMillis;@Value("${spring.datasource.druid.validationQuery}")private String validationQuery;@Value("${spring.datasource.druid.testWhileIdle}")private boolean testWhileIdle;@Value("${spring.datasource.druid.testOnBorrow}")private boolean testOnBorrow;@Value("${spring.datasource.druid.testOnReturn}")private boolean testOnReturn;@Value("${spring.datasource.druid.poolPreparedStatements}")private boolean poolPreparedStatements;@Value("${spring.datasource.druid.maxPoolPreparedStatementPerConnectionSize}")private int maxPoolPreparedStatementPerConnectionSize;@Value("${spring.datasource.druid.timeBetweenLogStatsMillis}")private int timeBetweenLogStatsMillis;@Value("${spring.datasource.druid.keep-alive}")private boolean keepAlive;@Value("${spring.datasource.druid.connectionProperties}")private String connectionProperties;@Beanpublic DataSource masterDataSource() {return generateDataSource(masterJdbcUrl,masterUsername,masterPassword,masterDriverClassName,name);}@Bean@ConditionalOnProperty(prefix = "spring.datasource.slave", name = "enabled", havingValue = "true")public DataSource slaveDataSource1() {return generateDataSource(slaveJdbcUrl1,slaveUsername1,slavePassword1,slaveDriverClassName1,slaveName);}@Bean@ConditionalOnProperty(prefix = "spring.datasource.slave2", name = "enabled", havingValue = "true")public DataSource slaveDataSource2() {return generateDataSource(slaveJdbcUrl2,slaveUsername2,slavePassword2,slaveDriverClassName2,slaveName2);}private DruidDataSource generateDataSource(String url,String username,String password,String driverClassName,String name){DruidDataSource datasource = new DruidDataSource();datasource.setUrl(url);datasource.setUsername(username);datasource.setPassword(password);datasource.setDriverClassName(driverClassName);datasource.setName(name);datasource.setInitialSize(initialSize);datasource.setMinIdle(minIdle);datasource.setMaxWait(maxWait);datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);datasource.setValidationQuery(validationQuery);datasource.setTestWhileIdle(testWhileIdle);datasource.setTestOnBorrow(testOnBorrow);datasource.setTestOnReturn(testOnReturn);datasource.setPoolPreparedStatements(poolPreparedStatements);datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);datasource.setKeepAlive(keepAlive);try {datasource.setFilters(filters);} catch (SQLException e) {System.err.println("druid configuration initialization filter: " + e);}datasource.setConnectionProperties(connectionProperties);datasource.setTimeBetweenLogStatsMillis(timeBetweenLogStatsMillis);return datasource;}@Bean(name = "dynamicDataSource")@DependsOn({"masterDataSource", "slaveDataSource1", "slaveDataSource2"})  //如果不加这个,会报错:The dependencies of some of the beans in the application context form a cycle@Primarypublic DynamicDataSource dataSource(DataSource masterDataSource, DataSource slaveDataSource1, DataSource slaveDataSource2) {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DataSourceType.MASTER.name(), masterDataSource);targetDataSources.put(DataSourceType.BACKONE.name(), slaveDataSource1);targetDataSources.put(DataSourceType.BACKTWO.name(), slaveDataSource2);return new DynamicDataSource(masterDataSource, targetDataSources);}}

(4)DataSourceType

public enum DataSourceType {/*** 主库*/MASTER,/*** 从库1*/BACKONE,/*** 从库2*/BACKTWO;
}

(5)DynamicDataSource

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;import javax.sql.DataSource;
import java.util.Map;public class DynamicDataSource extends AbstractRoutingDataSource {public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {super.setDefaultTargetDataSource(defaultTargetDataSource);super.setTargetDataSources(targetDataSources);// afterPropertiesSet()方法调用时用来将targetDataSources的属性写入resolvedDataSources中的super.afterPropertiesSet();}/*** 根据Key获取数据源的信息*/@Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceContextHolder.getDataSourceType();}}

(6)DynamicDataSourceContextHolder

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class DynamicDataSourceContextHolder {public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);/*** 使用ThreadLocal维护变量,ThreadLocal为每个使用该变量的线程提供独立的变量副本,*  所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。*/private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();/*** 设置数据源变量*/public static void setDataSourceType(String dataSourceType){log.info("切换到{}数据源", dataSourceType);CONTEXT_HOLDER.set(dataSourceType);}/*** 获取数据源变量*/public static String getDataSourceType(){return CONTEXT_HOLDER.get();}/*** 清空数据源变量*/public static void clearDataSourceType(){CONTEXT_HOLDER.remove();}}

3.使用

(1)调用从库1

/*** 调用从库1*/
@Component
@DataSource(value = DataSourceType.BACKONE)
public interface TestOneMapper {}

(2)调用从库2

/*** 调用从库2*/
@Component
@DataSource(value = DataSourceType.BACKTWO)
public interface TestTwoMapper {}

欢迎关注我的公众号

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

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

相关文章

pytorch中的tqdm库

tqdm 是一个 Python 的进度条库&#xff0c;名字来源于阿拉伯语 "taqaddum"&#xff08;意思是“进步”&#xff09;。它以简单易用、高效著称&#xff0c;常用于循环操作中显示进度信息。 基本用法 1. 普通循环 tqdm 可以轻松为 for 循环添加进度条&#xff1a; …

MongoDB数据建模小案例

MongoDB数据建模小案例 朋友圈评论内容管理 需求 社交类的APP需求,一般都会引入“朋友圈”功能,这个产品特性有一个非常重要的功能就是评论体系。 先整理下需求: 这个APP希望点赞和评论信息都要包含头像信息: 点赞列表,点赞用户的昵称,头像;评论列表,评论用户的昵称…

针对嵌入式图形界面的图片压缩,几种比较平衡的算法选择:

RLE (Run Length Encoding) 变种 RLE-4: 4位游程编码&#xff0c;适合图标等色彩数较少的图像SRLE (Sparse RLE): 稀疏型RLE&#xff0c;对于UI中大面积相同颜色区域效果很好优点&#xff1a;解码速度快&#xff0c;实现简单缺点&#xff1a;对于复杂图案压缩率较低特别适合&am…

C++研发笔记16——C语言程序设计初阶学习笔记14

本篇笔记我们继续来学习第三部分《分支语句和循环语句》&#xff0c;在上一篇中我们详细学习了循环语句——for语句以及循环语句——do-while循环&#xff0c;接下来本部分的内容还包括&#xff1a;折半查找算法、猜数字游戏、goto语句。本篇笔记便可以结束第三部分的知识学习部…

三一集团Java开发面试题及参考答案

Java 中有几种常见的线程池? 在 Java 中,常见的线程池有以下几种: 首先是 FixedThreadPool(固定线程数线程池)。它的特点是创建一个固定大小的线程池,线程数量在初始化的时候就已经确定。例如,通过 Executors.newFixedThreadPool (int nThreads) 来创建,其中 nThreads …

CSS元素宽高特点、类型转化、显式和隐藏(display)

元素的宽高特点 块级元素 可以设置宽高&#xff0c;不可以和其他元素在一行设置宽高时&#xff0c;元素的宽高为设置的值没有设置宽高时&#xff0c;宽度和父级宽高一样&#xff0c;高度由元素内容决定 行级元素 不可以设置宽高&#xff0c;可以和其他元素在一行元素的宽高…

k8s的Pod亲和性

针对对象为Pod&#xff0c;目的是实现&#xff0c;新建Pod和目标Pod调度到一起&#xff0c;在同一个Node上。 示例&#xff1a; rootk8s-master:/home/vagrant# cat pod.yaml apiVersion: v1 kind: Pod metadata:name: testpod01labels:app: myapp01env: test1 spec:conta…

视频推拉流EasyDSS无人机直播技术巡查焚烧、烟火情况

焚烧作为一种常见的废弃物处理方式&#xff0c;往往会对环境造成严重污染。因此&#xff0c;减少焚烧、推广绿色能源和循环经济成为重要措施。通过加强森林防灭火队伍能力建设与长效机制建立&#xff0c;各地努力减少因焚烧引发的森林火灾&#xff0c;保护生态环境。 巡察烟火…

K8S对接ceph的RBD块存储

1 PG数量限制问题 1.1 原因分析 1.还是老样子&#xff0c;先创建存储池&#xff0c;在初始化为rbd。 [rootceph141~]# ceph osd pool create wenzhiyong-k8s 128 128 Error ERANGE: pg_num 128 size 3 for this pool would result in 295 cumulative PGs per OSD (2067 tot…

React Router 6的学习

安装react-router-dom npm i react-router-dom 支持不同的路由创建 createBrowserRouter 特点 推荐使用的方式&#xff0c;基于 HTML5 的 History API。支持用户友好的 URL&#xff0c;无需 #。适用于生产环境的绝大多数场景。 适用 使用现代浏览器&#xff0c;支持 pus…

React第十四节useState使用详解差异

一、useState() Hook 使用 useState视图更新用法 1、写法&#xff1a; import { useState } from react const [name, setName] useState(Andy)利用数组解构写法&#xff0c; 第一个参数是自定义的属性&#xff0c;用于初始化时候渲染&#xff0c;如上面代码&#xff0c;初…

微信小程序web-view 嵌套h5界面 实现文件预览效果

实现方法&#xff1a;(这里我是在小程序里面单独加了一个页面用来下载预览文件) 安装 使用方法请参考文档 npm 安装 npm install weixin-js-sdk import wx from weixin-js-sdk预览 h5界面代码 <u-button click"onclick" type"primary" :loading"…

HTTP 状态码大全

常见状态码 200 OK # 客户端请求成功 400 Bad Request # 客户端请求有语法错误 不能被服务器所理解 401 Unauthorized # 请求未经授权 这个状态代码必须和WWW- Authenticate 报头域一起使用 403 Forbidden # 服务器收到请求但是拒绝提供服务 404 Not Found # 请求资源不存…

一文详解TCP协议 [图文并茂, 明了易懂]

欢迎来到啊妮莫的学习小屋! 目录 什么是TCP协议 TCP协议特点✨ TCP报文格式 三次握手和四次挥手✨ 可靠性 效率性 基于字节流✨ 基于TCP的应用层协议 什么是TCP协议 TCP(传输控制协议, Transmission Control Protocol) 是一种面向连接的, 可靠的, 基于字节流的传输层通…

在Linux(ubuntu22.04)搭建rust开发环境

1.安装rust 1.安装curl: sudo apt install curl 2.安装rust最新版 curl --proto ‘https’ --tlsv1.2 https://sh.rustup.rs -sSf | sh 安装完成后出现&#xff1a;Rust is installed now. Great! 重启当前shell即可 3.检验是否安装成功 rustc --version 结果出现&…

UnityShaderLab 实现程序化形状(一)

1.实现一个长宽可变的矩形&#xff1a; 代码&#xff1a; fixed4 frag (v2f i) : SV_Target{return saturate(length(saturate(abs(i.uv - 0.5)-0.13)))/0.03;} 2.实现一个半径可变的圆形&#xff1a; 代码&#xff1a; fixed4 frag (v2f i) : SV_Target{return (distance(a…

如何解决压测过程中JMeter堆内存溢出问题

如何解决压测过程中JMeter堆内存溢出问题 背景一、为什么会堆内存溢出&#xff1f;二、解决堆内存溢出措施三、堆内存参数应该怎么调整&#xff1f;四、堆内存大小配置建议 背景 Windows环境下使用JMeter压测运行一段时间后&#xff0c;JMeter日志窗口报错“java.lang.OutOfMe…

第46届世界技能大赛网络安全赛项安徽省选拔赛模块B任务书参考

第46届世界技能大赛网络安全赛项安徽省选拔赛模块B任务书参考 介绍:项目和任务的描述第一部分 网络安全事件响应任务一:事件调查取证任务二:web应用系统漏洞修复任务三:操作系统恶意文件检测(动态)第二部分数字调查取证任务一 :操作系统恶意文件检测(静态)(win.img和mem.dump)…

spark3 sql优化:同一个表关联多次,优化方案

目录 1.合并查询2.使用 JOIN 条件的过滤优化3.使用 Map-side Join 或 Broadcast Join4.使用 Partitioning 和 Bucketing5.利用 DataFrame API 进行优化假设 A 和 B 已经加载为 DataFramePerform left joins with specific conditions6.使用缓存或持久化7.避免笛卡尔积总结 1.合…

宽字节注入

尽管现在呼吁所有的程序都使用unicode编码&#xff0c;所有的网站都使用utf-8编码&#xff0c;来一个统一的国际规范。但仍然有很多&#xff0c;包括国内及国外&#xff08;特别是非英语国家&#xff09;的一些cms&#xff0c;仍然使用着自己国家的一套编码&#xff0c;比如gbk…