1. 程式人生 > >Spring Boot配置動態資料來源訪問N個數據庫,支援資料庫動態增刪,數量不限

Spring Boot配置動態資料來源訪問N個數據庫,支援資料庫動態增刪,數量不限

方案能支援資料庫動態增刪,數量不限。

資料庫環境準備

下面以Mysql為例,先在本地建3個數據庫用於測試。需要說明的是本方案不限資料庫數量,支援不同的資料庫部署在不同的伺服器上。如圖所示db_project_001、db_project_002、db_project_003。

 

搭建Java後臺微服務專案

建立一個Spring Boot的maven專案:

 

config:資料來源配置。

datasource:自己實現的動態資料來源相關類。

dbmgr:管理專案編碼與資料庫IP、名稱的對映關係(實際專案中這部分資料儲存在redis快取中,可動態增刪)。

mapper:mybatis的資料庫訪問介面。

model:對映模型。

rest:微服務對外發布的restful介面,這裡用來測試。

application.yml:配置資料庫JDBC引數。

詳細的程式碼實現

1. 資料來源配置管理類(DataSourceConfig.java)

複製程式碼
 1 package com.elon.dds.config;
 2 
 3 import javax.sql.DataSource;
 4 
 5 import org.apache.ibatis.session.SqlSessionFactory;
 6 import org.mybatis.spring.SqlSessionFactoryBean;
 7 import org.mybatis.spring.annotation.MapperScan;
8 import org.springframework.beans.factory.annotation.Qualifier; 9 import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder; 10 import org.springframework.boot.context.properties.ConfigurationProperties; 11 import org.springframework.context.annotation.Bean; 12 import org.springframework.context.annotation.Configuration;
13 14 import com.elon.dds.datasource.DynamicDataSource; 15 16 /** 17 * 資料來源配置管理。 18 * 19 * @author elon 20 * @version 2018年2月26日 21 */ 22 @Configuration 23 @MapperScan(basePackages="com.elon.dds.mapper", value="sqlSessionFactory") 24 public class DataSourceConfig { 25 26 /** 27 * 根據配置引數建立資料來源。使用派生的子類。 28 * 29 * @return 資料來源 30 */ 31 @Bean(name="dataSource") 32 @ConfigurationProperties(prefix="spring.datasource") 33 public DataSource getDataSource() { 34 DataSourceBuilder builder = DataSourceBuilder.create(); 35 builder.type(DynamicDataSource.class); 36 return builder.build(); 37 } 38 39 /** 40 * 建立會話工廠。 41 * 42 * @param dataSource 資料來源 43 * @return 會話工廠 44 */ 45 @Bean(name="sqlSessionFactory") 46 public SqlSessionFactory getSqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) { 47 SqlSessionFactoryBean bean = new SqlSessionFactoryBean(); 48 bean.setDataSource(dataSource); 49 50 try { 51 return bean.getObject(); 52 } catch (Exception e) { 53 e.printStackTrace(); 54 return null; 55 } 56 } 57 }
複製程式碼

2.  定義動態資料來源

1)  首先增加一個數據庫標識類,用於區分不同的資料庫(DBIdentifier.java)

由於我們為不同的project建立了單獨的資料庫,所以使用專案編碼作為資料庫的索引。而微服務支援多執行緒併發的,採用執行緒變數。

複製程式碼
 1 package com.elon.dds.datasource;
 2 
 3 /**
 4  * 資料庫標識管理類。用於區分資料來源連線的不同資料庫。
 5  * 
 6  * @author elon
 7  * @version 2018-02-25
 8  */
 9 public class DBIdentifier {
10     
11     /**
12      * 用不同的工程編碼來區分資料庫
13      */
14     private static ThreadLocal<String> projectCode = new ThreadLocal<String>();
15 
16     public static String getProjectCode() {
17         return projectCode.get();
18     }
19 
20     public static void setProjectCode(String code) {
21         projectCode.set(code);
22     }
23 }
複製程式碼

2)  從DataSource派生了一個DynamicDataSource,在其中實現資料庫連線的動態切換(DynamicDataSource.java)

複製程式碼
 1 package com.elon.dds.datasource;
 2 
 3 import java.lang.reflect.Field;
 4 import java.sql.Connection;
 5 import java.sql.SQLException;
 6 
 7 import org.apache.logging.log4j.LogManager;
 8 import org.apache.logging.log4j.Logger;
 9 import org.apache.tomcat.jdbc.pool.DataSource;
