SpringBoot配置多數據源多數據庫

Springboot支持配置多數據源。默認情況,在yml文件中只會配置一個數據庫。如果涉及到操作多個數據庫的情況,在同實例中(即同一個ip地址下的不同數據庫),可以采用數據庫名點數據庫表的方式,實現跨庫表的操作。(database.table)但這種方式屬于硬編碼,如果數據庫名變化意味著代碼也要變化,不易維護。

下面是在springboot項目中,配置多數據源。需要添加幾個配置類,和相關注解的方式,實現在某個特定方法層面下切換數據源。

1、目錄結構

2、具體代碼

1)DataSource

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface DataSource {String name() default "";
}

2)DataSourceAspect

/*** 多數據源,切面處理類**/
@Aspect
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class DataSourceAspect implements Ordered {protected Logger logger = LoggerFactory.getLogger(getClass());@Pointcut("@annotation(io.installer.commons.dynamic.datasource.annotation.DataSource)")public void dataSourcePointCut() {}@Around("dataSourcePointCut()")public Object around(ProceedingJoinPoint point) throws Throwable {MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();DataSource ds = method.getAnnotation(DataSource.class);if (ds == null) {DynamicDataSource.setDataSource(DataSourceNames.FIRST);logger.debug("set datasource is " + DataSourceNames.FIRST);} else {DynamicDataSource.setDataSource(ds.name());logger.debug("set datasource is " + ds.name());}try {return point.proceed();} finally {DynamicDataSource.clearDataSource();logger.debug("clean datasource");}}@Overridepublic int getOrder() {return 1;}
}

3)DataSourceNames

public interface DataSourceNames {String FIRST = "first";String SECOND = "second";
}

4)DynamicContextHolder

/*** 多數據源上下文**/
public class DynamicContextHolder {private static final ThreadLocal<Deque<String>> CONTEXT_HOLDER = ThreadLocal.withInitial(ArrayDeque::new);/*** 獲得當前線程數據源** @return 數據源名稱*/public static String peek() {return CONTEXT_HOLDER.get().peek();}/*** 設置當前線程數據源** @param dataSource 數據源名稱*/public static void push(String dataSource) {CONTEXT_HOLDER.get().push(dataSource);}/*** 清空當前線程數據源*/public static void poll() {Deque<String> deque = CONTEXT_HOLDER.get();deque.poll();if (deque.isEmpty()) {CONTEXT_HOLDER.remove();}}}

5)DynamicDataSource

/*** 多數據源** @author Mark sunlightcs@gmail.com* @since 1.0.0*/
public class DynamicDataSource extends AbstractRoutingDataSource {private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {//設置默認數據源super.setDefaultTargetDataSource(defaultTargetDataSource);super.setTargetDataSources(targetDataSources);super.afterPropertiesSet();}@Overrideprotected Object determineCurrentLookupKey() {//獲取數據源,沒有指定,則為默認數據源return getDataSource();}public static void setDataSource(String dataSource) {contextHolder.set(dataSource);}public static String getDataSource() {return contextHolder.get();}public static void clearDataSource() {contextHolder.remove();}}

6)DynamicDataSourceConfig

@Configuration
@EnableConfigurationProperties(DynamicDataSourceProperties.class)
public class DynamicDataSourceConfig {//數據源1,讀取spring.datasource.druid.first下的配置信息@Bean@ConfigurationProperties("spring.datasource.druid.first")public DataSource firstDataSource() {return DruidDataSourceBuilder.create().build();}//數據源2,讀取spring.datasource.druid.second下的配置信息@Bean@ConfigurationProperties("spring.datasource.druid.second")public DataSource secondDataSource() {return DruidDataSourceBuilder.create().build();}//加了@Primary注解,表示指定DynamicDataSource為Spring的數據源//因為DynamicDataSource是繼承與AbstractRoutingDataSource,而AbstractR//outingDataSource又是繼承于AbstractDataSource,AbstractDataSource實現了統一//的DataSource接口,所以DynamicDataSource也可以當做DataSource使用@Bean@Primarypublic DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource) {Map<Object, Object> targetDataSources = new HashMap<>();targetDataSources.put(DataSourceNames.FIRST, firstDataSource);targetDataSources.put(DataSourceNames.SECOND, secondDataSource);return new DynamicDataSource(firstDataSource, targetDataSources);}
}

7)DynamicDataSourceFactory

