需求:系统中要实现切换数据库(业务数据库和his数据库)

网上很多资料上有提到AbstractRoutingDataSource,大致是这么说的

在Spring 2.0.1中引入了AbstractRoutingDataSource, 该类充当了DataSource的路由中介, 能有在运行时, 根据某种key值来动态切换到真正的DataSource上。

Spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性。而这样的方案就会不同于常见的单一数据实例的方案,这就要程序在运行时根据当时的请求及系统状态来动态的决定将数据存储在哪个数据库实例中,以及从哪个数据库提取数据。

Spring对于多数据源,以数据库表为参照,大体上可以分成两大类情况: 
一是,表级上的跨数据库。即,对于不同的数据库却有相同的表(表名和表结构完全相同)。 
二是,非表级上的跨数据库。即,多个数据源不存在相同的表。 
Spring2.x的版本中采用Proxy模式,就是我们在方案中实现一个虚拟的数据源,并且用它来封装数据源选择逻辑,这样就可以有效地将数据源选择逻辑从Client中分离出来。Client提供选择所需的上下文(因为这是Client所知道的),由虚拟的DataSource根据Client提供的上下文来实现数据源的选择。 
具体的实现就是,虚拟的DataSource仅需继承AbstractRoutingDataSource实现determineCurrentLookupKey()在其中封装数据源的选择逻辑

一、原理

首先看下AbstractRoutingDataSource类结构,继承了AbstractDataSource:

  1. public abstract class AbstractRoutingDataSource extends org.springframework.jdbc.datasource.AbstractDataSource implements org.springframework.beans.factory.InitializingBean

既然是AbstractDataSource,当然就是javax.sql.DataSource的子类,于是我们自然地回去看它的getConnection方法:

  1. public Connection getConnection() throws SQLException {
  2. return determineTargetDataSource().getConnection();
  3. }
  4.  
  5. public Connection getConnection(String username, String password) throws SQLException {
  6. return determineTargetDataSource().getConnection(username, password);
  7. }

原来关键就在determineTargetDataSource()里:

  1. protected DataSource determineTargetDataSource() {
  2. Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
  3. Object lookupKey = determineCurrentLookupKey();
  4. DataSource dataSource = this.resolvedDataSources.get(lookupKey);
  5. if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
  6. dataSource = this.resolvedDefaultDataSource;
  7. }
  8. if (dataSource == null) {
  9. throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
  10. }
  11. return dataSource;
  12. }

这里用到了我们需要进行实现的抽象方法determineCurrentLookupKey(),该方法返回需要使用的DataSource的key值,然后根据这个key从resolvedDataSources这个map里取出对应的DataSource,如果找不到,则用默认的resolvedDefaultDataSource。

回过头看AbstractDataSource的afterPropertiesSet方法:

  1. public void afterPropertiesSet() {
  2. if (this.targetDataSources == null) {
  3. throw new IllegalArgumentException("Property 'targetDataSources' is required");
  4. }
  5. this.resolvedDataSources = new HashMap<Object, DataSource>(this.targetDataSources.size());
  6. for (Map.Entry entry : this.targetDataSources.entrySet()) {
  7. Object lookupKey = resolveSpecifiedLookupKey(entry.getKey());
  8. DataSource dataSource = resolveSpecifiedDataSource(entry.getValue());
  9. this.resolvedDataSources.put(lookupKey, dataSource);
  10. }
  11. if (this.defaultTargetDataSource != null) {
  12. this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
  13. }
  14. }

配置数据源实例:

  1. <bean id="onlineDynamicDataSource" class="com.xx.stat.base.dynamic.DynamicDataSource">
  2. <property name="targetDataSources">
  3. <map key-type="java.lang.String">
  4. <entry key="xx" value-ref="dataSourceXX"/>
  5. <entry key="yy" value-ref="dataSourceYY"/>
  6. </map>
  7. </property>
  8. <property name="defaultTargetDataSource" ref="dataSource"/>
  9. </bean>

观察上面的配置文件,发现我们配置的是targetDataSources和defaultTargetDataSource

二、Spring配置多数据源的方式和具体使用过程

1、数据源的名称常量类

  1. public enum DatabaseTypeEnum {
  2. DB_DLHMC("dlhmc", "dlhmc数据库,默认的数据库"),DB_HIS("his", "HIS数据库");
  3. private String value;
  4. private String desc;
  5.  
  6. private DatabaseTypeEnum(String value, String description) {
  7. this.value = value;
  8. this.desc = description;
  9. }
  10.  
  11. public String getValue() {
  12. return value;
  13. }
  14.  
  15. public String getDesc() {
  16. return desc;
  17. }
  18.  
  19. @Override
  20. public String toString() {
  21.  
  22. return "{" + value + ":" + desc + "}";
  23. }
  24.  
  25. public static DatabaseTypeEnum from(String value) {
  26. for (DatabaseTypeEnum item : values()) {
  27. if (item.getValue() == value) {
  28. return item;
  29. }
  30. }
  31. throw new IllegalArgumentException(String.format(
  32. "非法的输入参数 '%s' ! 必须是%s中的其中一个。", value, Arrays.asList(values())
  33. .toString()));
  34. }
  35.  
  36. }

2、建立一个获得和设置上下文环境的类,主要负责改变上下文数据源的名称

  1. public class DatabaseContextHolder {
  2. private static ThreadLocal<String> contextHolder=new ThreadLocal<String>();
  3. public static void setDbType(String dbType){
  4. contextHolder.set(dbType);
  5. }
  6. public static String getDbType(){
  7. return contextHolder.get();
  8. }
  9.  
  10. public static void clearDbType(){
  11. contextHolder.remove();
  12. }
  13.  
  14. }

3、建立动态数据源类,注意,这个类必须继承AbstractRoutingDataSource,且实现方法 determineCurrentLookupKey,该方法返回一个Object,一般是返回字符串

  1. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
  2.  
  3. public class DynamicDataSource extends AbstractRoutingDataSource{
  4. @Override
  5. protected Object determineCurrentLookupKey() {
  6. return DatabaseContextHolder.getDbType();
  7. }
  8.  
  9. }

4、编写spring的配置文件配置多个数据源

  1. <!-- 数据源配置 -->
  2. <bean id="defaultDS" class="com.alibaba.druid.pool.DruidDataSource" p:driverClassName="${jdbc.driver}" p:url="${jdbc.jdbcUrl}" p:username="${jdbc.username}" p:password="${jdbc.password}"
  3. p:initialSize="${jdbc.initialSize}" p:maxActive="${jdbc.maxActive}" p:testOnBorrow="${jdbc.testOnBorrow:false}" destroy-method="close">
  4. </bean>
  5. <bean id="hisDS" class="com.alibaba.druid.pool.DruidDataSource" p:driverClassName="${his.jdbc.driver}" p:url="${his.jdbc.jdbcUrl}" p:username="${his.jdbc.username}" p:password="${his.jdbc.password}"
  6. p:initialSize="${his.jdbc.initialSize}" p:maxActive="${his.jdbc.maxActive}" p:testOnBorrow="${his.jdbc.testOnBorrow:false}" destroy-method="close">
  7. </bean>
  8. <bean id="dataSource" class="com.supconit.util.datasource.DynamicDataSource">
  9. <property name="targetDataSources">
  10. <map key-type="java.lang.String">
  11. <entry key="dlhmc" value-ref="defaultDS" />
  12. <entry key="his" value-ref="hisDS" />
  13. <!-- entry key="2" value-ref="ds2" / -->
  14. </map>
  15. </property>
  16. <property name="defaultTargetDataSource" ref="defaultDS" />
  17. </bean>

5、使用

  1. @Override
  2. public List<VBedPatientNew> selectNursinglevel() {
  3. DatabaseContextHolder.setDbType(DatabaseTypeEnum.DB_HIS.getValue());
  4. List<VBedPatientNew> result=selectList("selectNursinglevel");
  5. DatabaseContextHolder.clearDbType();
  6. return result;
  7. }

利用AbstractRoutingDataSource实现动态数据源切换的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

    使用Spring 提供的 AbstractRoutingDataSource 实现 创建 AbstractRoutingDataSource 实现类,负责保存所有数据源与切换数据源策略:public ...

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

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

  8. 【开发笔记】- AbstractRoutingDataSource动态数据源切换,AOP实现动态数据源切换

    AbstractRoutingDataSource动态数据源切换 上周末,室友通宵达旦的敲代码处理他的多数据源的问题,搞的非常的紧张,也和我聊了聊天,大概的了解了他的业务的需求.一般的情况下我们都是使 ...

  9. AbstractRoutingDataSource动态数据源切换,AOP实现动态数据源切换

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/u012881904/article/de ...

随机推荐

  1. 利用ASP.NET一般处理程序动态生成Web图像(转)

    摘自:http://www.cnblogs.com/zhouhb/archive/2011/02/15/1955262.html 一般处理程序的扩展名为ashx,它实现了IHttpHandler接口, ...

  2. 开发完 iOS 应用,接下去你该做的事

    iOS专项总结 一个应用经过多次迭代后告一段落,接下去我们在技术上还可以做些什么呢?答案是提高代码的整体质量.关于这方面,除了我们常喊的 重构,测试也非常重要. 博主近期给我们的 iOS客户端代码来了 ...

  3. hadoop编译map/reduce时的问题

    参考链接 http://hadoop.apache.org/common/docs/stable/mapred_tutorial.html http://blog.endlesscode.com/20 ...

  4. Android学习系列(18)--App工程结构搭建

     本文算是一篇漫谈,谈一谈关于Android开发中工程初始化的时候如何在初期我们就能搭建一个好的架构.      关于android架构,因为手机的限制,目前我觉得也确实没什么大谈特谈的,但是从开发的 ...

  5. oracle中的一些基本概念

    Oracle数据库的物理文件是存储在磁盘上的数据文件.控制文件和日志文件的总称.数据文件和日志文件是数据库中最重要的文件.数据库由若干个表空间组成,表空间由表组成,表由段组成,段由区间组成,区间由数据 ...

  6. ASM_Oracle ASM的常用命令(汇总)

    2014-07-02 Created By BaoXinjian

  7. js中window.location.search的用法和作用。

    用该属性获取页面 URL 地址: window.location 对象所包含的属性 属性 描述 hash 从井号 (#) 开始的 URL(锚) host 主机名和当前 URL 的端口号 hostnam ...

  8. 生产环境JAVA进程高CPU占用故障排查

    问题描述:生产环境下的某台tomcat7服务器,在刚发布时的时候一切都很正常,在运行一段时间后就出现CPU占用很高的问题,基本上是负载一天比一天高. 问题分析:1,程序属于CPU密集型,和开发沟通过, ...

  9. Oracle中查询表字段基本信息、主键、外键(整理)

    背景 因为项目某些模块的数据结构设计没有严格按照某规范设计,所以只能从数据库中查询数据结构,需要查询的信息如下:字段名称.数据类型.是否为空.默认值.主键.外键等等. 在网上搜索了查询上述信息的方法, ...

  10. string.format的用途联想

    还有IFormattable,感觉这辈子都用不到.. 设想了一个物品简介界面 分别是Title,分割线,内容. "{0:Red} \n {1:S1} \n 简介 \n {2:Green}&q ...