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

作者:atheva 时间:2023-06-23 14:37:25 

需求:

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

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

来源:http://www.cnblogs.com/lizo/p/7089263.html?utm_source=tuicool&utm_medium=referral

标签:spring,动态数据源
0
投稿

猜你喜欢

  • Java Springboot整合支付宝接口的教程详解

    2023-11-06 19:41:49
  • Unity实现图形相交检测

    2021-09-16 09:31:01
  • Android编程之绘制文本(FontMetrics)实现方法

    2023-01-19 16:06:16
  • java实现置换密码加密解密

    2022-01-21 07:08:48
  • 浅谈JavaWeb中的web.xml配置部署描述符文件

    2023-11-12 00:14:13
  • 浅析C# 函数的传值与传址

    2023-11-22 04:46:57
  • Java访问WebService返回XML数据的方法

    2023-11-10 21:23:09
  • spring AOP的After增强实现方法实例分析

    2023-06-10 13:52:06
  • Java超详细梳理IO流的使用方法上

    2023-07-21 16:31:25
  • C/C++ Qt StatusBar底部状态栏应用教程

    2023-09-29 16:25:45
  • Java语言中&&与& ||与|的区别是什么

    2022-06-10 17:44:09
  • Java中Collections.emptyList()的注意事项

    2022-02-28 18:04:54
  • Android12 蓝牙适配的实现步骤

    2021-08-25 08:36:34
  • android动态设置app当前运行语言的方法

    2022-02-21 01:33:37
  • Java操作FreeMarker模板引擎的基本用法示例小结

    2022-01-15 11:34:15
  • Java持久层框架MyBatis简单实例

    2023-01-09 08:43:16
  • Java中高效的判断数组中某个元素是否存在详解

    2021-05-25 17:32:08
  • WinForm实现读取Resource中文件的方法

    2021-05-25 18:29:45
  • Maven打包时如何指定启动类

    2021-06-16 11:48:00
  • 深入理解C#中的枚举

    2022-06-03 02:58:34
  • asp之家 软件编程 m.aspxhome.com