10 import org.apache.tomcat.jdbc.pool.PoolProperties;
11 
12 import com.elon.dds.dbmgr.ProjectDBMgr;
13 
14 /**
15  * 定義動態資料來源派生類。從基礎的DataSource派生,動態性自己實現。
16  * 
17  * @author elon
18  * @version 2018-02-25
19  */
20 public class DynamicDataSource extends DataSource {
21     
22     private static Logger log = LogManager.getLogger(DynamicDataSource.class);
23     
24     /**
25      * 改寫本方法是為了在請求不同工程的資料時去連線不同的資料庫。
26      */
27     @Override
28     public Connection getConnection(){
29         
30         String projectCode = DBIdentifier.getProjectCode();
31         
32         //1、獲取資料來源
33         DataSource dds = DDSHolder.instance().getDDS(projectCode);
34         
35         //2、如果資料來源不存在則建立
36         if (dds == null) {
37             try {
38                 DataSource newDDS = initDDS(projectCode);
39                 DDSHolder.instance().addDDS(projectCode, newDDS);
40             } catch (IllegalArgumentException | IllegalAccessException e) {
41                 log.error("Init data source fail. projectCode:" + projectCode);
42                 return null;
43             }
44         }
45         
46         dds = DDSHolder.instance().getDDS(projectCode);
47         try {
48             return dds.getConnection();
49         } catch (SQLException e) {
50             e.printStackTrace();
51             return null;
52         }
53     }
54     
55     /**
56      * 以當前資料物件作為模板複製一份。
57      * 
58      * @return dds
59      * @throws IllegalAccessException 
60      * @throws IllegalArgumentException 
61      */
62     private DataSource initDDS(String projectCode) throws IllegalArgumentException, IllegalAccessException {
63         
64         DataSource dds = new DataSource();
65         
66         // 2、複製PoolConfiguration的屬性
67         PoolProperties property = new PoolProperties();
68         Field[] pfields = PoolProperties.class.getDeclaredFields();
69         for (Field f : pfields) {
70             f.setAccessible(true);
71             Object value = f.get(this.getPoolProperties());
72             
73             try
74             {
75                 f.set(property, value);                
76             }
77             catch (Exception e)
78             {
79                 //有一些static final的屬性不能修改。忽略。
80                 log.info("Set value fail. attr name:" + f.getName());
81                 continue;
82             }
83         }
84         dds.setPoolProperties(property);
85 
86         // 3、設定資料庫名稱和IP(一般來說,埠和使用者名稱、密碼都是統一固定的)
87         String urlFormat = this.getUrl();
88         String url = String.format(urlFormat, ProjectDBMgr.instance().getDBIP(projectCode), 
89                 ProjectDBMgr.instance().getDBName(projectCode));
90         dds.setUrl(url);
91 
92         return dds;
93     }
94 }
複製程式碼

3)  通過DDSTimer控制資料連線釋放(DDSTimer.java)

複製程式碼
 1 package com.elon.dds.datasource;
 2 
 3 import org.apache.tomcat.jdbc.pool.DataSource;
 4 
 5 /**
 6  * 動態資料來源定時器管理。長時間無訪問的資料庫連線關閉。
 7  * 
 8  * @author elon
 9  * @version 2018年2月25日
10  */
11 public class DDSTimer {
12     
13     /**
14      * 空閒時間週期。超過這個時長沒有訪問的資料庫連線將被釋放。預設為10分鐘。
15      */
16     private static long idlePeriodTime = 10 * 60 * 1000;
17     
18     /**
19      * 動態資料來源
20      */
21     private DataSource dds;
22     
23     /**
24      * 上一次訪問的時間
25      */
26     private long lastUseTime;
27     
28     public DDSTimer(DataSource dds) {
29         this.dds = dds;
30         this.lastUseTime = System.currentTimeMillis();
31     }
32     
33     /**
34      * 更新最近訪問時間
35      */
36     public void refreshTime() {
37         lastUseTime = System.currentTimeMillis();
38     }
39     
40     /**
41      * 檢測資料連線是否超時關閉。
42      * 
43      * @return true-已超時關閉; false-未超時
44      */
45     public boolean checkAndClose() {
46         
47         if (System.currentTimeMillis() - lastUseTime > idlePeriodTime)
48         {
49             dds.close();
50             return true;
51         }
52         
53         return false;
54     }
55 
56     public DataSource getDds() {
57         return dds;
58     }
59 }
複製程式碼

4)  通過DDSHolder來管理不同的資料來源,提供資料來源的新增、查詢功能(DDSHolder.java)

