论坛首页 Java企业应用论坛

再析在spring框架中解决多数据源的问题

浏览 32870 次
精华帖 (2) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2007-06-18  
在前面我写了《如何在spring框架中解决多数据源的问题》,通过设计模式中的Decorator模式在spring框架中解决多数据源的问题,得到了许多网友的关注。在与网友探讨该问题的过程中,我发现我的方案并不完善,它只解决了一部分问题。

总结多数据源的问题,其实它需要分为以下三种情况:各个数据源的数据结构不同、各个数据源的数据结构相同、各个数据源的数据结构部分相同又有部分不同。对于第二种情况,各个数据源的数据结构相同,我们使用一个sessionFactory,而在sessionFactory中通过MultiDataSource来动态切换数据源,应当是一个不错的方案,既解决了多个sessionFactory对相同的值对象重复装载对内存的浪费,又使数据源的切换对客户程序透明,简化了代码的实现和对客户程序的影响。但是,对于第一种情况,各个数据源的数据结构不同,运用这样的方案存在潜在风险。

对于各个数据源的数据结构不同的情况,使用一个sessionFactory而在这个sessionFactory中动态切换数据源,可能造成数据访问的张冠李戴。譬如,数据源A有表T而数据源B没有,可能造成客户程序在访问表T的时候却尝试去连接数据源B,因为客户程序访问哪个数据源是在程序运行期间由客户程序决定的,因此这样的错误是很难发现的。也许客户程序的一个不经意的错误就可能造成错误。解决这个问题的方法有两个:一是严格要求客户程序不要写错,这当然是可以做到的,但作为框架设计者,另一个解决方法是在框架中就避免出现这样的情况。因此我祭出了MultiSessionFactory的方案来解决各个数据源的数据结构不同的多数据源问题。

问题的分析

MultiDataSource的方案一样,MultiSessionFactory同样是在spring框架下调用ApplicationContextgetBean()方法而不会另外创建beanFacoty,也同样使用Decorator模式来处理切换的问题。MultiSessionFactory的对象关系如图:

在该方案中,SessionFactory就是Hibernateorg.hibernate.SessionFactory接口,Decorator就是MultiSessionFactorySessionFactory1SessionFactory2往往是springorg.springframework.orm.hibernate3.LocalSessionFactoryBean。细心的朋友可能会注意,实际上LocalSessionFactoryBean并不是SessionFactory的实现,这个方案是否有问题呢?这个问题其实也一直困扰了我好久,最后我发现,我们通过ApplicationContextgetBean()得到一个LocalSessionFactoryBean的时候其实并不是真正地得到了它,而是得到了一个SessionFactory,因为springLocalSessionFactoryBean重写了getObject(),使其返回的是SessionFactory。一个简单的明证就是,HibernateDaoSupportsessionFactory属性的类型是SessionFactory,而我们在spring配置的时候注入的却是LocalSessionFactoryBean

方案的实现

在整个这个方案中,我们需要实现的只有MultiSessionFactory类和我们可爱的Spserver,总共就两个类,然后呢就是一些spring的配置,就完成了。

MultiSessionFactory实现了SessionFactory,同时为了得到AplicationContext而实现了ApplicationContextAwareMultiSessionFactory的代码如下:

java 代码
  1. public class MultiSessionFactory implements SessionFactory, ApplicationContextAware {   
  2.     private static final long serialVersionUID = 2064557324203496378L;   
  3.     private static final Log log = LogFactory.getLog(MultiSessionFactory.class);   
  4.     private ApplicationContext applicationContext = null;   
  5.     private SessionFactory sessionFactory = null;   
  6.     public ApplicationContext getApplicationContext() {   
  7.         return applicationContext;   
  8.     }   
  9.     public void setApplicationContext(ApplicationContext applicationContext) {   
  10.        this.applicationContext = applicationContext;   
  11.     }   
  12.     public SessionFactory getSessionFactory(String sessionFactoryName) {   
  13.        log.debug("sessionFactoryName:"+sessionFactoryName);   
  14.        try{   
  15.            if(sessionFactoryName==null||sessionFactoryName.equals("")){   
  16.               return sessionFactory;   
  17.            }   
  18.            return (SessionFactory)this.getApplicationContext().getBean(sessionFactoryName);   
  19.        }catch(NoSuchBeanDefinitionException ex){   
  20.            throw new DaoException("There is not the sessionFactory 
  21.        }   
  22.     }   
  23.   
  24.     public SessionFactory getSessionFactory() {   
  25.        String sessionFactoryName = SpObserver.getSp();   
  26.        return getSessionFactory(sessionFactoryName);   
  27.     }   
  28.   
  29.     public void setSessionFactory(SessionFactory sessionFactory) {   
  30.        this.sessionFactory = sessionFactory;   
  31.     }   
  32.   
  33.     // SessionFactory接口需要实现的方法   
  34.   
  35. ......   
  36.   
  37. }  

MultiSessionFactory的完整代码见我提供的附件。setSessionFactory()实际上是设定的默认sessionFactory,它在spring装载的时候调用,其对应的数据源应当是主数据源,即项目初始化中需要读取初始化数据的数据源。在任何多数据源项目中,都应当有一个存放初始化数据、系统维护数据、用户权限数据的数据源,这就是主数据源。因此MultiSessionFactory的配置应当这样写:

xml 代码
  1. <bean id="sessionFactory" class="com.htxx.service.dao.MultiSessionFactory">  
  2.     <property name="sessionFactory"><ref bean="hostSessionFactory"/>property> 
  3. >  

SpServer的写法与《如何在spring框架中解决多数据源的问题》中的一样,我就不再累赘了。

另外,在spring配置中配置多个数据源,每个数据源对应一个sessionFactory,这个对应的sessionFactory中的值对象应当是该数据源的值对象。客户程序在执行数据访问前,通过调用SpServerputSp()方法,告诉MultiSessionFactory需要切换到哪个sessionFactory,然后执行数据访问。这样,不同数据源的值对象通过放在不同的sessionFactory中,避免了张冠李戴的情况。具体的示例见附件的MultiSessionFactoryTest

另外的方案

也许有些朋友对以上方案还不满意,因为在执行数据访问前毕竟还要多做一步指定sessionFactory的工作。实际上,对于各个数据源的数据结构不同的项目,一个值对象应当使用哪个数据源有一个非常确定的对应关系。如果通过配置文件将值对象与它的sessionFactory对应起来,那么我们在执行数据访问的时候传递的是哪个值对象,MultiSessionFactory马上就可以去找到对应的sessionFactory。这个方案你可以通过AOP来制作一个拦截器拦截所有诸如save()delete()get()load()等方法来实现,也可以扩展HibernateDaoSupport来实现。这样的方案使客户程序甚至都不用知道他是在操作的一个多数据源系统。当然,这个方案感兴趣的朋友可以自己去实现。

另外,在这个方案中的核心是运用Decorator设计模式来解决切换sessionFactory的目的,即MultiSessionFactory的实现。至于通过什么方式来通知MultiSessionFactory应当切换到哪个SessionFactory,可以根据不同项目的情况自由选择。我在这里给大家提供了通过SpOberver和建立值对象与sessionFactory关系的配置文件这两个方案,你也可以有自己的方案解决。

第三种情况的解决方案

前面我已经给出了第一种和第二种情况的解决方案:各个数据源的数据结构不同的情况用MultiSessionFactory解决;各个数据源的数据结构相同的情况用MultiDataSource解决。那么第三种情况,各个数据源的数据结构部分相同又有部分不同,又应当如何解决呢?当然是将MultiSessionFactoryMultiDataSource结合起来解决。对于数据结构不同的部分,其分别创建各自的sessionFactory然后通过MultiSessionFactory来切换,而对于数据结构相同的部分,建立共同的sessionFactory和多个不同的dataSource然后通过MultiDataSource来切换就可以了。

还有的朋友问到这样的方案其事务处理和二级缓存的情况。这个方案是在spring框架下的解决方案,其事务处理的能力也是由spring的能力来决定的。目前spring要处理跨数据库的事务处理是通过JTA来实现的,这种方式在该方案中同样可以实现,朋友们可以试一试。另外,本方案能使用二级缓存吗?当然可以。对于MultiSessionFactory当然没有任何问题,它通过不同的sessionFactory分离开了不同的数据源和值对象,我们可以毫无顾忌地使用。对于MultiDataSource来说,就有点问题了。MultiDataSource使多个数据源使用共同的sessionFactory,因此它仿佛就是将多个数据源在逻辑上合并为一个数据源。正因为如此,我们需要保证对于同一个表在所有数据源中都要主键唯一。什么意思呢?数据源A和数据源B都有表T,如果数据源A中的表T拥有ID001的一条数据,那么在数据源B的表T中就不能有ID001的记录。如果你总是通过MultiDataSource来执行表的插入操作,并且使用uuid.hex生成主键,这当然不会有问题。但如果你有通过其它方式插入表的操作,你应当保证这样的唯一性。另外,对于查询的操作,缓存中存放的既可能是数据源A的数据,也可能是数据源B的数据,因此你应当对数据有一个规划。对于表T的数据,哪些应当插入到数据源A中,哪些应当插入到B中,应当有一个定义。假如是通过不同单位来决定插入哪个数据源,那么在查询数据源A的表T是,应当增加条件只查询数据源A应当有的单位而排除调其它单位。如此这样,你只要注意到这两个问题,你就可以放心大胆地使用二级缓存。
  • example.rar (16.4 KB)
  • 描述: 示例文件
  • 下载次数: 4165
  • 大小: 50.5 KB
   发表时间:2007-08-25  
怎么最近没有新做了,大家都等着呢
0 请登录后投票
   发表时间:2007-08-25  
怎么最近没有新做了,大家都等着呢


可能太忙了吧?当上准爸爸了,恭喜恭喜先!
0 请登录后投票
   发表时间:2007-08-28  
最近刚写了2篇关于DWR的文章,强烈推荐大家试试dwr+spring+hibernate框架,有意外惊喜哟。可惜这2篇我正在给它们写示例,所以还没有贴出来。
0 请登录后投票
   发表时间:2007-11-20  
还是不是很明白你的多数据源的事物是怎么解决的?当有多个数据源的情况,用户如果一次超作只访问你一个数据库,那么事务是可以控制的,但是我的疑问是如果一次超作要涉及到多个数据源呢,事务怎么保证呢?比如有两个数据源,比如数据源A 和数据源B , 我在一个方法里面要在 A和B里面都插入一条数据,并且保证失败都失败,成功都成功,但是看你的列子好像都没有解决这个问题?能详细说下吗?
0 请登录后投票
   发表时间:2007-11-20  
wcjmaomao 写道
还是不是很明白你的多数据源的事物是怎么解决的?当有多个数据源的情况,用户如果一次超作只访问你一个数据库,那么事务是可以控制的,但是我的疑问是如果一次超作要涉及到多个数据源呢,事务怎么保证呢?比如有两个数据源,比如数据源A 和数据源B , 我在一个方法里面要在 A和B里面都插入一条数据,并且保证失败都失败,成功都成功,但是看你的列子好像都没有解决这个问题?能详细说下吗?

其实一直以来我都有一个误解,后来经过一番仔细研究以后才发现弄明白,那就是,在hibernate中的事务处理与session以及sessionFactory没有直接的关联。hibernate使用session进行各种操作,但这些操作只是模拟地进行数据库操作,并没有真正去操作。当它要真正去执行数据库操作的时候,它实际上是将这些操作交给hibernate的事务处理部分进行处理。因此,在我的方案中为你提供了多数据源处理,而其事务处理只与你配置的事务处理方式有关。你这里的操作是跨数据库的事务处理,应当使用JTA事务处理方式。这方面的配置你可以在网上搜寻相关文档。
0 请登录后投票
   发表时间:2007-11-21  
想了好久,也查了好多资料,问了好多人,涉及到多数据库超作事务的时候,估计除了jta,也没有什么其他的方法。
0 请登录后投票
   发表时间:2008-05-08  
maserkinger 写道
实现单数据源多用户下的数据独立性

如果我是用oracle,但我只有一个数据源,我现在建了多个schema,那能否动态改变sessionFactory中的default_schema设置,来指向特定的schema查询,如果可以,怎么改变,好象无法改变LocalSessionFactoryBean,因为这是spring代理过了?

首先在spring里为每一个schema配置一个dataSource,然后像我的博客写的那样,配置一个multiDataSource,将sessionFactory指向multiDataSource,spring的配置工作就完成了。剩下的工作就是在用户登录的时候,告诉系统他应当访问那个数据源(如同博客上说的那样设计,你也可以自己设计其它的方式,总之告诉系统他访问的数据源)。
0 请登录后投票
   发表时间:2008-05-09  
不知道lz在切换使用多数据源时有没有遇到过下面的问题,那时候可折磨了我一个多月
http://www.iteye.com/post/411231
0 请登录后投票
   发表时间:2008-05-12  
javaboy2006 写道
不知道lz在切换使用多数据源时有没有遇到过下面的问题,那时候可折磨了我一个多月
http://www.iteye.com/post/411231

我是用的这个驱动com.microsoft.sqlserver.jdbc.SQLServerDriver,很稳定没有发现什么问题,你可以在微软发布的sqljdbc.jar中找到。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics