一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 通過Spring Boot配置動態數據源訪問多個數據庫的實現代碼

通過Spring Boot配置動態數據源訪問多個數據庫的實現代碼

2021-04-08 14:47Elon.Yang Java教程

這篇文章主要介紹了通過Spring Boot配置動態數據源訪問多個數據庫的實現代碼,需要的朋友可以參考下

之前寫過一篇博客《spring+mybatis+mysql搭建分布式數據庫訪問框架》描述如何通過spring+mybatis配置動態數據源訪問多個數據庫。但是之前的方案有一些限制(原博客中也描述了):只適用于數據庫數量不多且固定的情況。針對數據庫動態增加的情況無能為力。

下面講的方案能支持數據庫動態增刪,數量不限。

數據庫環境準備

下面一mysql為例,先在本地建3個數據庫用于測試。需要說明的是本方案不限數據庫數量,支持不同的數據庫部署在不同的服務器上。如圖所示db_project_001、db_project_002、db_project_003。

通過Spring Boot配置動態數據源訪問多個數據庫的實現代碼

搭建java后臺微服務項目

創建一個spring boot的maven項目:

通過Spring Boot配置動態數據源訪問多個數據庫的實現代碼

config:數據源配置管理類。

datasource:自己實現的數據源管理邏輯。

dbmgr:管理了項目編碼與數據庫ip、名稱的映射關系(實際項目中這部分數據保存在redis緩存中,可動態增刪)。

mapper:數據庫訪問接口。

model:映射模型。

rest:微服務對外發布的restful接口,這里用來測試。

application.yml:配置了數據庫的jdbc參數。

詳細的代碼實現

1. 添加數據源配置

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.elon.dds.config;
import javax.sql.datasource;
import org.apache.ibatis.session.sqlsessionfactory;
import org.mybatis.spring.sqlsessionfactorybean;
import org.mybatis.spring.annotation.mapperscan;
import org.springframework.beans.factory.annotation.qualifier;
import org.springframework.boot.autoconfigure.jdbc.datasourcebuilder;
import org.springframework.boot.context.properties.configurationproperties;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import com.elon.dds.datasource.dynamicdatasource;
/**
 * 數據源配置管理。
 *
 * @author elon
 * @version 2018年2月26日
 */
@configuration
@mapperscan(basepackages="com.elon.dds.mapper", value="sqlsessionfactory")
public class datasourceconfig {
 /**
 * 根據配置參數創建數據源。使用派生的子類。
 *
 * @return 數據源
 */
 @bean(name="datasource")
 @configurationproperties(prefix="spring.datasource")
 public datasource getdatasource() {
 datasourcebuilder builder = datasourcebuilder.create();
 builder.type(dynamicdatasource.class);
 return builder.build();
 }
 /**
 * 創建會話工廠。
 *
 * @param datasource 數據源
 * @return 會話工廠
 */
 @bean(name="sqlsessionfactory")
 public sqlsessionfactory getsqlsessionfactory(@qualifier("datasource") datasource datasource) {
 sqlsessionfactorybean bean = new sqlsessionfactorybean();
 bean.setdatasource(datasource);
 try {
  return bean.getobject();
 } catch (exception e) {
  e.printstacktrace();
  return null;
 }
 }
}

2.定義動態數據源

1)  首先增加一個數據庫標識類,用于區分不同的數據庫訪問。

由于我們為不同的project創建了單獨的數據庫,所以使用項目編碼作為數據庫的索引。而微服務支持多線程并發的,采用線程變量。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.elon.dds.datasource;
/**
 * 數據庫標識管理類。用于區分數據源連接的不同數據庫。
 *
 * @author elon
 * @version 2018-02-25
 */
public class dbidentifier {
 /**
 * 用不同的工程編碼來區分數據庫
 */
 private static threadlocal<string> projectcode = new threadlocal<string>();
 public static string getprojectcode() {
 return projectcode.get();
 }
 public static void setprojectcode(string code) {
 projectcode.set(code);
 }
}

2)  從datasource派生了一個dynamicdatasource,在其中實現數據庫連接的動態切換

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import java.lang.reflect.field;
import java.sql.connection;
import java.sql.sqlexception;
import org.apache.logging.log4j.logmanager;
import org.apache.logging.log4j.logger;
import org.apache.tomcat.jdbc.pool.datasource;
import org.apache.tomcat.jdbc.pool.poolproperties;
import com.elon.dds.dbmgr.projectdbmgr;
/**
 * 定義動態數據源派生類。從基礎的datasource派生,動態性自己實現。
 *
 * @author elon
 * @version 2018-02-25
 */