複製程式碼
 1 package com.elon.dds.datasource;
 2 
 3 import java.util.HashMap;
 4 import java.util.Iterator;
 5 import java.util.Map;
 6 import java.util.Map.Entry;
 7 import java.util.Timer;
 8 
 9 import org.apache.tomcat.jdbc.pool.DataSource;
10 
11 /**
12  * 動態資料來源管理器。
13  * 
14  * @author elon
15  * @version 2018年2月25日
16  */
17 public class DDSHolder {
18     
19     /**
20      * 管理動態資料來源列表。<工程編碼,資料來源>
21      */
22     private Map<String, DDSTimer> ddsMap = new HashMap<String, DDSTimer>();
23 
24     /**
25      * 通過定時任務週期性清除不使用的資料來源
26      */
27     private static Timer clearIdleTask = new Timer();
28     static {
29         clearIdleTask.schedule(new ClearIdleTimerTask(), 5000, 60 * 1000);
30     };
31     
32     private DDSHolder() {
33         
34     }
35     
36     /*
37      * 獲取單例物件
38      */
39     public static DDSHolder instance() {
40         return DDSHolderBuilder.instance;
41     }
42     
43     /**
44      * 新增動態資料來源。
45      * 
46      * @param projectCode 專案編碼 
47      * @param dds dds
48      */
49     public synchronized void addDDS(String projectCode, DataSource dds) {
50         
51         DDSTimer ddst = new DDSTimer(dds);
52         ddsMap.put(projectCode, ddst);
53     }
54     
55     /**
56      * 查詢動態資料來源
57      * 
58      * @param projectCode 專案編碼
59      * @return dds
60      */
61     public synchronized DataSource getDDS(String projectCode) {
62         
63         if (ddsMap.containsKey(projectCode)) {
64             DDSTimer ddst = ddsMap.get(projectCode);
65             ddst.refreshTime();
66             return ddst.getDds();
67         }
68         
69         return null;
70     }
71     
72     /**
73      * 清除超時無人使用的資料來源。
74      */
75     public synchronized void clearIdleDDS() {
76         
77         Iterator<Entry<String, DDSTimer>> iter = ddsMap.entrySet().iterator();
78         for (; iter.hasNext(); ) {
79             
80             Entry<String, DDSTimer> entry = iter.next();
81             if (entry.getValue().checkAndClose())
82             {
83                 iter.remove();
84             }
85         }
86     }
87     
88     /**
89      * 單例構件類
90      * @author elon
91      * @version 2018年2月26日
92      */
93     private static class DDSHolderBuilder {
94         private static DDSHolder instance = new DDSHolder();
95     }
96 }
複製程式碼

5)  定時器任務ClearIdleTimerTask用於定時清除空閒的資料來源(ClearIdleTimerTask.java)

