利用AbstractRoutingDataSource实现动态数据源切换
需求:系统中要实现切换数据库(业务数据库和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实现动态数据源切换的更多相关文章
- SpringMVC 利用AbstractRoutingDataSource实现动态数据源切换
SpringMVC 利用AbstractRoutingDataSource实现动态数据源切换 本文转载至:http://exceptioneye.iteye.com/blog/1698064 Spri ...
- AbstractRoutingDataSource 实现动态数据源切换原理简单分析
AbstractRoutingDataSource 实现动态数据源切换原理简单分析 写在前面,项目中用到了动态数据源切换,记录一下其运行机制. 代码展示 下面列出一些关键代码,后续分析会用到 数据配置 ...
- Spring(AbstractRoutingDataSource)实现动态数据源切换--转载
原始出处:http://linhongyu.blog.51cto.com/6373370/1615895 一.前言 近期一项目A需实现数据同步到另一项目B数据库中,在不改变B项目的情况下,只好选择项目 ...
- AbstractRoutingDataSource实现动态数据源切换 专题
需求:系统中要实现切换数据库(业务数据库和his数据库) 网上很多资料上有提到AbstractRoutingDataSource,大致是这么说的 在Spring 2.0.1中引入了AbstractRo ...
- Spring(AbstractRoutingDataSource)实现动态数据源切换
转自: http://blog.51cto.com/linhongyu/1615895 一.前言 近期一项目A需实现数据同步到另一项目B数据库中,在不改变B项目的情况下,只好选择项目A中切换数据源,直 ...
- spring AbstractRoutingDataSource实现动态数据源切换
使用Spring 提供的 AbstractRoutingDataSource 实现 创建 AbstractRoutingDataSource 实现类,负责保存所有数据源与切换数据源策略:public ...
- Spring 实现动态数据源切换--转载 (AbstractRoutingDataSource)的使用
[参考]Spring(AbstractRoutingDataSource)实现动态数据源切换--转载 [参考] 利用Spring的AbstractRoutingDataSource解决多数据源的问题 ...
- 【开发笔记】- AbstractRoutingDataSource动态数据源切换,AOP实现动态数据源切换
AbstractRoutingDataSource动态数据源切换 上周末,室友通宵达旦的敲代码处理他的多数据源的问题,搞的非常的紧张,也和我聊了聊天,大概的了解了他的业务的需求.一般的情况下我们都是使 ...
- AbstractRoutingDataSource动态数据源切换,AOP实现动态数据源切换
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/u012881904/article/de ...
随机推荐
- 锋利的jQuery(第二版)源码下载地址
书上给的http://cssrain.sinaapp.com无法访问 问作者邮箱要的: 第1版源码:百度云盘下载:http://pan.baidu.com/share/link?shareid=104 ...
- HDUOJ-----2824The Euler function
The Euler function Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Other ...
- JQuery包装集size,length,index,slice,find,filter,is,children,next,nextAll,parent,parents,closest,siblings,add,end,andSelf的用法
在使用Jquery包装集的知识之前首先要注意三个概念(当前包装集.新包装集.包装集内部元素)的区别. <!DOCTYPE html> <html xmlns="http:/ ...
- 制作IOS 后台极光推送时,遇到的小问题
推送广义上分为两种, 一种是 程序在前台的时候,不想在任务栏里面显示通知,直接在app中进行某种操作.这个叫做自定义消息.这个是在前台时,app与极光后台建立了一个长链接. 另一种是 程序处于前. ...
- TransactionScope事务处理方法介绍及.NET Core中的注意事项 SQL Server数据库漏洞评估了解一下 预热ASP.NET MVC 的VIEW [AUTOMAPPER]反射自动注册AUTOMAPPER PROFILE
TransactionScope事务处理方法介绍及.NET Core中的注意事项 作者:依乐祝 原文链接:https://www.cnblogs.com/yilezhu/p/10170712.ht ...
- python学习笔记013——包package
1 包(模块包)package 1.1 包的定义 包是将模块以文件夹的组织形式进行分组管理的方法 1.2 作用 分类管理,有利于防止命名冲突 可以在需要时加载一个或部分模块,而不是全部模块 mypac ...
- django中数据库的相关操作
一.使用环境 python2.7,django>1.7 二.数据库进行配置 在setting文件中进行修改 1.找到DATABASES DATABASES = { 'default': { 'E ...
- MongoDB查询指定字段(field)返回指定字段的方法
使用MongoDB的时候需要只查询指定的字段进行返回,也就是类似mysql里面的 SELECT id,name,age 这样而不是SELECT *.在MongoDB里面映射(projection)声明 ...
- mysql特殊语句学习
一.Mysql ON子句和USING子句 Mysql 中联接SQL语句中,ON子句的语法格式为:table1.column_name = table2.column_name. 当模式设计对联接表的列 ...
- storm配置:如何解决worker进程内存过小的问题
问题导读1.如何设置storm内存?2.如果没有配置文件的情况下,该如何配置一些参数?3.通过哪个参数可以配置内存? Storm中真正干活的是各个worker,而worker由supervisor负责 ...