发布于 2018-03-17 22:27:10 | 211 次阅读 | 评论: 0 | 来源: 网友投递

这里有新鲜出炉的精品教程,程序狗速度看过来!

Spring Framework 开源j2ee框架

Spring是什么呢?首先它是一个开源的项目,而且目前非常活跃;它是一个基于IOC和AOP的构架多层j2ee系统的框架,但它不强迫你必须在每一层 中必须使用Spring,因为它模块化的很好,允许你根据自己的需要选择使用它的某一个模块;它实现了很优雅的MVC,对不同的数据访问技术提供了统一的接口,采用IOC使得可以很容易的实现bean的装配,提供了简洁的AOP并据此实现Transcation Managment,等等


本篇文章主要介绍了基于spring多数据源动态调用及其事务处理 ,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

需求:

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

下图的单数据源的调用和多数据源动态调用的流程,可以看出在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写一个,

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持PHPERZ。



最新网友评论  共有(0)条评论 发布评论 返回顶部

Copyright © 2007-2017 PHPERZ.COM All Rights Reserved   冀ICP备14009818号  版权声明  广告服务