public class dynamicdatasource extends datasource {
 private static logger log = logmanager.getlogger(dynamicdatasource.class);
 /**
 * 改寫本方法是為了在請求不同工程的數據時去連接不同的數據庫。
 */
 @override
 public connection getconnection(){
 string projectcode = dbidentifier.getprojectcode();
 //1、獲取數據源
 datasource dds = ddsholder.instance().getdds(projectcode);
 //2、如果數據源不存在則創建
 if (dds == null) {
  try {
  datasource newdds = initdds(projectcode);
  ddsholder.instance().adddds(projectcode, newdds);
  } catch (illegalargumentexception | illegalaccessexception e) {
  log.error("init data source fail. projectcode:" + projectcode);
  return null;
  }
 }
 dds = ddsholder.instance().getdds(projectcode);
 try {
  return dds.getconnection();
 } catch (sqlexception e) {
  e.printstacktrace();
  return null;
 }
 }
 /**
 * 以當前數據對象作為模板復制一份。
 *
 * @return dds
 * @throws illegalaccessexception
 * @throws illegalargumentexception
 */
 private datasource initdds(string projectcode) throws illegalargumentexception, illegalaccessexception {
 datasource dds = new datasource();
 // 2、復制poolconfiguration的屬性
 poolproperties property = new poolproperties();
 field[] pfields = poolproperties.class.getdeclaredfields();
 for (field f : pfields) {
  f.setaccessible(true);
  object value = f.get(this.getpoolproperties());
  try
  {
  f.set(property, value); 
  }
  catch (exception e)
  {
  log.info("set value fail. attr name:" + f.getname());
  continue;
  }
 }
 dds.setpoolproperties(property);
 // 3、設置數據庫名稱和ip(一般來說,端口和用戶名、密碼都是統一固定的)
 string urlformat = this.geturl();
 string url = string.format(urlformat, projectdbmgr.instance().getdbip(projectcode),
  projectdbmgr.instance().getdbname(projectcode));
 dds.seturl(url);
 return dds;
 }
}

3)  通過ddstimer控制數據連接釋放(超過指定時間未使用的數據源釋放)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package com.elon.dds.datasource;
import org.apache.tomcat.jdbc.pool.datasource;
/**
 * 動態數據源定時器管理。長時間無訪問的數據庫連接關閉。
 *
 * @author elon
 * @version 2018年2月25日
 */
public class ddstimer {
 /**
 * 空閑時間周期。超過這個時長沒有訪問的數據庫連接將被釋放。默認為10分鐘。
 */
 private static long idleperiodtime = 10 * 60 * 1000;
 /**
 * 動態數據源
 */
 private datasource dds;
 /**
 * 上一次訪問的時間
 */
 private long lastusetime;
 public ddstimer(datasource dds) {
 this.dds = dds;
 this.lastusetime = system.currenttimemillis();
 }
 /**
 * 更新最近訪問時間
 */
 public void refreshtime() {
 lastusetime = system.currenttimemillis();
 }
 /**
 * 檢測數據連接是否超時關閉。
 *
 * @return true-已超時關閉; false-未超時
 */
 public boolean checkandclose() {
 if (system.currenttimemillis() - lastusetime > idleperiodtime)
 {
  dds.close();
  return true;
 }
 return false;
 }
 public datasource getdds() {
 return dds;
 }
}

4)      增加ddsholder來管理不同的數據源,提供數據源的添加、查詢功能

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package com.elon.dds.datasource;
import java.util.hashmap;
import java.util.iterator;
import java.util.map;
import java.util.map.entry;
import java.util.timer;
import org.apache.tomcat.jdbc.pool.datasource;
/**
 * 動態數據源管理器。
 *
 * @author elon
 * @version 2018年2月25日
 */
