使用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. ubuntu 16.04 install wine

    from: https://wiki.winehq.org/Ubuntu If your system is 64 bit, enable 32 bit architecture (if you ha ...

  2. DateJsonValueProcessor日期处理

    package com.zjx.controller; import java.text.SimpleDateFormat; import net.sf.json.JsonConfig; import ...

  3. 创建jsp+Servlet+JavaBean+JDBC+MySQL项目的过程

    1 根据需求建立Mysql数据,确立数据库的表的字段.属性.主键,外键等.下面我使用的数据库名dev ,表名user,字段  name,设置为主键.用户名不能为空,字段password,密码 2 在E ...

  4. CTR点击率预估干货分享

    CTR点击率预估干货分享 http://blog.csdn.net/bitcarmanlee/article/details/52138713

  5. Java http请求和调用(二)

    http请求代码 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader ...

  6. cmd 命令相关

    计算相关进程数: tasklist|find /i "cmd.exe" 安装git的可以 tasklist|find /i "cmd.exe"  | wc -l

  7. IntelliJ IDEA project module

    在IDEA 创建一个project,目录结构是这样的:在project下创建一个module之后目录结构是这样的: 简单的概括如下: IntelliJ系中的 Project  相当于Eclipse系中 ...

  8. spring JPA分页排序条件查询

    @RequestMapping("/listByPage") public Page<Production> listByPage(int page, int size ...

  9. 第八章 高级搜索树 (a1)伸展树:逐层伸展

  10. Maven国内镜像-阿里云

    国外的maven下载速度堪忧,大部分国内网络访问都很慢国内的阿里云同样提供了maven的文件镜像使用:1.在maven的setting.xml加入下段代码即可使用阿里云的maven镜像 <mir ...