public class DynamicDataSourceFactory {public static DruidDataSource buildDruidDataSource(DataSourceProperties properties) {DruidDataSource druidDataSource = new DruidDataSource();druidDataSource.setDriverClassName(properties.getDriverClassName());druidDataSource.setUrl(properties.getUrl());druidDataSource.setUsername(properties.getUsername());druidDataSource.setPassword(properties.getPassword());druidDataSource.setInitialSize(properties.getInitialSize());druidDataSource.setMaxActive(properties.getMaxActive());druidDataSource.setMinIdle(properties.getMinIdle());druidDataSource.setMaxWait(properties.getMaxWait());druidDataSource.setTimeBetweenEvictionRunsMillis(properties.getTimeBetweenEvictionRunsMillis());druidDataSource.setMinEvictableIdleTimeMillis(properties.getMinEvictableIdleTimeMillis());druidDataSource.setMaxEvictableIdleTimeMillis(properties.getMaxEvictableIdleTimeMillis());druidDataSource.setValidationQuery(properties.getValidationQuery());druidDataSource.setValidationQueryTimeout(properties.getValidationQueryTimeout());druidDataSource.setTestOnBorrow(properties.isTestOnBorrow());druidDataSource.setTestOnReturn(properties.isTestOnReturn());druidDataSource.setPoolPreparedStatements(properties.isPoolPreparedStatements());druidDataSource.setMaxOpenPreparedStatements(properties.getMaxOpenPreparedStatements());druidDataSource.setSharePreparedStatements(properties.isSharePreparedStatements());try {// druidDataSource.setFilters(properties.getFilters());druidDataSource.init();} catch (SQLException e) {e.printStackTrace();}return druidDataSource;}
}

8)DataSourceProperties

public class DataSourceProperties {private String driverClassName;private String url;private String username;private String password;/*** Druid默認參數*/private int initialSize = 2;private int maxActive = 10;private int minIdle = -1;private long maxWait = 60 * 1000L;private long timeBetweenEvictionRunsMillis = 60 * 1000L;private long minEvictableIdleTimeMillis = 1000L * 60L * 30L;private long maxEvictableIdleTimeMillis = 1000L * 60L * 60L * 7;private String validationQuery = "select 1";private int validationQueryTimeout = -1;private boolean testOnBorrow = false;private boolean testOnReturn = false;private boolean testWhileIdle = true;private boolean poolPreparedStatements = false;private int maxOpenPreparedStatements = -1;private boolean sharePreparedStatements = false;private String filters = "stat,wall";public String getDriverClassName() {return driverClassName;}public void setDriverClassName(String driverClassName) {this.driverClassName = driverClassName;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public int getInitialSize() {return initialSize;}public void setInitialSize(int initialSize) {this.initialSize = initialSize;}public int getMaxActive() {return maxActive;}public void setMaxActive(int maxActive) {this.maxActive = maxActive;}public int getMinIdle() {return minIdle;}public void setMinIdle(int minIdle) {this.minIdle = minIdle;}public long getMaxWait() {return maxWait;}public void setMaxWait(long maxWait) {this.maxWait = maxWait;}public long getTimeBetweenEvictionRunsMillis() {return timeBetweenEvictionRunsMillis;}public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;}public long getMinEvictableIdleTimeMillis() {return minEvictableIdleTimeMillis;}public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;}public long getMaxEvictableIdleTimeMillis() {return maxEvictableIdleTimeMillis;}public void setMaxEvictableIdleTimeMillis(long maxEvictableIdleTimeMillis) {this.maxEvictableIdleTimeMillis = maxEvictableIdleTimeMillis;}public String getValidationQuery() {return validationQuery;}public void setValidationQuery(String validationQuery) {this.validationQuery = validationQuery;}public int getValidationQueryTimeout() {return validationQueryTimeout;}public void setValidationQueryTimeout(int validationQueryTimeout) {this.validationQueryTimeout = validationQueryTimeout;}public boolean isTestOnBorrow() {return testOnBorrow;}public void setTestOnBorrow(boolean testOnBorrow) {this.testOnBorrow = testOnBorrow;}public boolean isTestOnReturn() {return testOnReturn;}public void setTestOnReturn(boolean testOnReturn) {this.testOnReturn = testOnReturn;}public boolean isTestWhileIdle() {return testWhileIdle;}public void setTestWhileIdle(boolean testWhileIdle) {this.testWhileIdle = testWhileIdle;}public boolean isPoolPreparedStatements() {return poolPreparedStatements;}public void setPoolPreparedStatements(boolean poolPreparedStatements) {this.poolPreparedStatements = poolPreparedStatements;}public int getMaxOpenPreparedStatements() {return maxOpenPreparedStatements;}public void setMaxOpenPreparedStatements(int maxOpenPreparedStatements) {this.maxOpenPreparedStatements = maxOpenPreparedStatements;}public boolean isSharePreparedStatements() {return sharePreparedStatements;}public void setSharePreparedStatements(boolean sharePreparedStatements) {this.sharePreparedStatements = sharePreparedStatements;}public String getFilters() {return filters;}public void setFilters(String filters) {this.filters = filters;}
}

