博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基于spring多数据源动态调用及其事务处理
阅读量:4944 次
发布时间:2019-06-11

本文共 4268 字,大约阅读时间需要 14 分钟。

需求:

有些时候,我们需要连接多个数据库,但是,在方法调用前并不知道到底是调用哪个。即同时保持多个数据库的连接,在方法中根据传入的参数来确定。

下图的单数据源的调用和多数据源动态调用的流程,可以看出在Dao层中需要有一个DataSource选择器,来确定到底是调用哪个数据源。

实现方式

对Dao层提供一个公共父类,保持有多个数据源的连接(本人是基于iBatis,即保持多个SQLSessionTemplate)

/** * Created by hzlizhou on 2017/2/6. */public abstract class MultiDatasourceDao implements IDaoSupport {    private Map
sqlSessionTemplateMap; private MultiDataSourceSelector multiDataSourceSelector; public MultiDatasourceDao(Map
sqlSessionTemplateMap, MultiDataSourceSelector multiDataSourceSelector) { this.sqlSessionTemplateMap = sqlSessionTemplateMap; this.multiDataSourceSelector = multiDataSourceSelector; } public Map
getSqlSessionTemplateMap() { return sqlSessionTemplateMap; } public void setSqlSessionTemplateMap(Map
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
[] 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() {        @Override        public Object doInTransaction(TransactionStatus status) {            Object result = null;            try {                result = pjp.proceed();            } catch (Throwable throwable) {                Class
[] c = dynamicTransaction.rollbackFor(); for (Class
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写一个,

转载于:https://www.cnblogs.com/lizo/p/7089263.html

你可能感兴趣的文章
项目练习计划
查看>>
Xshell远程登录
查看>>
@RequestParam与@PathVariable的区别
查看>>
C语言之break和continue
查看>>
jquery.form.js使用
查看>>
LINQ to Entities 不支持 LINQ 表达式节点类型“ArrayIndex”。
查看>>
回顾2012,展望2013
查看>>
Spring中的ApplicationContextAware使用
查看>>
HDU-2067-小兔的棋盘
查看>>
监听手机录音
查看>>
hadoop的WordCount样例
查看>>
客户化程序完成标准成本成批更新
查看>>
JZOJ 1286. 太空电梯
查看>>
大数据平台组件布置 与 进程查看
查看>>
Hadoop3集群搭建之——hive添加自定义函数UDTF (一行输入,多行输出)
查看>>
【转】去除inline-block元素的间隙
查看>>
JS - Math对象
查看>>
MUI开发指南(二) webview对象
查看>>
HTML5按键打开摄像头和拍照
查看>>
基本sql语句--触发器
查看>>