博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
利用AbstractRoutingDataSource实现动态数据源切换
阅读量:6614 次
发布时间:2019-06-24

本文共 5552 字,大约阅读时间需要 18 分钟。

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

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
(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 }

配置数据源实例:

 

观察上面的配置文件,发现我们配置的是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     @Override20     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
contextHolder=new ThreadLocal
(); 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     @Override5     protected Object determineCurrentLookupKey() {6         return DatabaseContextHolder.getDbType();7     }8 9 }

 

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

1 
2
4
5
7
8
9
10
11
12
13
14
15
16
17

 

5、使用

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

 

转载地址:http://ilhso.baihongyu.com/

你可能感兴趣的文章
利用nmap对Mongodb Redis未授权访问测试
查看>>
CakePHP
查看>>
我的友情链接
查看>>
编译mysql5.6.27
查看>>
搭建centos6.7网站服务器记录
查看>>
Release版本调用ffmpeg av_register_all程序崩溃
查看>>
Referenced management pack not found
查看>>
jquery中data函数的用法示例
查看>>
巧用strtotime函数计算日期
查看>>
JVM中java对象的生命周期
查看>>
mysql 查看连接数,状态
查看>>
JFinal集成YUI Compressor压缩合并JS和CSS
查看>>
windows下的Oracle卸载
查看>>
sqlserver查看死锁的存储过程
查看>>
在VirtualBox中的CentOS 6.3下安装VirtualBox增强包(GuestAd...
查看>>
Java开发中的23种设计模式详解(转)
查看>>
我的友情链接
查看>>
组策略18招
查看>>
关于Android中的数据存储
查看>>
Tomcat配置日志生产功能
查看>>