由于公司業務劃分了多個數據庫,開發一個項目會同事調用多個庫,經過學習我們采用了注解+aop的方式實現的
1.首先定義一個注解類
1
2
3
4
5
|
@retention (retentionpolicy.runtime) @target (elementtype.method) public @interface targetdatasource { string value(); //此處接收的是數據源的名稱 } |
2.然后建一個配置類,這個在項目啟動時會加載數據源,一開始采用了hikaricp,查資料說是最快性能最好的,然后又發現了阿里的druid,這個功能比較全面,而且性能也還可以,最主要他還有監控功能,具體實現看如下代碼
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
|
package com.example.demo.datasource; import com.alibaba.druid.pool.druiddatasource; import com.alibaba.druid.support.http.statviewservlet; import com.alibaba.druid.support.http.webstatfilter; import com.example.demo.datasource.dynamicdatasource; import com.zaxxer.hikari.hikariconfig; import com.zaxxer.hikari.hikaridatasource; import lombok.extern.slf4j.slf4j; import org.mybatis.spring.annotation.mapperscan; import org.springframework.beans.factory.annotation.autowired; import org.springframework.beans.factory.annotation.qualifier; import org.springframework.beans.factory.annotation.value; import org.springframework.boot.web.servlet.filterregistrationbean; import org.springframework.boot.web.servlet.servletregistrationbean; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.configuration; import org.springframework.jdbc.datasource.datasourcetransactionmanager; import org.springframework.scheduling.annotation.enablescheduling; import org.springframework.scheduling.annotation.scheduled; import org.springframework.transaction.platformtransactionmanager; import org.w3c.dom.nodelist; import org.w3c.dom.document; import org.w3c.dom.element; import org.w3c.dom.node; import javax.servlet.annotation.webinitparam; import javax.servlet.annotation.webservlet; import javax.sql.datasource; import javax.xml.parsers.documentbuilder; import javax.xml.parsers.documentbuilderfactory; import java.lang.reflect.field; import java.lang.reflect.method; import java.util.hashmap; import java.util.map; import java.io.file; import com.alibaba.druid.support.http.statviewservlet; /** * author: wangchao * version: * date: 2017/9/11 * description:數據源配置 * modification history: * date author version description * -------------------------------------------------------------- * why & what is modified: */ @configuration @enablescheduling public class datasourceconfig { /*@autowired private dbproperties properties;*/ @value("${datasource.filepath}") private string filepath;//數據源配置 @bean(name = "datasource") public datasource datasource() { //按照目標數據源名稱和目標數據源對象的映射存放在map中 map<object, object> targetdatasources = new hashmap<>(); //查找xml數據連接字符串 targetdatasources=getdatamap(filepath); //動態獲取dbproperties類申明的屬性 /*field[] fields=properties.getclass().getdeclaredfields(); for(int i=0;i<fields.length;i++) { targetdatasources.put(fields[i].getname(), getfieldvaluebyname(fields[i].getname(),properties)); }*/ //采用是想abstractroutingdatasource的對象包裝多數據源 dynamicdatasource datasource = new dynamicdatasource(); datasource.settargetdatasources(targetdatasources); //設置默認的數據源,當拿不到數據源時,使用此配置 //datasource.setdefaulttargetdatasource(properties.getuzaitravel()); return datasource; } @bean public platformtransactionmanager txmanager() { return new datasourcetransactionmanager(datasource()); } /** *獲取數據源集合 */ private map<object, object> getdatamap(string fiepath) { try { map<object, object> targetdatasources = new hashmap<>(); file xmlfile = new file(fiepath); documentbuilderfactory builderfactory = documentbuilderfactory.newinstance(); documentbuilder builder = builderfactory.newdocumentbuilder(); document doc = builder.parse(xmlfile); doc.getdocumentelement().normalize(); system.out.println("root element: " + doc.getdocumentelement().getnodename()); nodelist nlist = doc.getelementsbytagname("db"); for(int i = 0 ; i<nlist.getlength();i++) { node node = nlist.item(i); element ele = (element)node; /*hikariconfig config = new hikariconfig(); config.setdriverclassname(ele.getelementsbytagname("driver-class").item(0).gettextcontent()); config.setjdbcurl(ele.getelementsbytagname("jdbc-url").item(0).gettextcontent()); config.setusername(ele.getelementsbytagname("username").item(0).gettextcontent()); config.setpassword(ele.getelementsbytagname("password").item(0).gettextcontent()); //config.adddatasourceproperty("password", ele.getelementsbytagname("password").item(0).gettextcontent()); hikaridatasource datasource = new hikaridatasource(config);*/ druiddatasource datasource = new druiddatasource(); datasource.setdriverclassname(ele.getelementsbytagname("driver-class").item(0).gettextcontent()); datasource.setusername(ele.getelementsbytagname("username").item(0).gettextcontent()); datasource.setpassword(ele.getelementsbytagname("password").item(0).gettextcontent()); datasource.seturl(ele.getelementsbytagname("jdbc-url").item(0).gettextcontent()); datasource.setinitialsize(5); datasource.setminidle(1); datasource.setmaxactive(10);// 啟用監控統計功能 datasource.setfilters("stat");//設置是否顯示sql語句 targetdatasources.put(ele.getelementsbytagname("databasename").item(0).gettextcontent(), datasource); } return targetdatasources; } catch (exception ex) { return null; } } //訪問的ip @value("${druid.ip}") private string ip; //登錄名 @value("${druid.druidlgoinname}") private string druidlgoinname; //密碼 @value("${druid.druidlgoinpassword}") private string druidlgoinpassword; @bean public servletregistrationbean druidstatviewservle() { //org.springframework.boot.context.embedded.servletregistrationbean提供類的進行注冊. servletregistrationbean servletregistrationbean = new servletregistrationbean(new statviewservlet(), "/druid/*"); //添加初始化參數:initparams //白名單: servletregistrationbean.addinitparameter("allow",ip); //ip黑名單 (存在共同時,deny優先于allow) : 如果滿足deny的話提示:sorry, you are not permitted to view this page. // servletregistrationbean.addinitparameter("deny", "192.168.1.73"); //登錄查看信息的賬號密碼. servletregistrationbean.addinitparameter("loginusername",druidlgoinname); servletregistrationbean.addinitparameter("loginpassword",druidlgoinpassword); //是否能夠重置數據. servletregistrationbean.addinitparameter("resetenable","false"); return servletregistrationbean; } /** * 注冊一個:filterregistrationbean * @return */ @bean public filterregistrationbean druidstatfilter2(){ filterregistrationbean filterregistrationbean = new filterregistrationbean( new webstatfilter()); //添加過濾規則. filterregistrationbean.addurlpatterns( "/*" ); //添加不需要忽略的格式信息. filterregistrationbean.addinitparameter( "exclusions" , "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*" ); return filterregistrationbean; } } |
3.動態數據源,從之前已加載的數據源中選取,dynamicdatasource和dynamicdatasourceholder配合使用
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
|
public class dynamicdatasource extends abstractroutingdatasource{ //數據源路由,此方用于產生要選取的數據源邏輯名稱 @override protected object determinecurrentlookupkey() { //從共享線程中獲取數據源名稱 return dynamicdatasourceholder.getdatasource(); } } public class dynamicdatasourceholder { /** * 本地線程共享對象 */ private static final threadlocal<string> thread_local = new threadlocal<>(); public static void putdatasource(string name) { thread_local.set(name); } public static string getdatasource() { return thread_local.get(); } public static void removedatasource() { thread_local.remove(); } } |
4.就是使用aop,在dao層切換數據源
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
|
@component @aspect public class datasourceaspect { //切換放在mapper接口的方法上,所以這里要配置aop切面的切入點 @pointcut ( "execution( * com.example.demo.dao.*.*(..))" ) public void datasourcepointcut() { } @before ( "datasourcepointcut()" ) public void before(joinpoint joinpoint) { object target = joinpoint.gettarget(); string method = joinpoint.getsignature().getname(); class <?>[] clazz = target.getclass().getinterfaces(); class <?>[] parametertypes = ((methodsignature) joinpoint.getsignature()).getmethod().getparametertypes(); try { method m = clazz[ 0 ].getmethod(method, parametertypes); //如果方法上存在切換數據源的注解,則根據注解內容進行數據源切換 if (m != null && m.isannotationpresent(targetdatasource. class )) { targetdatasource data = m.getannotation(targetdatasource. class ); string datasourcename = data.value(); dynamicdatasourceholder.putdatasource(datasourcename); } else { } } catch (exception e) { } } //執行完切面后,將線程共享中的數據源名稱清空 @after ( "datasourcepointcut()" ) public void after(joinpoint joinpoint){ dynamicdatasourceholder.removedatasource(); } } |
數據連接都配置在xml里面
xml路徑在配置文件里面配置,這樣適用讀寫分離和多個不同的數據源,而且多個項目可以共用這一個配置
最后引用注解,需要注意的是注解的數據庫名稱和xml里面databasename節點是一一對應的,可以隨便自定義,比如讀寫是一個數據庫名字,這時候就可以定義成pringtest_r表示讀庫
至此多數據源就配置完成,至于阿里的druid下次再分享,代碼都貼出來,如果大家感覺還有哪些不足的地方,歡迎指正。
以上這篇spring boot+mybatis 多數據源切換(實例講解)就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持服務器之家。
原文鏈接:http://www.cnblogs.com/ok123/archive/2017/09/14/7523106.html