原創地址:http://www.javashuo.com/article/p-uiycfbai-ev.htmlsql
有些時候,咱們須要鏈接多個數據庫,可是,在方法調用前並不知道究竟是調用哪一個。即同時保持多個數據庫的鏈接,在方法中根據傳入的參數來肯定。數據庫
下圖的單數據源的調用和多數據源動態調用的流程,能夠看出在Dao層中須要有一個DataSource選擇器,來肯定究竟是調用哪一個數據源。編程
對Dao層提供一個公共父類,保持有多個數據源的鏈接(本人是基於iBatis,即保持多個SQLSessionTemplate)ide
/** * 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中的(若是你看過Dubbo傳遞上下文的那篇文章就知道爲何了)函數
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的時候就是動態了。this
其實這個都好辦,而後咱們就面臨一個稍微複雜一點的問題,那DataSource是動態的,事務也就必須是動態了的。並且還對原有的代碼沒有侵入(例如Spring中的@Transactional 註解),那實現一個相似@Transactional的方法吧。名字就叫作@DynamicTransactionalspa
@Documented @Target({METHOD, TYPE}) @Retention(RUNTIME) public @interface DynamicTransactional { Propagation propagation() default Propagation.REQUIRED; Class<? extends Throwable>[] rollbackFor() default {}; }
基本思想是在經過AOP切面攔截@DynamicTransactional註解,調用,而後本身編程實現事務.net
切面內的核心方法是orm
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返回blog
切面的配置:重點是怎麼對指定註解進行切面
<aop:config>
<aop:aspect id="multiTransactionManagerAspect" ref="multiTransactionManagerAop">
<aop:around method="invokeWithinTransaction"
arg-names="dynamicTransaction"
pointcut="@annotation(dynamicTransaction)"/>
</aop:aspect>
</aop:config>
固然,這裏只是實現了在方法上的@DynamicTransactional使用,若是該註解還要對類使用,對全部函數加一個切點,判斷該切點的類上是否有@DynamicTransactional註解
注意:因爲切面的優先級,若是要實現 方法上的註解優先級高於類上的,還須要一點點小的處理
------------------------------------------2017年2月10日 更新---------------------------------------
本身實現基於AbstractRoutingDataSource,把多個DataSource加入到SQLSessionFactory,和以前的方式同樣,經過ThreadLocal來肯定使用哪一個DataSource。
關於動態事務,上面是使用切面,自定義標籤,使用TransactionTemplate來實現的,若是想更優雅的話,能夠仿照DataSourceTransactionManager寫一個,不過先要知曉裏面的原理。