關于SpringBoot中的多數據源集成

引言

其實對于分庫分表這塊的場景,目前市場上有很多成熟的開源中間件,eg:MyCAT,Cobar,sharding-JDBC等。?

本文主要是介紹基于springboot的多數據源切換,輕量級的一種集成方案,對于小型的應用可以采用這種方案,我之前在項目中用到是因為簡單,便于擴展以及優化。

應用場景

假設目前我們有以下幾種數據訪問的場景:?
1.一個業務邏輯中對不同的庫進行數據的操作(可能你們系統不存在這種場景,目前都時微服務的架構,每個微服務基本上是對應一個數據庫比較多,其他的需要通過服務來方案。),?
2.訪問分庫分表的場景;后面的文章會單獨介紹下分庫分表的集成。

假設這里,我們以6個庫,每個庫1000張表的例子來介紹),因為隨著業務量的增長,一個庫很難抗住這么大的訪問量。比如說訂單表,我們可以根據userid進行分庫分表。?
分庫策略:userId%6[表的數量];?
分庫分表策略:庫路由[userId/(6*1000)/1000],表路由[?userId/(6*1000)%1000].

集成方案

方案總覽:?
方案是基于springjdbc中提供的api實現,看下下面兩段代碼,是我們的切入點,我們都是圍繞著這2個核心方法進行集成的。

第一段代碼是注冊邏輯,會將defaultTargetDataSource和targetDataSources這兩個數據源對象注冊到resolvedDataSources中。

第二段是具體切換邏輯: 如果數據源為空,那么他就會找我們的默認數據源defaultTargetDataSource,如果設置了數據源,那么他就去讀這個值?Object lookupKey = determineCurrentLookupKey();我們后面會重寫這個方法。

我們會在配置文件中配置主數據源(默認數據源)和其他數據源,然后在應用啟動的時候注冊到spring容器中,分別給defaultTargetDataSource和targetDataSources進行賦值,進而進行Bean注冊。

真正的使用過程中,我們定義注解,通過切面,定位到當前線程是由訪問哪個數據源(維護著一個ThreadLocal的對象),進而調用determineCurrentLookupKey方法,進行數據源的切換。

 1 public void afterPropertiesSet() {
 2         if (this.targetDataSources == null) {
 3             throw new IllegalArgumentException("Property 'targetDataSources' is required");
 4         }
 5         this.resolvedDataSources = new HashMap<Object, DataSource>(this.targetDataSources.size());
 6         for (Map.Entry<Object, Object> entry : this.targetDataSources.entrySet()) {
 7             Object lookupKey = resolveSpecifiedLookupKey(entry.getKey());
 8             DataSource dataSource = resolveSpecifiedDataSource(entry.getValue());
 9             this.resolvedDataSources.put(lookupKey, dataSource);
10         }
11         if (this.defaultTargetDataSource != null) {
12             this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
13         }
14     }   
15 
16 @Override
17 public Connection getConnection() throws SQLException {
18         return determineTargetDataSource().getConnection();
19 }
20 
21 protected DataSource determineTargetDataSource() {
22         Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
23         Object lookupKey = determineCurrentLookupKey();
24         DataSource dataSource = this.resolvedDataSources.get(lookupKey);
25         if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
26             dataSource = this.resolvedDefaultDataSource;
27         }
28         if (dataSource == null) {
29             throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
30         }
31         return dataSource;
32     }

1.動態數據源注冊?注冊默認數據源以及其他額外的數據源; 這里只是復制了核心的幾個方法,具體的大家下載git看代碼。

 1  // 創建DynamicDataSource
 2         GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
 3         beanDefinition.setBeanClass(DyncRouteDataSource.class);
 4         beanDefinition.setSynthetic(true);
 5         MutablePropertyValues mpv = beanDefinition.getPropertyValues();
 6         //默認數據源
 7         mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource); 
 8         //其他數據源
 9         mpv.addPropertyValue("targetDataSources", targetDataSources);
10         //注冊到spring bean容器中
11         registry.registerBeanDefinition("dataSource", beanDefinition);

2.在Application中加載動態數據源,這樣spring容器啟動的時候就會將數據源加載到內存中。