9)DynamicDataSourceProperties

@ConfigurationProperties(prefix = "dynamic")
public class DynamicDataSourceProperties {private Map<String, DataSourceProperties> datasource = new LinkedHashMap<>();public Map<String, DataSourceProperties> getDatasource() {return datasource;}public void setDatasource(Map<String, DataSourceProperties> datasource) {this.datasource = datasource;}
}

10)yml配置

spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedruid:first:#MySQLurl: jdbc:mysql://localhost:3306/database1?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8username: rootpassword: rootsecond:#MySQLurl: jdbc:mysql://localhost:3306/database2?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&serverTimezone=GMT%2B8username: rootpassword: rootdriver-class-name: com.mysql.cj.jdbc.Driverinitial-size: 10max-active: 100min-idle: 10max-wait: 6000pool-prepared-statements: truemax-pool-prepared-statement-per-connection-size: 20time-between-eviction-runs-millis: 60000min-evictable-idle-time-millis: 300000#Oracle需要打開注釋#      validation-query: SELECT 1 FROM DUALtest-while-idle: truetest-on-borrow: falsetest-on-return: falsestat-view-servlet:enabled: trueurl-pattern: /druid/*

3、測試代碼

1)DynamicDataSourceTestService

/*** 測試多數據源** @author Mark sunlightcs@gmail.com*/
@Service
//@DataSource(name = DataSourceNames.FIRST)
public class DynamicDataSourceTestService {@Resourceprivate SysUserDao sysUserDao;//@Transactionalpublic void updateUser(Long id) {SysUserEntity user = new SysUserEntity();user.setId(id);user.setMobile("13500000000");//sysUserDao.updateById(user);System.out.println(sysUserDao.selectById(id));}@DataSource(name = DataSourceNames.SECOND)@Transactionalpublic void updateUserBySlave1(Long id) {SysUserEntity user = new SysUserEntity();user.setId(id);user.setMobile("13500000001");//sysUserDao.updateById(user);System.out.println(sysUserDao.selectById(id));}//    @DataSource(name = DataSourceNames.SECOND)
//    @Transactional
//    public void updateUserBySlave2(Long id){
//        SysUserEntity user = new SysUserEntity();
//        user.setId(id);
//        user.setMobile("13500000002");
//        sysUserDao.updateById(user);
//
//        //測試事物
//        int i = 1/0;
//    }
}

2)DynamicDataSourceTest

