spring AbstractRoutingDataSource实现动态数据源切换
使用Spring 提供的 AbstractRoutingDataSource 实现
创建 AbstractRoutingDataSource 实现类,负责保存所有数据源与切换数据源策略:
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDatasourceKey();
}
}
xml中配置数据源与事务:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="dynamicDataSource" class="spring.dbs.DynamicDataSource">
<property name="defaultTargetDataSource" ref="primaryDataSource"/>
<property name="targetDataSources">
<map>
<entry key="primaryDataSource" value-ref="primaryDataSource"></entry>
<entry key="secondaryDataSource" value-ref="secondaryDataSource"></entry>
<entry key="thirdlyDataSource" value-ref="thirdlyDataSource"></entry>
</map>
</property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dynamicDataSource"/>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="batch*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="persist*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="remove*" propagation="REQUIRED"/>
<tx:method name="merge*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="select*" read-only="true" propagation="SUPPORTS"/>
<tx:method name="query*" read-only="true" propagation="SUPPORTS"/>
<tx:method name="get*" read-only="true" propagation="SUPPORTS"/>
<tx:method name="load*" read-only="true" propagation="SUPPORTS"/>
<tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
<tx:method name="*" read-only="true" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* spring.service..*(..))"/>
<aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice"/>
</aop:config>
</beans>
看 DataSourceContextHolder实现,负责处理切换数据源策略的key,使用ThreadLocal确保线程隔离:
public class DataSourceContextHolder {
private static final ThreadLocal<String> DATASOURCE_KEY = new ThreadLocal<>();
/**
* 设置 datasourceKey
*
* @param datasourceKey
*/
public static void setDatasourceKey(String datasourceKey) {
DATASOURCE_KEY.set(datasourceKey);
}
/**
* 获取 datasourceKey
*
* @return
*/
public static String getDatasourceKey() {
return DATASOURCE_KEY.get();
}
/**
* 删除 datasourceKey
*/
public static void clearDatasourceKey() {
DATASOURCE_KEY.remove();
}
}
使用Aop实现策略,实现动态切换数据源:
@Component
@Aspect
public class DatasourceHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(DatasourceHandler.class);
private static final String[] DATASOURCES = new String[]{"primaryDataSource", "secondaryDataSource", "thirdlyDataSource"};
① @Pointcut("execution(* spring.service..*(..))")
public void datasourcePoint() {
}
@Around("datasourcePoint()")
public Object handler(ProceedingJoinPoint point) throws Throwable {
LOGGER.info("begin get datasource......");
Object[] args = point.getArgs();
if (null != args && args.length > 0) {
② String key = String.valueOf(args[0]);
// 存入key
③ DataSourceContextHolder.setDatasourceKey(getKey(key));
}
Object result = point.proceed();
// 清除key
④ DataSourceContextHolder.clearDatasourceKey();
LOGGER.info("end get datasource......");
return result;
}
/**
* 获取key方法
*
* @param key
* @return
*/
private String getKey(String key) {
LOGGER.info("this key is : [ " + key + " ]!");
CRC32 crc = new CRC32();
crc.update(key.getBytes(StandardCharsets.UTF_8));
long result = crc.getValue();
LOGGER.info("this key's crc32 is : [ " + result + " ]!");
int index = (int) (result % 3);
LOGGER.info("this key's module is : [ " + index + " ]!");
int len = DATASOURCES.length;
if (index > (len - 1)) {
index = len - 1;
}
LOGGER.info("this key's result module is : [ " + index + " ]!");
String datasourceKey = DATASOURCES[index];
LOGGER.info("this datasource key is : [ " + datasourceKey + " ]!");
return datasourceKey;
}
}
①所有service包下的方法执行策略;
②demo默认使用第一个参数做为key;
③使用CRC32取模,获取数据源key,保存到DataSourceContextHolder.ThreadLocal.DATASOURCE_KEY 中,确保不同线程策略正确;
④业务逻辑处理完后,清除key;
使用JdbcTemplate,配置如下:
@Configuration
public class JdbcTemplateBean {
@Autowired
@Qualifier("dynamicDataSource")
private DataSource dynamicDataSource;
@Bean(name = "dynamicJdbcTemplate")
@Qualifier("dynamicJdbcTemplate")
public NamedParameterJdbcTemplate primaryJdbcTemplate() {
return new NamedParameterJdbcTemplate(dynamicDataSource);
}
}
使用过程中发现一问题,当事务配置为 propagation="REQUIRED"时,因为 Aop顺序问题导致先寻找数据源开启事务,后执行数据源切换策略,此时使用 <aop:advisor order="2"/>属性配置事务顺序为2,并使用 org.springframework.core.Ordered接口,设置策略执行顺序为1,保证策略执行在寻找数据源之前,具体如下:
事务切面配置:
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* spring.service..*(..))"/>
<aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice" order="2"/>
</aop:config>
策略执行切面:
@Component
@Aspect
public class DatasourceHandler implements Ordered {
@Pointcut("execution(* spring.service..*(..))")
public void datasourcePoint() {
}
@Around("datasourcePoint()")
public Object handler(ProceedingJoinPoint point) throws Throwable {
// 策略实现
}
@Override
public int getOrder() {
return 1;
}
}
spring AbstractRoutingDataSource实现动态数据源切换的更多相关文章
- Spring(AbstractRoutingDataSource)实现动态数据源切换--转载
原始出处:http://linhongyu.blog.51cto.com/6373370/1615895 一.前言 近期一项目A需实现数据同步到另一项目B数据库中,在不改变B项目的情况下,只好选择项目 ...
- Spring(AbstractRoutingDataSource)实现动态数据源切换
转自: http://blog.51cto.com/linhongyu/1615895 一.前言 近期一项目A需实现数据同步到另一项目B数据库中,在不改变B项目的情况下,只好选择项目A中切换数据源,直 ...
- SpringMVC 利用AbstractRoutingDataSource实现动态数据源切换
SpringMVC 利用AbstractRoutingDataSource实现动态数据源切换 本文转载至:http://exceptioneye.iteye.com/blog/1698064 Spri ...
- AbstractRoutingDataSource 实现动态数据源切换原理简单分析
AbstractRoutingDataSource 实现动态数据源切换原理简单分析 写在前面,项目中用到了动态数据源切换,记录一下其运行机制. 代码展示 下面列出一些关键代码,后续分析会用到 数据配置 ...
- dubbo服务+Spring事务+AOP动态数据源切换 出错
1:问题描述,以及分析 项目用了spring数据源动态切换,服务用的是dubbo.在运行一段时间后程序异常,更新操作没有切换到主库上. 这个问题在先调用读操作后再调用写操作会出现. 经日志分析原因: ...
- AbstractRoutingDataSource实现动态数据源切换 专题
需求:系统中要实现切换数据库(业务数据库和his数据库) 网上很多资料上有提到AbstractRoutingDataSource,大致是这么说的 在Spring 2.0.1中引入了AbstractRo ...
- 利用AbstractRoutingDataSource实现动态数据源切换
需求:系统中要实现切换数据库(业务数据库和his数据库) 网上很多资料上有提到AbstractRoutingDataSource,大致是这么说的 在Spring 2.0.1中引入了AbstractRo ...
- Spring 实现动态数据源切换--转载 (AbstractRoutingDataSource)的使用
[参考]Spring(AbstractRoutingDataSource)实现动态数据源切换--转载 [参考] 利用Spring的AbstractRoutingDataSource解决多数据源的问题 ...
- 【Spring】使用Spring的AbstractRoutingDataSource实现多数据源切换
最近因为项目需要在做两个项目间数据同步的需求,具体是项目1的数据通过消息队列同步到项目2中,因为这个更新操作还涉及到更新多个库的数据,所以就需要多数据源切换的操作.下面就讲讲在Spring中如何进行数 ...
随机推荐
- iOS Hardware Guide
来自U3D文档 Hardware models The following list summarizes iOS hardware available in devices of various g ...
- __builtin__与__builtins__的区别与关系
在学习Python时,很多人会问到__builtin__.__builtins__和builtins之间有什么关系.百度或Google一下,有很 多答案,但是这些答案要么不准确,要么只说了一点点,并不 ...
- HTML实现文件拖动上传
在大型企业的开发过程中,很多比较有趣而实际的功能往往都是让大家望而却步,我给大家带来一个百度云盘和360云盘的HTML5多文件拖动上传技术: 1:记得导入:common-fileupload.jar包 ...
- cloudrea manager 调整datanode数据存储目录
由于datanode所需磁盘空间较大,所以工作中可能会涉及到给datanode增加磁盘目录或者更改数据目录 CM停止该datanode节点 CM页面增加目录或者修改目录 如果是修改目录的话 需要将服务 ...
- PIE结对编程
学习进度条 点滴成就 学习时间 新编写代码行数 博客量 学到知识点 第一周 8 0 0 了解软件工程 第二周 7 0 1 了解软件工程 第三周 11 0 1 用例图 第四周 6 25 0 结对编程 第 ...
- 真机IOS8.3以上的文件夹共享
ios8.3以上的版本,苹果规定需要验证身份,将不在默认开启文件共享,但是在实际测试工作中,提取文件是经常需要做的操作,笔者在使用GT采集性能数据后,通过itoos或itunes都无法获得目标app的 ...
- 根据二进制和十进制转换规则转换成游戏[xyytit]
摘要: 二進位是由十進位轉換而成,它的數字都由1.0組成的.我們研究發現由十進位轉換而成的二進位的數字可以不只局限在於1~127,它的數可以更加深加廣,並且可以利用二進位的規則轉換成遊戲.我們利用2n ...
- google thumbnailator
Thumbnailator 是一个优秀的图片处理的Google开源Java类库.处理效果远比Java API的好. 从API提供现有的图像文件和图像对象的类中简化了处理过程,两三行代码就能够从现有图片 ...
- VMware安装centos虚拟机 通过NAT与主机互通并能上网
1.关于centos虚拟机的安装,我这里就不详细说明了,网上有很多教程,默认你们已经安装好. (我的环境是centos6.6 x86 最小安装版) 2.右键虚拟主机,选择设置选项. 3.在 ...
- BZOJ2424 [HAOI2010]订货 - 费用流
题解 (非常裸的费用流 题意有一点表明不清: 该月卖出的商品可以不用算进仓库里面. 然后套上费用流模板 代码 #include<cstring> #include<queue> ...