1 @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, MybatisAutoConfiguration.class })
2 @Import(DyncDataSourceRegister.class)
3 @ServletComponentScan                                       
4 @ComponentScan(basePackages = { "com.happy.springboot.api","com.happy.springboot.service"})
5 public class Application {
6     public static void main(String[] args) {
7         SpringApplication.run(Application.class, args);
8     }
9 }

3.數據源切換核心邏輯:創建一個數據源切換的注解,并且基于該注解實現切面邏輯,這里我們通過threadLocal來實現線程間的數據源切換;

 1 import java.lang.annotation.Documented;
 2 import java.lang.annotation.ElementType;
 3 import java.lang.annotation.Retention;
 4 import java.lang.annotation.RetentionPolicy;
 5 import java.lang.annotation.Target;
 6 
 7 
 8 @Target({ ElementType.TYPE, ElementType.METHOD })
 9 @Retention(RetentionPolicy.RUNTIME)
10 @Documented
11 public @interface TargetDataSource {
12     String value();
13     // 是否分庫
14     boolean isSharding() default false;
15     // 獲取分庫策略
16     String strategy() default "";
17 }
18 
19 // 動態數據源上下文
20 public class DyncDataSourceContextHolder {
21 
22     private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();
23 
24     public static List<String> dataSourceNames = new ArrayList<String>();
25 
26     public static void setDataSource(String dataSourceName) {
27         contextHolder.set(dataSourceName);
28     }
29 
30     public static String getDataSource() {
31         return contextHolder.get();
32     }
33 
34     public static void clearDataSource() {
35         contextHolder.remove();
36     }
37 
38     public static boolean containsDataSource(String dataSourceName) {
39         return dataSourceNames.contains(dataSourceName);
40     }
41 }
42 
43 /**
44  * 
45  * @author jasoHsu
46  * 動態數據源在切換,將數據源設置到ThreadLocal對象中
47  */
48 @Component
49 @Order(-10)
50 @Aspect
51 public class DyncDataSourceInterceptor {
52 
53     @Before("@annotation(targetDataSource)")
54     public void changeDataSource(JoinPoint point, TargetDataSource targetDataSource) throws Exception {
55         String dbIndx = null;
56 
57         String targetDataSourceName = targetDataSource.value() + (dbIndx == null ? "" : dbIndx);
58         if (DyncDataSourceContextHolder.containsDataSource(targetDataSourceName)) {
59             DyncDataSourceContextHolder.setDataSource(targetDataSourceName);
60         }
61     }
62 
63     @After("@annotation(targetDataSource)")
64     public void resetDataSource(JoinPoint point, TargetDataSource targetDataSource) {
65         DyncDataSourceContextHolder.clearDataSource();
66     }
67 }
68 
69 //
70 public class DyncRouteDataSource extends AbstractRoutingDataSource {
71 @Override
72     protected Object determineCurrentLookupKey() {
73         return DyncDataSourceContextHolder.getDataSource();
74     }
75     public DataSource findTargetDataSource() {
76         return this.determineTargetDataSource();
77     }
78 }

4.與mybatis集成,其實這一步與前面的基本一致。?
只是將在sessionFactory以及數據管理器中,將動態數據源注冊進去【dynamicRouteDataSource】,而非之前的靜態數據源【getMasterDataSource()】

 1     @Resource
 2     DyncRouteDataSource dynamicRouteDataSource;
 3 
 4     @Bean(name = "sqlSessionFactory")
 5     public SqlSessionFactory getinvestListingSqlSessionFactory() throws Exception {
 6         String configLocation = "classpath:/conf/mybatis/configuration.xml";
 7         String mapperLocation = "classpath*:/com/happy/springboot/service/dao/*/*Mapper.xml";
 8         SqlSessionFactory sqlSessionFactory = createDefaultSqlSessionFactory(dynamicRouteDataSource, configLocation,
 9                 mapperLocation);
10 
11         return sqlSessionFactory;
12     }
13 
14 
15     @Bean(name = "txManager")
16     public PlatformTransactionManager txManager() {
17         return new DataSourceTransactionManager(dynamicRouteDataSource);
18     }

