详解基于spring多数据源动态调用及其事务处理
作者:atheva 发布时间:2023-06-23 14:37:25
需求:
有些时候,我们需要连接多个数据库,但是,在方法调用前并不知道到底是调用哪个。即同时保持多个数据库的连接,在方法中根据传入的参数来确定。
下图的单数据源的调用和多数据源动态调用的流程,可以看出在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写一个,
来源:http://www.cnblogs.com/lizo/p/7089263.html?utm_source=tuicool&utm_medium=referral
猜你喜欢
- 1.map遍历快速实现边距,文字自适应改变大小Container( // padding: EdgeI
- [LeetCode] 9. Palindrome Number 验证回文数字Determine whether an integer is
- 前言通过此篇文章,你将了解到:Flutter windows和Android桌面应用屏幕适配的解决方案;屏幕适配的相关知识和原理;flutt
- 概述:App几乎都离不开与服务器的交互,本文主要讲解了flutter网络请求三种方式 flutter自带的HttpClient、 第三方库h
- Lambda 表达式Lambda 表达式是现代 C++ 中最重要的特性之一,而 Lambda 表达式,实际上就是提供了一个类似匿名函数的特性
- 自从SEOTcs系统11月份24日更新了一下SEO得分算法以来,一直困扰我的一个问题出现了,java的数据job任务,在执行过程中会经常报以
- 在谈 JVM 内存区域划分之前,我们先来看一下 Java 程序的具体执行过程,我画了一幅图。Java 源代码文件经过编译器编译后生成字节码文
- 无论哪种界面框架输入文本框都是非常重要的控件, 但是发现flutter中的输入框TextField介绍的虽然多,但是各个属性怎么组合满足需要
- forward_list 概述forward_list 是 C++ 11 新增的容器,它的实现为单链表。forward_list 是支持从容
- 前言本文的记录如何用CustomPaint、GestureDetector实现一个进度条控件。首先需要说明的是 flutter Materi
- 在使用springMVC框架构建web应用,客户端常会请求字符串、整型、json等格式的数据,通常使用@ResponseBody注解使 co
- 背景今天面试字节算法岗时被问到的问题,让我用C++实现一个softmax函数。softmax是逻辑回归在多分类问题上的推广。大概的公式如下:
- 相比于直线检测,直线拟合的最大特点是将所有数据只拟合出一条直线void fitLine( InputArray points, Output
- 上一节《SSH框架网上商城项目第1战之整合Struts2、Hibernate4.3和Spring4.2》我们搭建好了Struts2、Hibe
- 背景后台系统需要接入 企业微信登入,满足企业员工快速登入系统流程图简单代码说明自定义一套 springsecurity 认证逻辑主要就是 根
- 背景最近好几个项目在运行过程中客户都提出文件上传大小的限制能否设置的大一些,用户经常需要上传好几个G的资料文件,如图纸,视频等,并且需要在上
- 前言在使用Java开发接口请求中,我们需要对请求进行进行统一返回值,这时候我们自己封装一个统一的Result返回类,下面就介绍下我用的这种的
- 开发环境win10Android Studio效果用于多级菜单展示,或选择。如 每个省,市,县;如 树木的病虫害;关键代码 @overrid
- 前言最近看插件库上少有的取色器大都是圆形的或者奇奇怪的的亚子,所以今天做两个矩形的颜色取色器提示:以下是本篇文章正文内容,下面案例可供参考一
- 什么是异步?为什么要用它?异步编程提供了一个非阻塞的,事件驱动的编程模型。 这种编程模型利用系统中多核执行任务来提供并行,因此提供了应用的吞