再析在spring框架中解决多数据源的问题
总结多数据源的问题,其实它需要分为以下三种情况:各个数据源的数据结构不同、各个数据源的数据结构相同、各个数据源的数据结构部分相同又有部分不同。对于第二种情况,各个数据源的数据结构相同,我们使用一个sessionFactory,而在sessionFactory中通过MultiDataSource来动态切换数据源,应当是一个不错的方案,既解决了多个sessionFactory对相同的值对象重复装载对内存的浪费,又使数据源的切换对客户程序透明,简化了代码的实现和对客户程序的影响。但是,对于第一种情况,各个数据源的数据结构不同,运用这样的方案存在潜在风险。
对于各个数据源的数据结构不同的情况,使用一个sessionFactory而在这个sessionFactory中动态切换数据源,可能造成数据访问的张冠李戴。譬如,数据源A有表T而数据源B没有,可能造成客户程序在访问表T的时候却尝试去连接数据源B,因为客户程序访问哪个数据源是在程序运行期间由客户程序决定的,因此这样的错误是很难发现的。也许客户程序的一个不经意的错误就可能造成错误。解决这个问题的方法有两个:一是严格要求客户程序不要写错,这当然是可以做到的,但作为框架设计者,另一个解决方法是在框架中就避免出现这样的情况。因此我祭出了MultiSessionFactory的方案来解决各个数据源的数据结构不同的多数据源问题。
问题的分析
与MultiDataSource的方案一样,MultiSessionFactory同样是在spring框架下调用ApplicationContext的getBean()方法而不会另外创建beanFacoty,也同样使用Decorator模式来处理切换的问题。MultiSessionFactory的对象关系如图:
在该方案中,SessionFactory就是Hibernate的org.hibernate.SessionFactory接口,Decorator就是MultiSessionFactory,SessionFactory1和SessionFactory2往往是spring的org.springframework.orm.hibernate3.LocalSessionFactoryBean。细心的朋友可能会注意,实际上LocalSessionFactoryBean并不是SessionFactory的实现,这个方案是否有问题呢?这个问题其实也一直困扰了我好久,最后我发现,我们通过ApplicationContext的getBean()得到一个LocalSessionFactoryBean的时候其实并不是真正地得到了它,而是得到了一个SessionFactory,因为spring为LocalSessionFactoryBean重写了getObject(),使其返回的是SessionFactory。一个简单的明证就是,HibernateDaoSupport的sessionFactory属性的类型是SessionFactory,而我们在spring配置的时候注入的却是LocalSessionFactoryBean。
方案的实现
在整个这个方案中,我们需要实现的只有MultiSessionFactory类和我们可爱的Spserver,总共就两个类,然后呢就是一些spring的配置,就完成了。
MultiSessionFactory实现了SessionFactory,同时为了得到AplicationContext而实现了ApplicationContextAware。MultiSessionFactory的代码如下:
public class MultiSessionFactory implements SessionFactory, ApplicationContextAware {
private static final long serialVersionUID = 2064557324203496378L;
private static final Log log = LogFactory.getLog(MultiSessionFactory.class);
private ApplicationContext applicationContext = null;
private SessionFactory sessionFactory = null;
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public SessionFactory getSessionFactory(String sessionFactoryName) {
log.debug("sessionFactoryName:"+sessionFactoryName);
try{
if(sessionFactoryName==null||sessionFactoryName.equals("")){
return sessionFactory;
}
return (SessionFactory)this.getApplicationContext().getBean(sessionFactoryName);
}catch(NoSuchBeanDefinitionException ex){
throw new DaoException("There is not the sessionFactory
}
} public SessionFactory getSessionFactory() {
String sessionFactoryName = SpObserver.getSp();
return getSessionFactory(sessionFactoryName);
} public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
} // SessionFactory接口需要实现的方法 ...... }
MultiSessionFactory的完整代码见我提供的附件。setSessionFactory()实际上是设定的默认sessionFactory,它在spring装载的时候调用,其对应的数据源应当是主数据源,即项目初始化中需要读取初始化数据的数据源。在任何多数据源项目中,都应当有一个存放初始化数据、系统维护数据、用户权限数据的数据源,这就是主数据源。因此MultiSessionFactory的配置应当这样写:
<bean id="sessionFactory" class="com.htxx.service.dao.MultiSessionFactory">
<property name="sessionFactory"><ref bean="hostSessionFactory"/>property>
>
SpServer的写法与《如何在spring框架中解决多数据源的问题》中的一样,我就不再累赘了。
另外,在spring配置中配置多个数据源,每个数据源对应一个sessionFactory,这个对应的sessionFactory中的值对象应当是该数据源的值对象。客户程序在执行数据访问前,通过调用SpServer的putSp()方法,告诉MultiSessionFactory需要切换到哪个sessionFactory,然后执行数据访问。这样,不同数据源的值对象通过放在不同的sessionFactory中,避免了张冠李戴的情况。具体的示例见附件的MultiSessionFactoryTest。
另外的方案
也许有些朋友对以上方案还不满意,因为在执行数据访问前毕竟还要多做一步指定sessionFactory的工作。实际上,对于各个数据源的数据结构不同的项目,一个值对象应当使用哪个数据源有一个非常确定的对应关系。如果通过配置文件将值对象与它的sessionFactory对应起来,那么我们在执行数据访问的时候传递的是哪个值对象,MultiSessionFactory马上就可以去找到对应的sessionFactory。这个方案你可以通过AOP来制作一个拦截器拦截所有诸如save()、delete()、get()、load()等方法来实现,也可以扩展HibernateDaoSupport来实现。这样的方案使客户程序甚至都不用知道他是在操作的一个多数据源系统。当然,这个方案感兴趣的朋友可以自己去实现。
另外,在这个方案中的核心是运用Decorator设计模式来解决切换sessionFactory的目的,即MultiSessionFactory的实现。至于通过什么方式来通知MultiSessionFactory应当切换到哪个SessionFactory,可以根据不同项目的情况自由选择。我在这里给大家提供了通过SpOberver和建立值对象与sessionFactory关系的配置文件这两个方案,你也可以有自己的方案解决。
第三种情况的解决方案
前面我已经给出了第一种和第二种情况的解决方案:各个数据源的数据结构不同的情况用MultiSessionFactory解决;各个数据源的数据结构相同的情况用MultiDataSource解决。那么第三种情况,各个数据源的数据结构部分相同又有部分不同,又应当如何解决呢?当然是将MultiSessionFactory和MultiDataSource结合起来解决。对于数据结构不同的部分,其分别创建各自的sessionFactory然后通过MultiSessionFactory来切换,而对于数据结构相同的部分,建立共同的sessionFactory和多个不同的dataSource然后通过MultiDataSource来切换就可以了。
还有的朋友问到这样的方案其事务处理和二级缓存的情况。这个方案是在spring框架下的解决方案,其事务处理的能力也是由spring的能力来决定的。目前spring要处理跨数据库的事务处理是通过JTA来实现的,这种方式在该方案中同样可以实现,朋友们可以试一试。另外,本方案能使用二级缓存吗?当然可以。对于MultiSessionFactory当然没有任何问题,它通过不同的sessionFactory分离开了不同的数据源和值对象,我们可以毫无顾忌地使用。对于MultiDataSource来说,就有点问题了。MultiDataSource使多个数据源使用共同的sessionFactory,因此它仿佛就是将多个数据源在逻辑上合并为一个数据源。正因为如此,我们需要保证对于同一个表在所有数据源中都要主键唯一。什么意思呢?数据源A和数据源B都有表T,如果数据源A中的表T拥有ID为001的一条数据,那么在数据源B的表T中就不能有ID为001的记录。如果你总是通过MultiDataSource来执行表的插入操作,并且使用uuid.hex生成主键,这当然不会有问题。但如果你有通过其它方式插入表的操作,你应当保证这样的唯一性。另外,对于查询的操作,缓存中存放的既可能是数据源A的数据,也可能是数据源B的数据,因此你应当对数据有一个规划。对于表T的数据,哪些应当插入到数据源A中,哪些应当插入到B中,应当有一个定义。假如是通过不同单位来决定插入哪个数据源,那么在查询数据源A的表T是,应当增加条件只查询数据源A应当有的单位而排除调其它单位。如此这样,你只要注意到这两个问题,你就可以放心大胆地使用二级缓存。
再析在spring框架中解决多数据源的问题的更多相关文章
- 如何在spring框架中解决多数据源的问题
在我们的项目中遇到这样一个问题:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库.我们以往在spring和hibernate框架中总是配置一个数据源,因而sessi ...
- Spring5源码解析-Spring框架中的单例和原型bean
Spring5源码解析-Spring框架中的单例和原型bean 最近一直有问我单例和原型bean的一些原理性问题,这里就开一篇来说说的 通过Spring中的依赖注入极大方便了我们的开发.在xml通过& ...
- 设计模式(二十一)——解释器模式(Spring 框架中SpelExpressionParser源码分析)
1 四则运算问题 通过解释器模式来实现四则运算,如计算 a+b-c 的值,具体要求 1) 先输入表达式的形式,比如 a+b+c-d+e, 要求表达式的字母不能重复 2) 在分别输入 a ,b, c, ...
- Spring框架中的定时器 使用和配置
Spring框架中的定时器 如何使用和配置 转载自:<Spring框架中的定时器 如何使用和配置>https://www.cnblogs.com/longqingyang/p/554543 ...
- 细说shiro之五:在spring框架中集成shiro
官网:https://shiro.apache.org/ 1. 下载在Maven项目中的依赖配置如下: <!-- shiro配置 --> <dependency> <gr ...
- 【Spring】8、Spring框架中的单例Beans是线程安全的么
看到这样一个问题:spring框架中的单例Beans是线程安全的么? Spring框架并没有对单例bean进行任何多线程的封装处理.关于单例bean的线程安全和并发问题需要开发者自行去搞定.但实际上, ...
- Spring框架中文件目录遍历漏洞 Directory traversal in Spring framework
官方给出的描述是Spring框架中报告了一个与静态资源处理相关的目录遍历漏洞.某些URL在使用前未正确加密,使得攻击者能够获取文件系统上的任何文件,这些文件也可用于运行SpringWeb应用程序的进程 ...
- Spring框架中 配置c3p0连接池 完成对数据库的访问
开发准备: 1.导入jar包: ioc基本jar jdbcTemplate基本jar c3p0基本jar 别忘了mysql数据库驱动jar 原始程序代码:不使用配置文件方式(IOC)生成访问数据库对象 ...
- Spring框架中ModelAndView、Model、ModelMap区别
原文地址:http://www.cnblogs.com/google4y/p/3421017.html SPRING框架中ModelAndView.Model.ModelMap区别 注意:如果方法 ...
随机推荐
- order by 指定顺序 mysql
LOCATE(substr,str), LOCATE(substr,str,pos) 第一个语法返回字符串str第一次出现的子串SUBSTR的位置.第二个语法返回第一次出现在字符串str的子串SUBS ...
- Segment Tree Query I & II
Segment Tree Query I For an integer array (index from 0 to n-1, where n is the size of this array), ...
- 玩转ubuntu FAQ
一.用wubi安装ubuntu的时候自动重新下载 1.双击ubuntu.ios让windows加载这个镜像 2.断开网络 二.安装其他程序时提示Error: Dependency is not sat ...
- javascript onload队列
2014年10月29日 10:40:14 function addLoadEvent(func){ var oldonload = window.onload; if (typeof window.o ...
- poj 1611 The Suspects 解题报告
题目链接:http://poj.org/problem?id=1611 题意:给定n个人和m个群,接下来是m行,每行给出该群内的人数以及这些人所对应的编号.需要统计出跟编号0的人有直接或间接关系的人数 ...
- 如何在Ubuntu下安装”.deb“、”.bin“、”.tar.gz“、”.tar.bz2“格式的软件包!
今天在Ubuntu11.10中安装Google chrome浏览器是遇到了问题,下载好的“.deb”格式的安装文件google-chrome-stable.deb双击后或者右键快捷菜单选择 Synap ...
- ubuntu tar 命令详细讲解
Ubuntu--tar命令 tar zxvf ut6410-android2.1.tgz tar zcvf ut6410-android2.1.tgz ut6410-android2.1/ tar - ...
- 浅谈c标签
今天用c便签,没有写好,以为jsp不兼容c标签,最后发现,原来是c标签写错了,好吧,简单写两句. jsp要用c标签,首先要导入c标签库,导入jstl.jar与standard.jar两个jar包,一般 ...
- 【USACO】milk3
倒牛奶的问题, 开始看感觉跟倒水的问题很像, 想直接找规律, 写个类似于循环取余的代码. 但后来发现不行,因为这道题有三个桶,水量也是有限制的.只好用模拟的方法把所有的情况都试一遍. 建一个state ...
- [Android Pro] Android签名与认证详细分析之一(CERT.RSA剖析)
转载自:http://www.thinksaas.cn/group/topic/335450/ 一.Android签名概述 我们已经知道的是:Android对每一个Apk文件都会进行签名,在Apk文件 ...