您好,登錄后才能下訂單哦!
需求:
有些時候,我們需要連接多個數據庫,但是,在方法調用前并不知道到底是調用哪個。即同時保持多個數據庫的連接,在方法中根據傳入的參數來確定。
下圖的單數據源的調用和多數據源動態調用的流程,可以看出在Dao層中需要有一個DataSource選擇器,來確定到底是調用哪個數據源。
實現方式
對Dao層提供一個公共父類,保持有多個數據源的連接(本人是基于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; } //子類通過這個方法動態獲取SqlSessionTemplate protected SqlSessionTemplate getSqlSessionTemplate() { String clusterName = multiDataSourceSelector.getName(); SqlSessionTemplate result = sqlSessionTemplateMap.get(clusterName); Assert.notNull(result); return result; } }
MultiDataSourceSelector是一個借口,根據當前的調用環境,返回不不同的參數,根據這個參數就可以確定使用哪一個SQLSessionTemplate,例如我是把當前環境放入到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的時候就是動態了。
動態事務
其實這個都好辦,然后我們就面臨一個稍微復雜一點的問題,那DataSource是動態的,事務也就必須是動態了的。而且還對原有的代碼沒有侵入(例如Spring中的@Transactional 注解),那實現一個類似@Transactional的方法吧。名字就叫做@DynamicTransactional
@Documented @Target({METHOD, TYPE}) @Retention(RUNTIME) public @interface DynamicTransactional { Propagation propagation() default Propagation.REQUIRED; Class<? extends Throwable>[] rollbackFor() default {}; }
基本思想是在通過AOP切面攔截@DynamicTransactional注解,調用,然后自己編程實現事務
切面內的核心方法是
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返回
切面的配置:重點是怎么對指定注解進行切面
<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寫一個,
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。