需求:
有些時候,我們需要連接多個數據庫,但是,在方法調用前并不知道到底是調用哪個。即同時保持多個數據庫的連接,在方法中根據傳入的參數來確定。
下圖的單數據源的調用和多數據源動態調用的流程,可以看出在dao層中需要有一個datasource選擇器,來確定到底是調用哪個數據源。
實現方式
對dao層提供一個公共父類,保持有多個數據源的連接(本人是基于ibatis,即保持多個sqlsessiontemplate)
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
|
/** * created by hzlizhou on 2017/2/6. */ public abstract class multidatasourcedao implements idaosupport { private map<string, sqlsessiontemplate> sqlsessiontemplatemap; private multidatasourceselector multidatasourceselector; public multidatasourcedao(map<string, sqlsessiontemplate> sqlsessiontemplatemap, multidatasourceselector multidatasourceselector) { this .sqlsessiontemplatemap = sqlsessiontemplatemap; this .multidatasourceselector = multidatasourceselector; } public map<string, sqlsessiontemplate> getsqlsessiontemplatemap() { return sqlsessiontemplatemap; } public void setsqlsessiontemplatemap(map<string, sqlsessiontemplate> sqlsessiontemplatemap) { this .sqlsessiontemplatemap = sqlsessiontemplatemap; } //子類通過這個方法動態獲取sqlsessiontemplate protected sqlsessiontemplate getsqlsessiontemplate() { string clustername = multidatasourceselector.getname(); sqlsessiontemplate result = sqlsessiontemplatemap.get(clustername); assert .notnull(result); return result; } } |
multidatasourceselector是一個借口,根據當前的調用環境,返回不不同的參數,根據這個參數就可以確定使用哪一個sqlsessiontemplate,例如我是把當前環境放入到threadlocal中的
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
|
public interface multidatasourceselector { string getname(); } public class dubbocontextdatasourceselector implements multidatasourceselector { private string defaultname; public dubbocontextdatasourceselector(string defaultname) { this .defaultname = defaultname; } @override public string getname() { //dubbocontextholder 是一個保持一個threadlocal的map string res = dubbocontextholder.getcontext().get(dubbocontextconstants.cluster_name); if (res == null ) { res = getdefaultname(); } return res; } public string getdefaultname() { return defaultname; } } |
然后在dao層的中獲取sqlsessiontemplate的時候就是動態了。
動態事務
其實這個都好辦,然后我們就面臨一個稍微復雜一點的問題,那datasource是動態的,事務也就必須是動態了的。而且還對原有的代碼沒有侵入(例如spring中的@transactional 注解),那實現一個類似@transactional的方法吧。名字就叫做@dynamictransactional
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
@documented @target ({method, type}) @retention (runtime) public @interface dynamictransactional { propagation propagation() default propagation.required; class <? extends throwable>[] rollbackfor() default {}; } |
基本思想是在通過aop切面攔截@dynamictransactional注解,調用,然后自己編程實現事務
切面內的核心方法是
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
|
private object invokewithintransaction( final proceedingjoinpoint pjp, final dynamictransactional dynamictransaction) { //創建transactiontemplate final platformtransactionmanager tran = multitransactionmanagerholder.gettransactionmanager(); transactiontemplate transactiontemplate = new transactiontemplate(); transactiontemplate.setpropagationbehavior(dynamictransaction.propagation().value()); transactiontemplate.settransactionmanager(tran); //在事務中執行 return transactiontemplate.execute( new transactioncallback<object>() { @override public object dointransaction(transactionstatus status) { object result = null ; try { result = pjp.proceed(); } catch (throwable throwable) { class <? extends throwable>[] c = dynamictransaction.rollbackfor(); for ( class <? extends throwable> tmp : c) { if (tmp.isassignablefrom(throwable.getclass())) { status.setrollbackonly(); } } } return result; } }); } |
其中multitransactionmanagerholder和上面動態數據源選擇的原理一樣,通過從threadlocal中拿去變量,選擇對應的transactionmanager返回
切面的配置:重點是怎么對指定注解進行切面
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<aop:config> <aop:aspect id= "multitransactionmanageraspect" ref= "multitransactionmanageraop" > <aop:around method= "invokewithintransaction" arg-names= "dynamictransaction" pointcut= "@annotation(dynamictransaction)" /> </aop:aspect> </aop:config> |
當然,這里只是實現了在方法上的@dynamictransactional使用,如果該注解還要對類使用,對所有函數加一個切點,判斷該切點的類上是否有@dynamictransactional注解
注意:由于切面的優先級,如果要實現 方法上的注解優先級高于類上的,還需要一點點小的處理
調用時序圖
自己實現基于abstractroutingdatasource,把多個datasource加入到sqlsessionfactory,和之前的方式一樣,通過threadlocal來確定使用哪個datasource。
關于動態事務,上面是使用切面,自定義標簽,使用transactiontemplate來實現的,如果想更優雅的話,可以仿照datasourcetransactionmanager寫一個,
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:http://www.cnblogs.com/lizo/p/7089263.html?utm_source=tuicool&utm_medium=referral