原创地址: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写一个,不过先要知晓里面的原理。