欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详解基于spring多数据源动态调用及其事务处理

程序员文章站 2023-12-05 19:58:34
需求: 有些时候,我们需要连接多个数据库,但是,在方法调用前并不知道到底是调用哪个。即同时保持多个数据库的连接,在方法中根据传入的参数来确定。 下图的单数据源的调用和多...

需求:

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

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

详解基于spring多数据源动态调用及其事务处理

实现方式

对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注解,调用,然后自己编程实现事务

详解基于spring多数据源动态调用及其事务处理

切面内的核心方法是

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。

详解基于spring多数据源动态调用及其事务处理

关于动态事务,上面是使用切面,自定义标签,使用transactiontemplate来实现的,如果想更优雅的话,可以仿照datasourcetransactionmanager写一个,

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