使用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实现动态数据源切换的更多相关文章

  1. Spring(AbstractRoutingDataSource)实现动态数据源切换--转载

    原始出处:http://linhongyu.blog.51cto.com/6373370/1615895 一.前言 近期一项目A需实现数据同步到另一项目B数据库中,在不改变B项目的情况下,只好选择项目 ...

  2. Spring(AbstractRoutingDataSource)实现动态数据源切换

    转自: http://blog.51cto.com/linhongyu/1615895 一.前言 近期一项目A需实现数据同步到另一项目B数据库中,在不改变B项目的情况下,只好选择项目A中切换数据源,直 ...

  3. SpringMVC 利用AbstractRoutingDataSource实现动态数据源切换

    SpringMVC 利用AbstractRoutingDataSource实现动态数据源切换 本文转载至:http://exceptioneye.iteye.com/blog/1698064 Spri ...

  4. AbstractRoutingDataSource 实现动态数据源切换原理简单分析

    AbstractRoutingDataSource 实现动态数据源切换原理简单分析 写在前面,项目中用到了动态数据源切换,记录一下其运行机制. 代码展示 下面列出一些关键代码,后续分析会用到 数据配置 ...

  5. dubbo服务+Spring事务+AOP动态数据源切换 出错

    1:问题描述,以及分析 项目用了spring数据源动态切换,服务用的是dubbo.在运行一段时间后程序异常,更新操作没有切换到主库上. 这个问题在先调用读操作后再调用写操作会出现. 经日志分析原因: ...

  6. AbstractRoutingDataSource实现动态数据源切换 专题

    需求:系统中要实现切换数据库(业务数据库和his数据库) 网上很多资料上有提到AbstractRoutingDataSource,大致是这么说的 在Spring 2.0.1中引入了AbstractRo ...

  7. 利用AbstractRoutingDataSource实现动态数据源切换

    需求:系统中要实现切换数据库(业务数据库和his数据库) 网上很多资料上有提到AbstractRoutingDataSource,大致是这么说的 在Spring 2.0.1中引入了AbstractRo ...

  8. Spring 实现动态数据源切换--转载 (AbstractRoutingDataSource)的使用

    [参考]Spring(AbstractRoutingDataSource)实现动态数据源切换--转载 [参考] 利用Spring的AbstractRoutingDataSource解决多数据源的问题 ...

  9. 【Spring】使用Spring的AbstractRoutingDataSource实现多数据源切换

    最近因为项目需要在做两个项目间数据同步的需求,具体是项目1的数据通过消息队列同步到项目2中,因为这个更新操作还涉及到更新多个库的数据,所以就需要多数据源切换的操作.下面就讲讲在Spring中如何进行数 ...

随机推荐

  1. Apache Hive 执行HQL语句报错 ( 10G )

    # 故障描述: hive > , ) as uuid, count(distinct(request_body["uuid"])) as count from log_bft ...

  2. python-最好大学排名

    # -*- coding: utf-8 -*-"""Created on Mon Apr 3 09:37:52 2017 @author: zuihaodaxuepaim ...

  3. Haskell语言学习笔记(20)IORef, STRef

    IORef 一个在IO monad中使用变量的类型. 函数 参数 功能 newIORef 值 新建带初值的引用 readIORef 引用 读取引用的值 writeIORef 引用和值 设置引用的值 m ...

  4. 如何将Wav文件做到EXE文件里

    1)编写.RC文件 ..RC文件是资源的源文件,编译器也就编译这个文件,生成.RES的资源文件 首先在我们的项目子目录中建立一个纯文本文件,起名叫Sound.rc,文件中 有一行,内容为: SOUND ...

  5. 【Java】JVM(三)、Java垃圾收集器

    一.Minor GC.Major GC 和 Full GC Minor GC:清理新生代空间,当Eden空间不能分配时候引发Minor GC Major GC:清理老年代空间 Full GC:清理Ja ...

  6. ORACLE 对一个表进行循环查数,再根据MO供给数量写入另一个新表

    一. 加工处理后要变成如下效果 create table test1 (sonum varchar2(10),lineid varchar2(10),qty int ,qty2 int ,remark ...

  7. windows下多个文件合并成一个文件

    如果你拿到的是一堆文件,那么你想把它合并成一个文件来使用,那么按下面的步骤,轻轻松松就可以搞定. 第一步:把所有要合并的文件放到同一个文件下面 第二步:在CMD里面进入到你的文件目录 第三步:输入如下 ...

  8. web程序的配置文件的修改

    vs上面的config与wwwroot中的config有可能不一样,注意修改配置的资源路径及ip及数据库及禁止ip

  9. 自对齐(self-aligned)

    C语言是自对齐的,32位以4字节对齐,64位以8字节对齐(1字节=8 bits) 自对齐的好处:在一条指令内完成数据的取或者存的操作,使得内存访问更快:否则,如果一个变量跨机器字存储,那么要做两次或更 ...

  10. 最小重组缓冲区和路径MTU发现

    概括: 主要来源于unp,可参考:http://blog.csdn.net/ysu108/article/details/7764461 疑惑: 1. 最小重组缓冲区大小: ipv4为576,ipv6 ...