5.核心配置參數:

 1 spring:
 2   dataSource:
 3     happyboot:
 4       driverClassName: com.mysql.jdbc.Driver
 5       url: jdbc:mysql://localhost:3306/happy_springboot?characterEncoding=utf8&useSSL=true
 6       username: root
 7       password: admin
 8     extdsnames: happyboot01,happyboot02
 9     happyboot01:
10       driverClassName: com.mysql.jdbc.Driver
11       url: jdbc:mysql://localhost:3306/happyboot01?characterEncoding=utf8&useSSL=true
12       username: root
13       password: admin 
14     happyboot02:
15       driverClassName: com.mysql.jdbc.Driver
16       url: jdbc:mysql://localhost:3306/happyboot02?characterEncoding=utf8&useSSL=true
17       username: root
18       password: admin    

6.應用代碼

 1 @Service
 2 public class UserExtServiceImpl implements IUserExtService {
 3     @Autowired
 4     private UserInfoMapper userInfoMapper;
 5     @TargetDataSource(value="happyboot01")
 6     @Override
 7     public UserInfo getUserBy(Long id) {
 8         return userInfoMapper.selectByPrimaryKey(id);
 9     }
10 }

本文轉載自:https://blog.csdn.net/xuxian6823091/article/details/81051515

?

轉載于:https://www.cnblogs.com/xiyunjava/p/9317625.html

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

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

相關文章

實現vue2.0響應式的基本思路

注意&#xff0c;這里只是實現思路的還原&#xff0c;對于里面各種細節的實現&#xff0c;比如說數組里面數據的操作的監聽&#xff0c;以及對象嵌套這些細節本實例都不會涉及到&#xff0c;如果想了解更加細節的實現&#xff0c;可以通過閱讀源碼 observer文件夾以及instance文…

HTML標簽類型及特點

一、 概述 HTML&#xff08;Hyper Text Markup Language &#xff09;作為一種標記語言&#xff0c;網頁所有的內容均書寫在標簽內部&#xff0c;標簽是組成Html頁面的基本元素&#xff0c;因此對標簽特性的理解在HTML的學習過程中比較重要。 二、基本分類 HTML中的標簽從閉…

打開頁面

*<a href"javascript:void(0)" title"google" οnclick"window.parent.addTab(, 測試, Admin/UserRole, 100000)">測試444</a>*轉載于:https://www.cnblogs.com/niyl/p/10196528.html

python 大量使用json 存儲數據時,格式化輸出的方式

import json, pprintdic {name: 234, user_name: yan xia ting yu , list: [ds, a, 2], 你好這是鍵: 檐下聽雨}print(dic)pprint.pprint(dic)print(json.dumps(dic))print(json.dumps(dic, indent2))# {name: 234, user_name: yan xia ting yu , list: [ds, a, 2], 你好這是鍵…

vue computed 源碼分析

我們來看看computed的實現。最簡單的一個demo如下&#xff1a; <html> <head><meta http-equiv"Content-Type" content"text/html; charsetutf-8" /> </head> <body> <div id"app"><div name"test&…

軟件開發文檔整理(之)一張示意圖 | 清晰明了

在整個軟件開發周期&#xff0c;開發文檔是必不可少的資料&#xff0c;它們貫穿于整個開發周期&#xff0c;用來評估計劃、規劃進度、項目管理、軟件測試、軟件發布&#xff0c;可以說至關重要。 ??開發文檔必須歸檔&#xff0c;沒有歸檔的文檔作用大打折扣&#xff0c;時效性…

java大數BinInteger

當我們遇到long不行的時候就要考慮這個BinInteger了&#xff0c;因為這是只要你內存夠大&#xff0c;就能輸入很大的數&#xff0c;用這個處理高精度問題&#xff0c;是很容易的一件事&#xff0c;對于我這剛學java的萌新來說&#xff0c;長見識了&#xff0c;確實比C方便 BigI…

HTML頁面提交TABLE

在HTML頁面里&#xff0c;提交一個TABLE需要把TABLE的值傳入變量或json格式&#xff0c;然后submit到服務端的。 思路描述&#xff1a;將table里的值取出&#xff0c;放在json中&#xff0c;賦給一個input&#xff0c;通過一個input來實現table表數據提交到服務器&#xff0c;就…

生成條形碼

https://packagist.org/packages/picqer/php-barcode-generator轉載于:https://www.cnblogs.com/pansidong/p/9334224.html

