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中如何进行数 ...
随机推荐
- Hystrix-超时设置
由于客户端请求服务端方法时,服务端方法响应超过1秒将会触发降级,所以我们可以配置Hystrix默认的超时配置 如果我们没有配置默认的超时时间,Hystrix将取default_executionTim ...
- JSTL标签库学习3-fn
fn的全称为functions, 就是提供一些方法,函数等. 主要是一些操作字符串的函数. fn:contains(str, str)显示用户浏览器,操作系统.可以从header中获取user-age ...
- Javascript 函数传参问题
属于传值,不能改变参数的属性 example 1 function Myvalue(){ var arry = 5; return arry ; } document.getElementById( ...
- sessionpage1
session1 <%@page import="java.text.SimpleDateFormat"%> <%@ page language="ja ...
- base和this
base//只能继承其直接基类成员 常用于对象初始化时和基类通信1.base局限于构造函数.实例方法.实例属性访问其中2.base调用直接基类已被重写的方法,或者所有父类的非重载方法3.base制定创 ...
- JVM7、8详解及优化
一.引言:永久代为什么被移出HotSpot JVM了? 详见:JEP 122: Remove the Permanent Generation 原因主要有两个: 1.由于Permanent Gener ...
- C & C++ 宏与const
1.宏定义函数: 例:#define do{exp} while(0)与#define exp有什么不同,好处在哪里? 定义复杂代码,防止分号,或是括号不匹配等错误.比如: 定义: #define s ...
- php页面的基本语法
概述: 1. PHP 脚本在服务器上执行,然后将纯 HTML 结果发送回浏览器. 2. PHP 脚本以 <?php 开始,以 ?> 结束,可以放到文档中的任何位置. 3. 当 PHP 解析 ...
- mysql基本的增删改查和条件语句
增 insert into 表名(列名,列名......) values("test1",23),("test2",23),("test3" ...
- Spring框架的AOP的底层实现之JDK的动态代理(代码了解,理解原理)
1.创建接口UserDao: package com.huida.demo1; public interface UserDao { public void save(); public void u ...