日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

springboot 实现主从数据库动态切换,可实现读写分离

發布時間:2024/3/24 数据库 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 springboot 实现主从数据库动态切换,可实现读写分离 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

使用 AbstractRoutingDataSource 實現功能,代碼完整貼出,直接放心食用。?

從AbstractRoutingDataSource源碼角度簡單分析為什么可以實現數據庫動態切換。


前言

主從數據庫的配置,實現數據同步,配置可參考:

windows配置mysql8.0主從數據庫_追尋光的方向的博客-CSDN博客

一、AbstractRoutingDataSource

Spring boot提供了AbstractRoutingDataSource 根據用戶定義的規則選擇當前的數據源,這樣我們每次訪問數據庫之前,設置使用的數據源。從而實現動態切換數據源。

1.源碼簡單分析

?

?AbstractRoutingDataSource 繼承了?AbstractDataSource 類 從而實現了DataSource接口的getConnection()方法。

在AbstractRoutingDataSource 實現 getConnection 的具體方法源碼可以看到 ,最終獲取的數據源是DataSource dataSource = (DataSource)this.resolvedDataSources.get(lookupKey); 這一行。

resolvedDataSources是一個Map對象,就是說動態的數據源是放在這個Map對象的。

Object lookupKey = determineCurrentLookupKey(); 提供一個Map的key來獲取

那往下看,我們看下這個?resolvedDataSources 是怎么初始化的,

afterPropertiesSet 方法是?InitializingBean 接口的實現,InitializingBean接口為bean提供了初始化方法的方式,凡是繼承該接口的類,在初始化bean的時候都會執行該方法。

但是源碼中 resolvedDataSources 的來源是 targetDataSources?

那再來看看targetDataSources 是怎么初始化的。

2.源碼分析結論

到此,我們思路就有了

1.在每次數據庫訪問的時候能控制讀取存放數據源resolvedDataSources的key值。

2.調用setTargetDataSources方法,來初始化targetDataSources對象。

二、實現步驟

1.創建一個類繼承AbstractRoutingDataSource

import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;public class RoutingDataSource extends AbstractRoutingDataSource {private Logger logger = LogManager.getLogger();@Overrideprotected Object determineCurrentLookupKey() {String dataSource = RoutingDataSourceHolder.getDataSource();logger.info("使用數據源:{}", dataSource);return dataSource;} }

重寫?determineCurrentLookupKey方法,返回要使用的數據源key值。

2.創建一個管理數據源key值得類RoutingDataSourceHolder?

