詳解基于spring多數(shù)據(jù)源動態(tài)調(diào)用及其事務(wù)處理
需求:
有些時候,我們需要連接多個數(shù)據(jù)庫,但是,在方法調(diào)用前并不知道到底是調(diào)用哪個。即同時保持多個數(shù)據(jù)庫的連接,在方法中根據(jù)傳入的參數(shù)來確定。
下圖的單數(shù)據(jù)源的調(diào)用和多數(shù)據(jù)源動態(tài)調(diào)用的流程,可以看出在Dao層中需要有一個DataSource選擇器,來確定到底是調(diào)用哪個數(shù)據(jù)源。
實現(xiàn)方式
對Dao層提供一個公共父類,保持有多個數(shù)據(jù)源的連接(本人是基于iBatis,即保持多個SQLSessionTemplate)
/** * 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; } //子類通過這個方法動態(tài)獲取SqlSessionTemplate protected SqlSessionTemplate getSqlSessionTemplate() { String clusterName = multiDataSourceSelector.getName(); SqlSessionTemplate result = sqlSessionTemplateMap.get(clusterName); Assert.notNull(result); return result; } }
MultiDataSourceSelector是一個借口,根據(jù)當(dāng)前的調(diào)用環(huán)境,返回不不同的參數(shù),根據(jù)這個參數(shù)就可以確定使用哪一個SQLSessionTemplate,例如我是把當(dāng)前環(huán)境放入到ThreadLocal中的
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的時候就是動態(tài)了。
動態(tài)事務(wù)
其實這個都好辦,然后我們就面臨一個稍微復(fù)雜一點(diǎn)的問題,那DataSource是動態(tài)的,事務(wù)也就必須是動態(tài)了的。而且還對原有的代碼沒有侵入(例如Spring中的@Transactional 注解),那實現(xiàn)一個類似@Transactional的方法吧。名字就叫做@DynamicTransactional
@Documented @Target({METHOD, TYPE}) @Retention(RUNTIME) public @interface DynamicTransactional { Propagation propagation() default Propagation.REQUIRED; Class<? extends Throwable>[] rollbackFor() default {}; }
基本思想是在通過AOP切面攔截@DynamicTransactional注解,調(diào)用,然后自己編程實現(xiàn)事務(wù)
切面內(nèi)的核心方法是
private Object invokeWithinTransaction(final ProceedingJoinPoint pjp, final DynamicTransactional dynamicTransaction) { //創(chuàng)建TransactionTemplate final PlatformTransactionManager tran = multiTransactionManagerHolder.getTransactionManager(); TransactionTemplate transactionTemplate = new TransactionTemplate(); transactionTemplate.setPropagationBehavior(dynamicTransaction.propagation().value()); transactionTemplate.setTransactionManager(tran); //在事務(wù)中執(zhí)行 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和上面動態(tài)數(shù)據(jù)源選擇的原理一樣,通過從ThreadLocal中拿去變量,選擇對應(yīng)的TransactionManager返回
切面的配置:重點(diǎn)是怎么對指定注解進(jìn)行切面
<aop:config> <aop:aspect id="multiTransactionManagerAspect" ref="multiTransactionManagerAop"> <aop:around method="invokeWithinTransaction" arg-names="dynamicTransaction" pointcut="@annotation(dynamicTransaction)"/> </aop:aspect> </aop:config>
當(dāng)然,這里只是實現(xiàn)了在方法上的@DynamicTransactional使用,如果該注解還要對類使用,對所有函數(shù)加一個切點(diǎn),判斷該切點(diǎn)的類上是否有@DynamicTransactional注解
注意:由于切面的優(yōu)先級,如果要實現(xiàn) 方法上的注解優(yōu)先級高于類上的,還需要一點(diǎn)點(diǎn)小的處理
調(diào)用時序圖
自己實現(xiàn)基于AbstractRoutingDataSource,把多個DataSource加入到SQLSessionFactory,和之前的方式一樣,通過ThreadLocal來確定使用哪個DataSource。
關(guān)于動態(tài)事務(wù),上面是使用切面,自定義標(biāo)簽,使用TransactionTemplate來實現(xiàn)的,如果想更優(yōu)雅的話,可以仿照DataSourceTransactionManager寫一個,
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot 任務(wù)調(diào)度動態(tài)設(shè)置方式(不用重啟服務(wù))
這篇文章主要介紹了SpringBoot 任務(wù)調(diào)度 動態(tài)設(shè)置方式(不用重啟服務(wù)),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11Java使用注解實現(xiàn)BigDecimal的四舍五入
BigDecimal是Java中的一個類,位于java.math包中,它提供了任意精度的有符號十進(jìn)制數(shù)字的表示,以及對這些數(shù)字進(jìn)行算術(shù)運(yùn)算的方法,本文介紹了Java使用注解實現(xiàn)BigDecimal的四舍五入的相關(guān)知識,需要的朋友可以參考下2024-09-09Spring的@PreAuthorize注解自定義權(quán)限校驗詳解
這篇文章主要介紹了Spring的@PreAuthorize注解自定義權(quán)限校驗詳解,由于項目中,需要對外開放接口,要求做請求頭校驗,不做其他權(quán)限控制,所以準(zhǔn)備對開放的接口全部放行,不做登錄校驗,需要的朋友可以參考下2023-11-11SpringBoot數(shù)據(jù)訪問的實現(xiàn)
本文主要介紹了SpringBoot數(shù)據(jù)訪問的實現(xiàn),引入各種xxxTemplate,xxxRepository來簡化我們對數(shù)據(jù)訪問層的操作,感興趣的可以了解一下2023-11-11SpringBoot Actuator潛在的OOM問題的解決
本文主要介紹了SpringBoot Actuator潛在的OOM問題的解決,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11詳解直接插入排序算法與相關(guān)的Java版代碼實現(xiàn)
這篇文章主要介紹了直接插入排序算法與相關(guān)的Java版代碼實現(xiàn),需要的朋友可以參考下2016-05-05