public class ddsholder {
 /**
 * 管理動態數據源列表。<工程編碼,數據源>
 */
 private map<string, ddstimer> ddsmap = new hashmap<string, ddstimer>();
 /**
 * 通過定時任務周期性清除不使用的數據源
 */
 private static timer clearidletask = new timer();
 static {
 clearidletask.schedule(new clearidletimertask(), 5000, 60 * 1000);
 };
 private ddsholder() {
 }
 /*
 * 獲取單例對象
 */
 public static ddsholder instance() {
 return ddsholderbuilder.instance;
 }
 /**
 * 添加動態數據源。
 *
 * @param projectcode 項目編碼
 * @param dds dds
 */
 public synchronized void adddds(string projectcode, datasource dds) {
 ddstimer ddst = new ddstimer(dds);
 ddsmap.put(projectcode, ddst);
 }
 /**
 * 查詢動態數據源
 *
 * @param projectcode 項目編碼
 * @return dds
 */
 public synchronized datasource getdds(string projectcode) {
 if (ddsmap.containskey(projectcode)) {
  ddstimer ddst = ddsmap.get(projectcode);
  ddst.refreshtime();
  return ddst.getdds();
 }
 return null;
 }
 /**
 * 清除超時無人使用的數據源。
 */
 public synchronized void clearidledds() {
 iterator<entry<string, ddstimer>> iter = ddsmap.entryset().iterator();
 for (; iter.hasnext(); ) {
  entry<string, ddstimer> entry = iter.next();
  if (entry.getvalue().checkandclose())
  {
  iter.remove();
  }
 }
 }
 /**
 * 單例構件類
 * @author elon
 * @version 2018年2月26日
 */
 private static class ddsholderbuilder {
 private static ddsholder instance = new ddsholder();
 }
}

5)      定時器任務clearidletimertask用于定時清除空閑的數據源

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.elon.dds.datasource;
import java.util.timertask;
/**
 * 清除空閑連接任務。
 *
 * @author elon
 * @version 2018年2月26日
 */
public class clearidletimertask extends timertask {
 @override
 public void run() {
 ddsholder.instance().clearidledds();
 }
}

3.       管理項目編碼與數據庫ip和名稱的映射關系

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package com.elon.dds.dbmgr;
import java.util.hashmap;
import java.util.map;
/**
 * 項目數據庫管理。提供根據項目編碼查詢數據庫名稱和ip的接口。
 * @author elon
 * @version 2018年2月25日
 */
public class projectdbmgr {
 /**
 * 保存項目編碼與數據名稱的映射關系。這里是硬編碼,實際開發中這個關系數據可以保存到redis緩存中;
 * 新增一個項目或者刪除一個項目只需要更新緩存。到時這個類的接口只需要修改為從緩存拿數據。
 */
 private map<string, string> dbnamemap = new hashmap<string, string>();
 /**
 * 保存項目編碼與數據庫ip的映射關系。
 */
 private map<string, string> dbipmap = new hashmap<string, string>();
 private projectdbmgr() {
 dbnamemap.put("project_001", "db_project_001");
 dbnamemap.put("project_002", "db_project_002");
 dbnamemap.put("project_003", "db_project_003");
 dbipmap.put("project_001", "127.0.0.1");
 dbipmap.put("project_002", "127.0.0.1");
 dbipmap.put("project_003", "127.0.0.1");
 }
 public static projectdbmgr instance() {
 return projectdbmgrbuilder.instance;
 }
 // 實際開發中改為從緩存獲取
 public string getdbname(string projectcode) {
 if (dbnamemap.containskey(projectcode)) {
  return dbnamemap.get(projectcode);
 }
 return "";
 }
 //實際開發中改為從緩存中獲取
 public string getdbip(string projectcode) {
 if (dbipmap.containskey(projectcode)) {
  return dbipmap.get(projectcode);
 }
 return "";
 }
 private static class projectdbmgrbuilder {
 private static projectdbmgr instance = new projectdbmgr();
 }
}

4.       定義數據庫訪問的mapper

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.elon.dds.mapper;
import java.util.list;
import org.apache.ibatis.annotations.mapper;
import org.apache.ibatis.annotations.result;
import org.apache.ibatis.annotations.results;
import org.apache.ibatis.annotations.select;
import com.elon.dds.model.user;
/**
 * mybatis映射接口定義。
 *
 * @author elon
 * @version 2018年2月26日
 */
@mapper
public interface usermapper
{
 /**
 * 查詢所有用戶數據
 * @return 用戶數據列表
 */
 @results(value= {
  @result(property="userid", column="id"),
  @result(property="name", column="name"),
  @result(property="age", column="age")
 })
 @select("select id, name, age from tbl_user")
 list<user> getusers();
}

