需求:系统中要实现切换数据库(业务数据库和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:

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

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

public Connection getConnection() throws SQLException {
return determineTargetDataSource().getConnection();
} public Connection getConnection(String username, String password) throws SQLException {
return determineTargetDataSource().getConnection(username, password);
}

原来关键就在determineTargetDataSource()里:

 protected DataSource determineTargetDataSource() {
Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = this.resolvedDataSources.get(lookupKey);
if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
dataSource = this.resolvedDefaultDataSource;
}
if (dataSource == null) {
throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
}
return dataSource;
}

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

回过头看AbstractDataSource的afterPropertiesSet方法:

 public void afterPropertiesSet() {
if (this.targetDataSources == null) {
throw new IllegalArgumentException("Property 'targetDataSources' is required");
}
this.resolvedDataSources = new HashMap<Object, DataSource>(this.targetDataSources.size());
for (Map.Entry entry : this.targetDataSources.entrySet()) {
Object lookupKey = resolveSpecifiedLookupKey(entry.getKey());
DataSource dataSource = resolveSpecifiedDataSource(entry.getValue());
this.resolvedDataSources.put(lookupKey, dataSource);
}
if (this.defaultTargetDataSource != null) {
this.resolvedDefaultDataSource = resolveSpecifiedDataSource(this.defaultTargetDataSource);
}
}

配置数据源实例:

    <bean id="onlineDynamicDataSource" class="com.xx.stat.base.dynamic.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="xx" value-ref="dataSourceXX"/>
<entry key="yy" value-ref="dataSourceYY"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSource"/>
</bean>

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

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

1、数据源的名称常量类

 public enum DatabaseTypeEnum {
DB_DLHMC("dlhmc", "dlhmc数据库,默认的数据库"),DB_HIS("his", "HIS数据库");
private String value;
private String desc; private DatabaseTypeEnum(String value, String description) {
this.value = value;
this.desc = description;
} public String getValue() {
return value;
} public String getDesc() {
return desc;
} @Override
public String toString() { return "{" + value + ":" + desc + "}";
} public static DatabaseTypeEnum from(String value) {
for (DatabaseTypeEnum item : values()) {
if (item.getValue() == value) {
return item;
}
}
throw new IllegalArgumentException(String.format(
"非法的输入参数 '%s' ! 必须是%s中的其中一个。", value, Arrays.asList(values())
.toString()));
} }

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

 public class DatabaseContextHolder {
private static ThreadLocal<String> contextHolder=new ThreadLocal<String>();
public static void setDbType(String dbType){
contextHolder.set(dbType);
}
public static String getDbType(){
return contextHolder.get();
} public static void clearDbType(){
contextHolder.remove();
} }

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

 import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

 public class DynamicDataSource extends AbstractRoutingDataSource{
@Override
protected Object determineCurrentLookupKey() {
return DatabaseContextHolder.getDbType();
} }

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

 <!-- 数据源配置 -->
<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}"
p:initialSize="${jdbc.initialSize}" p:maxActive="${jdbc.maxActive}" p:testOnBorrow="${jdbc.testOnBorrow:false}" destroy-method="close">
</bean>
<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}"
p:initialSize="${his.jdbc.initialSize}" p:maxActive="${his.jdbc.maxActive}" p:testOnBorrow="${his.jdbc.testOnBorrow:false}" destroy-method="close">
</bean>
<bean id="dataSource" class="com.supconit.util.datasource.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="dlhmc" value-ref="defaultDS" />
<entry key="his" value-ref="hisDS" />
<!-- entry key="2" value-ref="ds2" / -->
</map>
</property>
<property name="defaultTargetDataSource" ref="defaultDS" />
</bean>

5、使用

@Override
public List<VBedPatientNew> selectNursinglevel() {
DatabaseContextHolder.setDbType(DatabaseTypeEnum.DB_HIS.getValue());
List<VBedPatientNew> result=selectList("selectNursinglevel");
DatabaseContextHolder.clearDbType();
return result;
}

利用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. 工具-VIM配置

    设置缩进的空格数 shiftwidth=4 设置制表符宽度 tabstop=4 高亮显示当前行 cursorline 高亮显示当前列 cursorcolumn

  2. PHP-各种下载安装

    Windows下常用PHP扩展下载: http://dev.freshsite.pl/php-accelerators.html http://windows.php.net/downloads/pe ...

  3. 使用EditPlus技巧,提高工作效率(附英文版、自动完成文件、语法文件下载)

    http://www.cnblogs.com/JustinYoung/archive/2008/01/14/editplus-skills.html

  4. 那些恶心人的Screen基本概念

    Screen的这些基本概念中,最重要的就是dip的理解,而理解dip就是理解android适配不同设备的关键. Screen Size 实际物理尺寸.就是我们常说的3.5英寸屏幕,4.7英寸屏幕等等, ...

  5. DataGridView合并单元格(多行多列合并)

    一.点击在拖入的显示控件(TreeList)右上方的箭头,在Treelist任务中选择数据源,添加项目数据源,依次选择数据库.数据集,新建连接,浏览选择数据库(*.mdb),依次点击 下一步,选择“表 ...

  6. Linux使用技巧5--格式化U盘

    通常来说,格式化一个分区的U盘还是非常easy的.仅仅须要使用mkfs命令指定目标文件系统就能够了,样例例如以下: $ sudo fdisk -l $ sudo mkfs -t vfat /dev/s ...

  7. eclipse3.7之后,在引入的jquery的js文件打红叉

    使用Eclipse 3.7时,工程中加入jquery.xx.js文件,发现该文件出现错误提示(红×),但使用Eclipse 3.7以前的版本就不会出现这种提示.是因为Eclipse 3.7在.proj ...

  8. Android开发13——内容提供者ContentProvider的基本使用

    一.ContentProvider简介 当应用继承ContentProvider类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据.ContentProvider为存储和获取数据提 ...

  9. html5中script的async属性

    html5中script的async属性 我兴奋于html5的原因之一是一些久久未能实现的特性现在可以真正运用于实际项目中了. 如我们使用placeholder效果蛮久了但是那需要javascript ...

  10. Android 权限表

    String ACCESS_CHECKIN_PROPERTIES 同意在登入数据库的时候读写当中的属性表,并上传改变的值 String ACCESS_COARSE_LOCATION 同意应用訪问范围( ...