在做ERP項(xiàng)目的時(shí)候有個(gè)需求是能夠管理和切換賬套,一個(gè)賬套就是一個(gè)數(shù)據(jù)庫,那么就需要實(shí)現(xiàn)數(shù)據(jù)庫的熱切換。網(wǎng)上找了很多資料再結(jié)合項(xiàng)目的具體需求實(shí)現(xiàn)了一個(gè)還算比較好用的數(shù)據(jù)庫熱切換。
創(chuàng)新互聯(lián)建站專注于企業(yè)全網(wǎng)營銷推廣、網(wǎng)站重做改版、隆堯網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5場景定制、商城系統(tǒng)網(wǎng)站開發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、成都外貿(mào)網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為隆堯等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。
原理是首先繼承AbstractRoutingDataSource并實(shí)現(xiàn)determineCurrentLookupKey方法,方法的內(nèi)容為
protected Object determineCurrentLookupKey() { return DataSourceContextHolder.getDataSourceType(); }
這個(gè)方法的功能是再執(zhí)行sql之前spring會(huì)先執(zhí)行這個(gè)方法,并從數(shù)據(jù)源Map中通過這個(gè)方法返回的key去確定要使用的數(shù)據(jù)源。
再新增一個(gè)方法:refreshDataSources
private void refreshDataSources(ListdatabaseList) { if (databaseList != null && databaseList.size() > 0) { BasicDataSource templateDataSource = (BasicDataSource) this.context.getBean("dataSource");//模板數(shù)據(jù)源 Map
這個(gè)方法的功能是將傳入的databaseList(需要熱切換的數(shù)據(jù)庫名稱列表),除了datasource的url需要替換外其他的屬性都通過默認(rèn)配置好的數(shù)據(jù)源中配置的屬性,最后將封裝好的數(shù)據(jù)源集合放入targetDataSources中,然后調(diào)用afterPropertiesSet方法刷新數(shù)據(jù)源,afterPropertiesSet的具體內(nèi)容可以查看源碼。
其中,這段代碼中的context對(duì)象是通過實(shí)現(xiàn)ApplicationContextAware接口的setApplicationContext方法取得的,spring會(huì)自動(dòng)注入applicationContext對(duì)象。
然后就是配置,我們還是按照常規(guī)的spring整合mybatis的配置方法,只是多了一個(gè)數(shù)據(jù)源的配置
其中dynamicDataSource就是上面實(shí)現(xiàn)AbstractRoutingDataSource的類,里面ref的dataSource就是默認(rèn)的數(shù)據(jù)源,其他配置數(shù)據(jù)ref的地方就使用這個(gè)dynamicDataSource,而不是dataSource。
最后就是怎么使用。因?yàn)閐ynamicDataSource已經(jīng)被spring管理起來了,所以,我們只需要在我們會(huì)用到的地方先生成一個(gè)數(shù)據(jù)庫名稱列表,可以從其他數(shù)據(jù)庫中查詢,然后調(diào)用refreshDataSources方法,最后調(diào)用
DataSourceContextHolder.setDataSourceType("你要切換的數(shù)據(jù)庫名稱");
下面是DataSourceContextHolder的代碼
public class DataSourceContextHolder { private static final ThreadLocalcontextHolder = new ThreadLocal (); public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } public static String getDataSourceType() { return contextHolder.get(); } public static void clearDataSourceType() { contextHolder.remove(); } }
到此,一個(gè)完整的實(shí)現(xiàn)數(shù)據(jù)庫熱切換的功能就全部實(shí)現(xiàn)了,有其他特定的需求可以在此基礎(chǔ)上修改。