複製程式碼
 1 package com.elon.dds.datasource;
 2 
 3 import java.util.TimerTask;
 4 
 5 /**
 6  * 清除空閒連線任務。
 7  * 
 8  * @author elon
 9  * @version 2018年2月26日
10  */
11 public class ClearIdleTimerTask extends TimerTask {
12     
13     @Override
14     public void run() {
15         DDSHolder.instance().clearIdleDDS();

            
           

相關推薦

Spring Boot配置動態資料來源訪問N個數支援資料庫動態增刪數量

方案能支援資料庫動態增刪,數量不限。資料庫環境準備下面以Mysql為例,先在本地建3個數據庫用於測試。需要說明的是本方案不限資料庫數量,支援不同的資料庫部署在不同的伺服器上。如圖所示db_project_001、db_project_002、db_project_003。 搭

MyBatis 配置資料來源實現多個數動態切換

1.配置properties路徑 我的配置檔案   2.配置mybatis資料來源 配置 第二套資料來源 3 動態資料來源的配置 兩個key分別引入了 兩套資料來源,預設使用jsdx_telecom 4.配置SqlSessionFa

spring boot 配置資料來源

1.application.yml配置 server: port: 8088 spring: http: multipart: max-file-size: 50Mb max-request-size: 50Mb enabl

spring boot配置druid資料來源和監控配置

直接上程式碼: 一.pom.xml中新增依賴 1 <dependency> 2 <groupId>com.github.drtrang</groupId> 3 <artifactId>druid-s

Spring Boot配置資料來源並實現Druid自動切換

SpringBoot多資料來源切換,先上配置檔案: 1.pom: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0"

Spring Boot配置跨域訪問策略

1. 引言 我們在開發過程中通常因為不同應用之間的介面呼叫或者應用之間介面整合時經常會遇到跨域問題, 導致無法正常獲取介面資料,那麼什麼是跨域? 跨域的解決辦法是什麼? 下面結合Spring Boot相關的專案應用實戰, 詳解說明跨域的解決方案。 1.1

spring boot 配置資料來源和多controller問題

1 配置資料來源參考這篇部落格 使用的是jpa hibernate 2 程式碼目錄問題 遇到一些奇奇怪怪的啟動問題 1 建立了另一個controller,結果無法完成對映,requestMapping失敗,報404 2 說是找不到userDao 的bean,無法自動

Spring Boot 配置Druid資料來源

Druid是阿里巴巴開源平臺上的一個專案,整個專案由資料庫連線池、外掛框架和SQL解析器組成。 新增Druid資料來源 要使用Druid資料來源,我們需要在 application.properties 下新增配置資訊 #資料庫訪問配置 #mysql

Spring Boot中使用Flyway來管理數版本

con 數據庫更新 多人 test 修改 utf8 number jdbc ima   flyway是一個開源的數據庫遷移工具。類似於數據庫的版本控制工具。flyway的數據庫修改文件默認放在resource下的db.migration文件夾中,以V{version_num

Laravel配置控制器內切換多個數(或任意切換多資料庫

1. 知識點:     Laravel預設資料庫是取.env和datebase.php中mysql填寫的資料庫。     如果需要切換多個數據庫,就需要填寫新的資料庫配置。     新的資料庫不需要.env檔案依賴。 &

Hibernate 連線訪問個數(含訪問不同資料庫的相同表)

利用訪問不同資料庫中的不同表或不同資料庫中的相同表。 本人在開發過程中的解決方案,希望大家交流。一般用myEclipse工具會自動生成Hibernate的相關檔案,大致有下面幾類: (1)資料庫配置檔

Mybatis 對映結果集為Map集合支援資料庫動態加列

1.需求場景 報表統計需求,資料庫頻繁調整返回資料欄位。 2.專案環境 Spring spring mvc mybatis 3.實現方法 (1)mapper.xml配置檔案 <select id="getDataList" parameterType="ma

Spring boot 配置 mybatis xml和動態SQL

star too conn -- 動態 div nec output out 1.pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns="

Spring boot 配置https 實現java通過https介面訪問

      近來公司需要搭建一個https的伺服器來除錯介面(伺服器用的spring boot框架),剛開始接觸就是一頓百度,最後發現網際網路認可的https安全連結的證書需要去CA認證機構申請,由於是除錯階段就採用了java的keytool工具來生成金鑰檔案

Spring boot配置資料來源報錯之java.sql.SQLException和java.sql.SQLNonTransientConnectionException

週末想自己用Spring boot整合Mybatis寫點簡單的demo,沒想到出了點以前沒遇到過的bug,特記錄一下 java.sql.SQLException: The server time zone value 'Öйú±ê׼ʱ¼ä' is unrecognized or repre

Spring Boot +Mybatis 多資料來源配置和使用

1、在application.properties中新增資料庫連線配置     mybatis.type-aliases-package=com.yc.edusys.bean     mybatis.mapper-locations=cla

Spring Boot Jpa多資料來源配置

前言隨著業務量發展,我們通常會進行資料庫拆分或是引入其他資料庫,從而我們需要配置多個數據源,如:user一個庫,business一個庫。那麼接下來我們就要考慮怎麼去在spring boot中實現多個數據源的配置。 ××× 實現建表首先是建表語句,我們要建立兩個資料庫,並各庫內新建一張表user表mysql

Spring Boot使用多資料來源配置JdbcTemplate.md

多資料來源配置 建立一個Spring配置類,定義兩個DataSource用來讀取application.properties中的不同配置。如下例子中,主資料來源配置為spring.datasource.primary開頭的配置,第二資

Spring Boot使用單資料來源配置JdbcTemplate.md

在單資料來源的情況下,Spring Boot的配置非常簡單,只需要在application.properties檔案中配置連線引數即可。 資料來源配置 首先,為了連線資料庫需要引入jdbc支援,在pom.xml中引

Spring boot配置多個Redis資料來源操作例項

原文:https://www.jianshu.com/p/c79b65b253fa     Spring boot配置多個Redis資料來源操作例項 在SpringBoot是專案中整合了兩個Redis的操作例項,可以增加多個; 一般在一個微服務生態群中是不會出現多