需求:

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

下图的单数据源的调用和多数据源动态调用的流程,可以看出在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. java泛型探索——介绍篇

    1. 泛型出现前后代码对比 先来看看泛型出现前,代码是这么写的: List words = new ArrayList(); words.add("Hello "); words. ...

  2. java环境安装之不能安装exe文件

    经过多年,打算再学下java 从官网下载java8安装包,但因为我的计算机的cd-room被卸载掉了,安装是报错说cab文件损坏之类的错误. 从stackOverflow中找到一篇眼前一亮的解决办法. ...

  3. JDBC事务详解

    在JDBC的数据库操作中,事务是由一个或者多个操作组成的一个不可分割的工作单元.JDBC的事务处理包含三个方面:事务的自动提交模式(Auto-commit mode).事务隔离级别(Transacti ...

  4. (转载)sizeof

    [C++专题]C++ sizeof 使用规则及陷阱分析   摘要:鉴于sizeof为各大软件公司笔试.面试必考题,现收集sizeof的各种用法,尽量做到全面理解,其中例子希望能举一反三.提示:下文例子 ...

  5. 最近项目用到Dubbo框架,分享一下~

    1. Dubbo是什么? Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案.简单的说,dubbo就是个服务框架,如果没有分布式的需求,其实是不需 ...

  6. 磁盘分区-gdisk用法

    gdisk用法 gdisk - InteractiveGUIDpartitiontable (GPT) manipulator GPTfdisk (akagdisk) isatext-modemenu ...

  7. R实现地理位置与经纬度相互转换

    本实例要实现目标通过输入城市名或者地名,然后找出其经度纬度值,以及通过可视化展现其线路流向以及周边地图展示 address_list数据: 山西省太原市小店区亲贤北街77号 贵州省贵阳市云岩区书香门第 ...

  8. Kotlin入门第三课:数据类型

    前文链接: Kotlin学习第一课:从对比Java开始 Kotlin入门第二课:集合操作 初次尝试用Kotlin实现Android项目 Kotlin的数据类型与Java类似,因此这篇文章主要看Kotl ...

  9. java基础之位运算

    java中常见的位运算符:&(逻辑与) |(逻辑或) ~(取反) ^(逻辑异或) >>(右移) <<(左移) >>>(无符号右移) &(逻辑与 ...

  10. mysql 数据库优化要点

    1尽可能使用更小的类型 2尽可能的定义字段为not null,除非这个字段需要设置成null 3如果没有可变长度的字段varchar,尽可使用char 4所有字段应该有默认值 5所有的数据应该在保存之 ...