@RunWith(SpringRunner.class)
@SpringBootTest
public class DynamicDataSourceTest {@Resourceprivate DynamicDataSourceTestService dynamicDataSourceTestService;@Testpublic void test() {Long id = 1067246875800000001L;//        dynamicDataSourceTestService.updateUser(id);
//        dynamicDataSourceTestService.updateUserBySlave1(id);dynamicDataSourceTestService.updateUserBySlave2(id);}}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/90647.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/90647.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/90647.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Rocky9.4部署Zabbix7

一、配置安裝源 rpm -Uvh https://repo.zabbix.com/zabbix/7.0/rocky/9/x86_64/zabbix-release-7.0-5.el9.noarch.rpm ? yum clean all 二、安裝Zabbix server&#xff0c;Web前端&#xff0c;agent yum install zabbix-server-mysql zabbix-web-mysql zabbix-nginx-conf z…

【Java】對象類型轉換(ClassCastException)異常:從底層原理到架構級防御,老司機的實戰經驗

在開發中&#xff0c;ClassCastException&#xff08;類轉換異常&#xff09;就像一顆隱藏的定時炸彈&#xff0c;常常在代碼運行到類型轉換邏輯時突然爆發。線上排查問題時&#xff0c;這類異常往往因為類型關系復雜而難以定位。多數開發者習慣于在轉換前加個instanceof判斷就…

探路者:用 AI 面試加速人才集結,為戶外愛好者帶來更專業的服務

作為深耕戶外用品領域的知名品牌&#xff0c;探路者已構建起覆蓋全國的銷售服務網絡&#xff0c;上千品種的產品矩陣更是為品牌在市場中站穩腳跟提供了有力支撐。對探路者來說&#xff0c;要持續為戶外愛好者帶來專業且貼心的體驗&#xff0c;專業人才是核心支撐。然而&#xf…

LeetCode——面試題 05.01 插入

通過萬歲&#xff01;&#xff01;&#xff01; 題目&#xff1a;一共會給四個數&#xff0c;分別是N、M、i、j&#xff0c;然后希望我們把N和M抓怒換為2進制以后&#xff0c;將M的二進制放在i到j之間的區域&#xff0c;如果M的二進制長度小于i-j1&#xff0c;則前面補0即可。最…

前端設計中如何在鼠標懸浮時同步修改塊內樣式

雖然只是一個小問題&#xff0c;但這個解決問題的過程也深化了自己對盒子模型的理解問題緣起正在寫一個登錄注冊的小窗口&#xff0c;想要在鼠標懸浮階段讓按鈕和文字都變色&#xff0c;但是發現實操的時候按鈕和文字沒辦法同時變色鼠標懸停前鼠標懸停后問題分析仔細分析了下該…

航空發動機高速旋轉件的非接觸式信號傳輸系統

航空發動機是飛機動力系統的核心&#xff0c;各種關鍵部件如渦輪、壓氣機等&#xff0c;經常處于極端高溫、高速旋轉的工作環境中。航空發動機內的傳感器數據&#xff0c;如何能夠穩定可靠的通過無線的方式傳輸到檢測太&#xff0c;一直是業內的一個難點和痛點。在這個領域&…

【postgresql按照逗號分割字段,并統計數量和求和】

postgresql按照逗號分割字段&#xff0c;并統計數量和求和postgresql按照逗號分割字段&#xff0c;并統計數量和求和postgresql按照逗號分割字段&#xff0c;并統計數量和求和 SELECT ucd, p ,tm, step, unitcd, tm_end from resource_calc_scene_rain_bound_value_plus whe…

「iOS」————繼承鏈與對象的結構

iOS學習前言對象的底層結構isa的類型isa_tobjc_class & objc_object類信息的靜態與動態存儲&#xff08;ro、rw、rwe機制&#xff09;cachebits繼承鏈isKindOfClass和isMemberOfClassisKindOfClass:isMemberofClass前言 對 對象底層結構的相關信息有點遺忘&#xff0c;簡略…

代碼隨想錄day46dp13

647. 回文子串 題目鏈接 文章講解 回溯法 class Solution { public:int count 0;// 檢查字符串是否是回文bool isPalindrome(string& s, int start, int end) {while (start < end) {if (s[start] ! s[end]) return false;start;end--;}return true;}// 回溯法&#…

學習隨筆錄

#61 學習隨筆錄 今日的思考 &#xff1a; 反思一下學習效率低下 不自律 或者 惰性思維 懶得思考 又或者 好高婺遠 頂級自律從不靠任何意志力&#xff0c;而在于「平靜如水的野心」_嗶哩嗶哩_bilibili 然后上面是心靈雞湯合集 vlog #79&#xff5c;程序員遠程辦公的一天…

python-函數進階、容器通用方法、字符串比大小(筆記)

python數據容器的通用方法#記住排序后容器類型會變成list容器列表 list[1,3,5,4,6,7] newListsorted(list,reverseTrue) print(newList) [7, 6, 5, 4, 3, 1]list[1,3,5,4,6,7] newListsorted(list,reverseFalse) print(newList) [1, 3, 4, 5, 6, 7]字典排序的是字典的key字符串…

關閉chrome自帶的跨域限制,簡化本地開發

在開發時為了圖方便,簡化本地開發,懶得去后端配置允許跨域,那就可以用此方法1. 右鍵桌面上的Chrome瀏覽器圖標&#xff0c;選擇“創建快捷方式”到桌面。2. 在新創建的快捷方式的圖標上右鍵&#xff0c;選擇“屬性”。3. 在彈出窗口中的“目標”欄中追加&#xff1a; --allow-r…

C++___快速入門(上)

第一個C程序#include<iostream> using namespace std; int main() {cout << "hello world !" << endl;return 0; }上邊的代碼就是用來打印字符串 “hello world !” 的&#xff0c;可見&#xff0c;與C語言還是有很大的差別的&#xff0c;接下來我…

構建企業級Docker日志驅動:將容器日志無縫發送到騰訊云CLS

源碼地址:https://github.com/k8scat/docker-log-driver-tencent-cls 在現代云原生架構中,容器化應用已經成為主流部署方式。隨著容器數量的快速增長,如何高效地收集、存儲和分析容器日志成為了一個關鍵挑戰。傳統的日志收集方式往往存在以下問題: 日志分散在各個容器中,難…

Kafka——消費者組重平衡能避免嗎?

引言 其實在消費者組到底是什么&#xff1f;中&#xff0c;我們講過重平衡&#xff0c;也就是Rebalance&#xff0c;現在先來回顧一下這個概念的原理和用途。它是Kafka實現消費者組&#xff08;Consumer Group&#xff09;彈性伸縮和容錯能力的核心機制&#xff0c;卻也常常成…

使用爬蟲獲取游戲的iframe地址

如何通過爬蟲獲取游戲的iframe地址要獲取網頁中嵌入的游戲的iframe地址&#xff08;即iframe元素的src屬性&#xff09;&#xff0c;您可以使用網絡爬蟲技術。iframe是HTML元素&#xff0c;用于在當前頁面中嵌入另一個文檔&#xff08;如游戲頁面&#xff09;&#xff0c;其地址…

NTLite Ent Version

NTLite是一款專業的系統安裝鏡像制作工具&#xff0c;通過這款軟件可以幫助用戶快速生成鏡像文件打好補丁&#xff0c;很多朋友在安裝電腦系統的時候一般都安裝了windows系統的所有Windows組件&#xff0c;其實有很多Windows組件你可能都用到不到&#xff0c;不如在安裝系統時就…

Maven之依賴管理

Maven之依賴管理一、Maven依賴管理的核心價值二、依賴的基本配置&#xff08;坐標與范圍&#xff09;2.1 依賴坐標&#xff08;GAV&#xff09;2.2 依賴范圍&#xff08;scope&#xff09;示例&#xff1a;常用依賴范圍配置三、依賴傳遞與沖突解決3.1 依賴傳遞性示例&#xff1…

【Unity實戰100例】Unity資源下載系統開發流程詳解(移動端、PC端 ,局域網控制臺服務)

目錄 一、項目概述 二、服務器開發 1、配置文件設計 1、加載配置 2. 處理客戶端請求 3. 文件下載處理 三、客戶端開發 1、配置管理 1、配置加載與保存 2、下載任務管理 1、任務類設計 2、下載隊列管理 3、核心下載流程 四、UI系統實現 五、部署與測試 1、服務…

[Python] -進階理解7- Python中的內存管理機制簡析

Python(尤其是 CPython)采用自動內存管理機制,核心包括引用計數(Reference Counting)與垃圾回收機制(Garbage Collection),并配合專門的內存池和分配器機制來提升效率與減少碎片。 這套機制隱藏在開發者視線之外,Python 開發者無需手動申請或釋放內存。 二、Python 內…