5.       定義查詢對象模型

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.elon.dds.model;
public class user
{
 private int userid = -1;
 private string name = "";
 private int age = -1;
 @override
 public string tostring()
 {
 return "name:" + name + "|age:" + age;
 }
 public int getuserid()
 {
 return userid;
 }
 public void setuserid(int userid)
 {
 this.userid = userid;
 }
 public string getname()
 {
 return name;
 }
 public void setname(string name)
 {
 this.name = name;
 }
 public int getage()
 {
 return age;
 }
 public void setage(int age)
 {
 this.age = age;
 }
}

6.       定義查詢用戶數據的restful接口

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.elon.dds.rest;
import java.util.list;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.requestmethod;
import org.springframework.web.bind.annotation.requestparam;
import org.springframework.web.bind.annotation.restcontroller;
import com.elon.dds.datasource.dbidentifier;
import com.elon.dds.mapper.usermapper;
import com.elon.dds.model.user;
/**
 * 用戶數據訪問接口。
 *
 * @author elon
 * @version 2018年2月26日
 */
@restcontroller
@requestmapping(value="/user")
public class wsuser {
 @autowired
 private usermapper usermapper;
 /**
 * 查詢項目中所有用戶信息
 *
 * @param projectcode 項目編碼
 * @return 用戶列表
 */
 @requestmapping(value="/v1/users", method=requestmethod.get)
 public list<user> queryuser(@requestparam(value="projectcode", required=true) string projectcode)
 {
 dbidentifier.setprojectcode(projectcode);
 return usermapper.getusers();
 }
}

要求每次查詢都要帶上projectcode參數。

 7.       編寫spring boot app的啟動代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.elon.dds;
import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;
/**
 * hello world!
 *
 */
@springbootapplication
public class app
{
 public static void main( string[] args )
 {
 system.out.println( "hello world!" );
 springapplication.run(app.class, args);
 }
}

8.       在application.yml中配置數據源

其中的數據庫ip和數據庫名稱使用%s。在查詢用戶數據中動態切換。

?
1
2
3
4
5
6
7
8
spring:
 datasource:
 url: jdbc:mysql://%s:3306/%s?useunicode=true&characterencoding=utf-8
 username: root
 password:
 driver-class-name: com.mysql.jdbc.driver
logging:
 config: classpath:log4j2.xml

測試方案

1.       查詢project_001的數據,正常返回 

通過Spring Boot配置動態數據源訪問多個數據庫的實現代碼

2.       查詢project_002的數據,正常返回

通過Spring Boot配置動態數據源訪問多個數據庫的實現代碼

總結

以上所述是小編給大家介紹的通過spring boot配置動態數據源訪問多個數據庫的實現代碼,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對服務器之家網站的支持!

原文鏈接:https://www.cnblogs.com/elon/archive/2018/03/01/8486618.html
 

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 奇米久草| 毛片免费毛片一级jjj毛片 | 亚洲高清毛片一区二区 | 精品国产一区二区三区国产馆 | 国产女主播在线播放一区二区 | 亚洲高清中文字幕一区二区三区 | japonensis日本护士18 | 三年片韩国在线观看 | 久久草福利自拍视频在线观看 | 国产精品久久久久久久久齐齐 | 高清视频免费 | 天天操网 | 日韩在线天堂免费观看 | 精品91一区二区三区 | 国产青草亚洲香蕉精品久久 | 亚洲视频在线免费观看 | 麻豆小视频在线观看 | 农村妇女野外牲交一级毛片 | 桃子视频www | 掀开奶罩边躁狠狠躁软学生 | 亚洲网站在线 | 国产一卡2卡3卡四卡精品网 | 亚洲偷窥图区色 | 日本孕妇与黑人xxxxxx | 久久伊人影视 | 俺去啦最新地址 | 国产亚洲女在线精品 | 国产最强大片免费视频 | 免费观看国产大片资源视频 | 亚洲2卡三卡4卡5卡精品 | 亚洲第五色综合网啪啪 | 996热精品视频在线观看 | 国产亚洲综合精品一区二区三区 | 美女把小内内脱个精光打屁屁 | 欧美视频网址 | 娇妻在床上迎合男人 | 色综合图片 | 情趣内衣在线观看 | 国产成人精品第一区二区 | 欧美3d怪物交videos网站 | www.日日爱 |