import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger;public class RoutingDataSourceHolder {private static Logger logger = LogManager.getLogger();private static final ThreadLocal<String> dataSources = new ThreadLocal<>();//一個事務內用同一個數據源public static void setDataSource(String dataSourceName) {if (dataSources.get() == null) {dataSources.set(dataSourceName);logger.info("設置數據源:{}", dataSourceName);}}public static String getDataSource() {return dataSources.get();}public static void clearDataSource() {dataSources.remove();} }

代碼設置了一個事務內使用同一個數據源。

兩個類解決了動態數據源key值的問題,下面處理初始化targetDataSources對象。

3.配置主從數據庫類DataSourceConfigurer

1.DataSourceConfigurer

import com.alibaba.druid.pool.DruidDataSource; import com.custom.common.utils.StringUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.context.properties.ConfigurationProperties; 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.util.HashMap; import java.util.Map;/*** 配置主從數據庫*/ @Configuration public class DataSourceConfigurer {private Logger logger = LogManager.getLogger();public final static String MASTER_DATASOURCE = "masterDataSource";public final static String SLAVE_DATASOURCE = "slaveDataSource";@Bean(MASTER_DATASOURCE)@ConfigurationProperties(prefix = "spring.datasource")public DruidDataSource masterDataSource(DataSourceProperties properties) {DruidDataSource build = properties.initializeDataSourceBuilder().type(DruidDataSource.class).build();logger.info("配置主數據庫:{}", build);return build;}@Bean(SLAVE_DATASOURCE)@ConfigurationProperties(prefix = "spring.slave-datasource")public DruidDataSource slaveDataSource(DataSourceProperties properties) {DruidDataSource build = properties.initializeDataSourceBuilder().type(DruidDataSource.class).build();logger.info("配置從數據庫:{}", build);return build;}/*** Primary 優先使用該Bean* DependsOn 先執行主從數據庫的配置* Qualifier 指定使用哪個Bean** @param masterDataSource* @param slaveDataSource* @return*/@Bean@Primary@DependsOn(value = {MASTER_DATASOURCE, SLAVE_DATASOURCE})public DataSource routingDataSource(@Qualifier(MASTER_DATASOURCE) DruidDataSource masterDataSource,@Qualifier(SLAVE_DATASOURCE) DruidDataSource slaveDataSource) {if (StringUtils.isBlank(slaveDataSource.getUrl())) {logger.info("沒有配置從數據庫,默認使用主數據庫");return masterDataSource;}Map<Object, Object> map = new HashMap<>();map.put(DataSourceConfigurer.MASTER_DATASOURCE, masterDataSource);map.put(DataSourceConfigurer.SLAVE_DATASOURCE, slaveDataSource);RoutingDataSource routing = new RoutingDataSource();//設置動態數據源routing.setTargetDataSources(map);//設置默認數據源routing.setDefaultTargetDataSource(masterDataSource);logger.info("主從數據庫配置完成");return routing;} }

設置初始化targetDataSources對象關鍵代碼

Map<Object, Object> map = new HashMap<>(); map.put(DataSourceConfigurer.MASTER_DATASOURCE, masterDataSource); map.put(DataSourceConfigurer.SLAVE_DATASOURCE, slaveDataSource); RoutingDataSource routing = new RoutingDataSource(); //設置動態數據源 routing.setTargetDataSources(map); //設置默認數據源 routing.setDefaultTargetDataSource(masterDataSource);

2.application.properties

# ---------------------------------------- # 主數據庫 # ---------------------------------------- spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.url=jdbc:mysql://127.0.0.1:3306/custom?useunicode=true&characterEncoding=utf8&serverTimezone=GMT%2b8 spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver# ---------------------------------------- # 從數據庫 # ---------------------------------------- spring.slave-datasource.type=com.alibaba.druid.pool.DruidDataSource spring.slave-datasource.url=jdbc:mysql://127.0.0.1:3309/custom?useunicode=true&characterEncoding=utf8&serverTimezone=GMT%2b8 spring.slave-datasource.username=root spring.slave-datasource.password=root spring.slave-datasource.driver-class-name=com.mysql.cj.jdbc.Driver

一個配置類處理了targetDataSources對象的初始化.

那問題都處理了,那具體要怎么使用呢,關鍵就是在事務之前調用RoutingDataSourceHolder.setDataSource()方法就可以了。我們寫一個aop實現吧。

4.創建aop注解和類

1.DataSourceWith

import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) @Documented public @interface DataSourceWith {String key() default ""; }

2.DataSourceWithAspect

import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.*; 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)// 保證該AOP在@Transactional之前運行 @Component public class DataSourceWithAspect {/*** 使用DataSourceWith注解就攔截*/@Pointcut("@annotation(com.custom.configure.datasource.DataSourceWith)||@within(com.custom.configure.datasource.DataSourceWith)")public void doPointcut() {}/*** 方法前,為了在事務前設置*/@Before("doPointcut()")public void doBefore(JoinPoint joinPoint) {MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();Method method = methodSignature.getMethod();// 獲取注解對象DataSourceWith dataSource = method.getAnnotation(DataSourceWith.class);if (dataSource == null) {//方法沒有就獲取類上的dataSource = method.getDeclaringClass().getAnnotation(DataSourceWith.class);}String key = dataSource.key();RoutingDataSourceHolder.setDataSource(key);}@After("doPointcut()")public void doAfter(JoinPoint joinPoint) {RoutingDataSourceHolder.clearDataSource();}}

3.使用和效果

@DataSourceWith在方法上或者類上都可以。

/*** 獲取部門列表**/@DataSourceWith(key = DataSourceConfigurer.SLAVE_DATASOURCE)public List<Dept> findDeptTree() {logger.debug("獲取部門樹數據");List<Dept> deptList = new ArrayList<>();return deptList;}

結果:動態切換成功

?

總結

以上是生活随笔為你收集整理的springboot 实现主从数据库动态切换,可实现读写分离的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 一边摸内裤一边吻胸 | 香蕉久久夜色精品国产使用方法 | 欧美精品123区| 丝袜视频一区 | 在线观看黄网址 | 色欲欲www成人网站 老色鬼av | 在线观看视频99 | 久久久久久久久久久影院 | 一级肉体全黄裸片 | 色综合久久天天综合网 | 久久精品在线视频 | 黄色网炮 | 一区二区三区在线免费观看 | 久久午夜无码鲁丝片午夜精品 | 国产精品二区一区二区aⅴ 一卡二卡三卡在线观看 | 黄色av软件 | 东京热无码av一区二区 | 亚洲国产一区二区三区 | jizzjizz在线观看 | 亚洲最新在线视频 | 久久久香蕉 | 四川少妇xxx奶大xxx | 尤物视频在线观看国产 | 激情综合婷婷 | 91精品久久久久久久久久入口 | 男女作爱网站 | 亚洲一区二区在线观看视频 | 波多野结衣黄色网址 | 国产日韩欧美精品一区 | www.av欧美 | 久久精品久久国产 | 国产一级二级在线观看 | 国产三级av在线 | 成年激情网 | 污视频免费在线观看网站 | 天天综合网久久综合网 | 黑人巨大猛烈捣出白浆 | 久久婷婷国产麻豆91 | 日本精品久久 | 亚洲电影一区二区 | 精品国产伦一区二区三 | 双性娇喘浑圆奶水h男男漫画 | 女生被男生c | 最近中文字幕mv免费高清在线 | 超碰av男人的天堂 | 国产欧美日韩综合精品一区二区 | 狼性av| 亚洲激情视频网 | 欧美三级视频在线观看 | 青青草国产成人av片免费 | 亚洲精品1区2区 | 最新三级网站 | 黑人性视频 | 麻豆乱码国产一区二区三区 | 毛片在线视频观看 | 国产一区免费在线 | 国产一区二区三区自拍 | 欧美性69 | 精品久久久国产 | 蘑菇福利视频一区播放 | 欧美一区二区三区观看 | 超碰在线伊人 | 青青青视频免费 | 亚洲国产精品毛片 | 亚洲一区二区三区蜜桃 | 国产伦精品一区二区三区在线观看 | 美女少妇av| 成人免费无码大片a毛片抽搐色欲 | 九九九九九九精品 | 色香天天 | 三年中文在线观看免费观看 | 看个毛片 | 一个人在线观看www软件 | 国产不卡视频在线 | 免费黄色在线网站 | 99ri精品| 在线三级av| 成人黄色在线看 | 欧美色图亚洲视频 | 操干视频 | 国产精彩视频 | 欧美天天性影院 | 一级免费毛片 | 激情五月综合 | 色多多在线观看 | 国产白丝精品91爽爽久久 | 蜜桃视频成人在线观看 | 五月天久久久久 | 青青草免费在线观看视频 | 风间由美一区 | 99精品一级欧美片免费播放 | 中文字幕有码在线 | 国产99久久九九精品无码 | 一区二区三区四区精品 | 黄色片网站在线观看 | www.射| 精品国产91久久久久久久妲己 | 黄污视频在线观看 | 亚洲国产日韩一区无码精品久久久 |