3.GDScript(1)概覽

GDScript 是上面提到的用于Godot的主要語言。和其他語言相比&#xff0c;它與Godot高度整合&#xff0c;有許多優點&#xff1a; 簡單&#xff0c;優雅&#xff0c;設計上為Lua、Python、Squirrel等語言用戶所熟悉。加載和編譯速度飛快。編輯器集成非常令人愉快&#xff0c;有節…

Web 前端框架分類解讀

Web前端框架可以分為兩類&#xff1a; JS的類庫框架 JQuery.JS Angular.JS&#xff08;模型&#xff0c; scope作用域&#xff0c;controller&#xff0c;依賴注入&#xff0c;MVVM&#xff09;&#xff1a;前端MVC Vue.JS&#xff08;MVVM&#xff09;***** Reat.JS &…

async / await對異步的處理

雖然co是社區里面的優秀異步解決方案&#xff0c;但是并不是語言標準&#xff0c;只是一個過渡方案。ES7語言層面提供async / await去解決語言層面的難題。目前async / await 在 IE edge中已經可以直接使用了&#xff0c;但是chrome和Node.js還沒有支持。幸運的是&#xff0c;b…

《SQL Server 2008從入門到精通》--20180717

目錄 1.觸發器1.1.DDL觸發器1.2.DML觸發器1.3.創建觸發器1.3.1.創建DML觸發器1.3.2.創建DDL觸發器1.3.3.嵌套觸發器1.3.4.遞歸觸發器1.4.管理觸發器1.觸發器 觸發器是一種特殊的存儲過程&#xff0c;與表緊密關聯。 1.1.DDL觸發器 當服務器或數據庫中發生數據定義語言&#xff…

主成分分析原理解釋(能力工場小馬哥)

主成分分析&#xff08;Principal components analysis&#xff09;-最大方差解釋 在這一篇之前的內容是《Factor Analysis》&#xff0c;由于非常理論&#xff0c;打算學完整個課程后再寫。在寫這篇之前&#xff0c;我閱讀了PCA、SVD和LDA。這幾個模型相近&#xff0c;卻都有自…

div/div作用

<div></div>主要是用來設置涵蓋一個區塊為主&#xff0c;所謂的區塊是包含一行以上的數據&#xff0c;所以在<div></div>的開始之前與結束后&#xff0c;瀏覽都會自動換行&#xff0c;所以夾在<div></div>間的數據&#xff0c;自然會與其前…

路由懶加載優化

首先得需要插件支持&#xff1a;syntax-dynamic-import import Vue from vue import VueRouter from vue-router /*import First from /components/First import Second from /components/Second*/Vue.use(VueRouter) //方案1 const first ()>import(/* webpackChunkName: &…

vue全面介紹--全家桶、項目實例

簡介 “簡單卻不失優雅&#xff0c;小巧而不乏大匠”。 2016年最火的前端框架當屬Vue.js了&#xff0c;很多使用過vue的程序員這樣評價它&#xff0c;“vue.js兼具angular.js和react.js的優點&#xff0c;并剔除了它們的缺點”。授予了這么高的評價的vue.js&#xff0c;也是開…

關于Keychain

1、Keychain 淺析 2、iOS的密碼管理系統 Keychain的介紹和使用 3.iOS開發中&#xff0c;唯一標識的解決方案之keyChainUUID 轉載于:https://www.cnblogs.com/KiVen2015/p/9341224.html

算法群模擬面試記錄

第一場&#xff1a;2018年12月30日&#xff08;周日&#xff09;&#xff0c;北京時間早上五點。 寫在最前面&#xff1a;好不容易五點爬了起來圍觀mock&#xff0c;結果早上周賽睡過去了&#xff0c;唉。orz。 面試官&#xff1a;wisdompeak&#xff0c;同學&#xff1a;littl…

js對象排序

Object.keys(objs).sort()可以獲取到排好序的keysvar objs {f: {id: 2,name: 2}, a: {id: 3,name: 3}, c: {id: 1,name: 1} }; // 自定義排序規則&#xff0c;按對象的id排序 var sortedObjKeys Object.keys(objs).sort(function(a, b) {return objs[b].id - objs[a].id; });…