需求:

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

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

基于spring多数据源动态调用及其事务处理的更多相关文章

  1. Spring多数据源动态切换

    title: Spring多数据源动态切换 date: 2019-11-27 categories: Java Spring tags: 数据源 typora-root-url: ...... --- ...

  2. Spring主从数据源动态切换

    参考文档: http://uule.iteye.com/blog/2126533 http://lanjingling.github.io/2016/02/15/spring-aop-dynamicd ...

  3. springAOP实现基于注解的数据源动态切换

    需求 代码实现读写数据库分离 武器 spring3.0以上版本 实现思路 1.继承org.springframework.jdbc.datasource.lookup.AbstractRoutingD ...

  4. spring 多数据源动态切换

    理解spring动态切换数据源,需要对spring具有一定的了解 工作中经常遇到读写分离,数据源切换的问题,那么以下是本作者实际工作中编写的代码  与大家分享一下! 1.定义注解 DataSource ...

  5. SPRINGAOP实现基于注解的数据源动态切换(转)

    需求 代码实现读写数据库分离 武器 spring3.0以上版本 实现思路 1.继承org.springframework.jdbc.datasource.lookup.AbstractRoutingD ...

  6. spring security实现动态配置url权限的两种方法

    缘起 标准的RABC, 权限需要支持动态配置,spring security默认是在代码里约定好权限,真实的业务场景通常需要可以支持动态配置角色访问权限,即在运行时去配置url对应的访问角色. 基于s ...

  7. 基于Spring Boot,使用JPA动态调用Sql查询数据

    在<基于Spring Boot,使用JPA操作Sql Server数据库完成CRUD>,<基于Spring Boot,使用JPA调用Sql Server数据库的存储过程并返回记录集合 ...

  8. Spring Boot2.x 动态数据源配置

    原文链接: Spring Boot2.x 动态数据源配置 基于 Spring Boot 2.x.Spring Data JPA.druid.mysql 的动态数据源配置Demo,适合用于数据库的读写分 ...

  9. 基于注解的Spring多数据源配置和使用

    前一段时间研究了一下spring多数据源的配置和使用,为了后期从多个数据源拉取数据定时进行数据分析和报表统计做准备.由于之前做过的项目都是单数据源的,没有遇到这种场景,所以也一直没有去了解过如何配置多 ...

随机推荐

  1. Docker - 虚拟网桥

    容器的网络模式 None --- 容器不能访问外部网络,内部存在回路地址. Container --- 将容器的网络栈合并到一起,可与其他容器共享网络. Host --- 与主机共享网络. Bridg ...

  2. window 远程在Linux(centOS7.0)上安装JDK以及配置环境变量

    本人是在windows 7 上安装了虚拟机,虚拟机安装的是linux(centOS7.0)系统现在在Windows 上安装SecureCRT 远程虚拟机的linux系统,安装JDK以及配置环境变量. ...

  3. AlertDialog中的EditText不能输入

    一.描述 在项目中有碰到使用AlertDialog,给他设置自定义布局,自定义布局中有包含EditText,但是运行起来后发现EditText不能输入文字,没有焦点,一开始还以为是事件拦截掉了,后来试 ...

  4. Java迭代器Iterator

    之前我们实现了迭代器模式,很多编程语言实际上已经内置了迭代器类,比如Java就为我们实现了迭代器Iterator.我们首先来看Iterator中的源码. 通过JDK源码我们发现Iterator是一个接 ...

  5. 必知的 15 个jQuery小技巧(干货)

    jQuery小技巧(干活) 1.返回顶部按钮 你可以利用 animate 和 scrollTop 来实现返回顶部的动画,而不需要使用其他插件. $('a.top').click(function(){ ...

  6. 2017年中国大学生程序设计竞赛-中南地区赛暨第八届湘潭市大学生计算机程序设计大赛题解&源码(A.高斯消元,D,模拟,E,前缀和,F,LCS,H,Prim算法,I,胡搞,J,树状数组)

    A------------------------------------------------------------------------------------ 题目链接:http://20 ...

  7. Java调用IDL方法总结

    Java调用IDL方法总结 Java调用IDL程序,需要先在java中加载IDL的java包(javaidlb.jar),该包不需要下载,在IDL的安装目录中可以直接找到(C:\Program Fil ...

  8. 开涛spring3(9.2) - Spring的事务 之 9.2 数据库事务概述

    9.2.1  概述 Spring框架支持事务管理的核心是事务管理器抽象,对于不同的数据访问框架(如Hibernate)通过实现策略接口 PlatformTransactionManager,从而能支持 ...

  9. THREE笛卡尔右手坐标系详解

    1,正常的笛卡尔右手坐标系,以屏幕右方为+X轴,屏幕上方为+Y轴,垂直屏幕向外为+Z轴,如下图,xy轴组成的平面为屏幕面 但由于THREE里的相机并不总是从屏幕正前方视角,还可以设置坐标系任意一个轴为 ...

  10. 模仿qq空间或朋友圈发布动态、评论动态、回复评论、删除动态或评论的功能(上)

      我们大部分人都发过动态,想必都知道发动态.回复评论.删除动态的整个过程,那么作为初学者,要模仿这些功能有点复杂的,最起码表的关系得弄清楚~~ 先把思路理一下: (1)用户登录,用session读取 ...