`

用设计模式解决多数据源问题

阅读更多
无意间看了网游一片解决多数据源方法的博文,觉得不错体现了设计模式的功能,转一下下

------------------------------------------------------------------------------------
在我们的项目中遇到这样一个问题:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。我们以往在spring和hibernate框架中总是配置一个数据源,因而sessionFactory的dataSource属性总是指向这个数据源并且恒定不变,所有DAO在使用sessionFactory的时候都是通过这个数据源访问数据库。但是现在,由于项目的需要,我们的DAO在访问sessionFactory的时候都不得不在多个数据源中不断切换,问题就出现了:如何让sessionFactory在执行数据持久化的时候,根据客户的需求能够动态切换不同的数据源?我们能不能在spring的框架下通过少量修改得到解决?是否有什么设计模式可以利用呢?

问题的分析

我首先想到在spring的applicationContext中配置所有的dataSource。这些dataSource可能是各种不同类型的,比如不同的数据库:Oracle、SQL Server、MySQL等,也可能是不同的数据源:比如apache 提供的org.apache.commons.dbcp.BasicDataSource、spring提供的org.springframework.jndi.JndiObjectFactoryBean等。然后sessionFactory根据客户的每次请求,将dataSource属性设置成不同的数据源,以到达切换数据源的目的。

但是,我很快发现一个问题:当多用户同时并发访问数据库的时候会出现资源争用的问题。这都是“单例模式”惹的祸。众所周知,我们在使用spring框架的时候,在beanFactory中注册的bean基本上都是采用单例模式,即spring在启动的时候,这些bean就装载到内存中,并且每个bean在整个项目中只存在一个对象。正因为只存在一个对象,对象的所有属性,更准确说是实例变量,表现得就如同是个静态变量(实际上“静态”与“单例”往往是非常相似的两个东西,我们常常用“静态”来实现“单例”)。拿我们的问题来说,sessionFactory在整个项目中只有一个对象,它的实例变量dataSource也就只有一个,就如同一个静态变量一般。如果不同的用户都不断地去修改dataSource的值,必然会出现多用户争用一个变量的问题,对系统产生隐患。

通过以上的分析,解决多数据源访问问题的关键,就集中在sessionFactory在执行数据持久化的时候,能够通过某段代码去根据客户的需要动态切换数据源,并解决资源争用的问题。

问题的解决
(一)   采用Decorator设计模式
要解决这个问题,我的思路锁定在了这个dataSource上了。如果sessionFactory指向的dataSource可以根据客户的需求去连接客户所需要的真正的数据源,即提供动态切换数据源的功能,那么问题就解决了。那么我们怎么做呢?去修改那些我们要使用的dataSource源码吗?这显然不是一个好的方案,我们希望我们的修改与原dataSource代码是分离的。根据以上的分析,使用GoF设计模式中的Decorator模式(装饰者模式)应当是我们可以选择的最佳方案。

什么是“Decorator模式”?简单点儿说就是当我们需要修改原有的功能,但我们又不愿直接去修改原有的代码时,设计一个Decorator套在原有代码外面。当我们使用Decorator的时候与原类完全一样,当Decorator的某些功能却已经修改为了我们需要修改的功能。Decorator模式的结构如图。

我们本来需要修改图中所有具体的Component类的一些功能,但却并不是去直接修改它们的代码,而是在它们的外面增加一个Decorator。Decorator与具体的Component类都是继承的AbstractComponent,因此它长得和具体的Component类一样,也就是说我们在使用Decorator的时候就如同在使用ConcreteComponentA或者ConcreteComponentB一样,甚至那些使用ConcreteComponentA或者ConcreteComponentB的客户程序都不知道它们用的类已经改为了Decorator,但是Decorator已经对具体的Component类的部分方法进行了修改,执行这些方法的结果已经不同了。
(二)    设计MultiDataSource类
现在回到我们的问题,我们需要对dataSource的功能进行变更,但又不希望修改dataSource中的任何代码。我这里指的dataSource是所有实现javax.sql.DataSource接口的类,我们常用的包括apache 提供的org.apache.commons.dbcp.BasicDataSource、spring提供的org.springframework.jndi.JndiObjectFactoryBean等,这些类我们不可能修改它们本身,更不可能对它们一个个地修改以实现动态分配数据源的功能,同时,我们又希望使用dataSource的sessionFactory根本就感觉不到这样的变化。Decorator模式就正是解决这个问题的设计模式。

首先写一个Decorator类,我取名叫MultiDataSource,通过它来动态切换数据源。同时在配置文件中将sessionFactory的dataSource属性由原来的某个具体的dataSource改为MultiDataSource。如图:

对比原Decorator模式,AbstractComponent是一个抽象类,但在这里我们可以将这个抽象类用接口来代替,即DataSource接口,而ConcreteComponent就是那些DataSource的实现类,如BasicDataSource、JndiObjectFactoryBean等。MultiDataSource封装了具体的dataSource,并实现了数据源动态切换:

java 代码

public class MultiDataSource implements DataSource {   
   private DataSource dataSource = null;   
   public MultiDataSource(DataSource dataSource){   
       this.dataSource = dataSource;   
   }   
       /* (non-Javadoc)  
        * @see javax.sql.DataSource#getConnection()  
        */  
   public Connection getConnection() throws SQLException {   
        return getDataSource().getConnection();   
   }   
     //其它DataSource接口应当实现的方法   
     
   public DataSource getDataSource(){   
      return this.dataSource;   
   }   
  }   
  public void setDataSource(DataSource dataSource) {   
      this.dataSource = dataSource;   
  }   
}   


客户在发出请求的时候,将dataSourceName放到request中,然后把request中的数据源名通过调用new MultiDataSource(dataSource)时可以告诉MultiDataSource客户需要的数据源,就可以实现动态切换数据源了。但细心的朋友会发现这在单例的情况下就是问题的,因为MultiDataSource在系统中只有一个对象,它的实例变量dataSource也只有一个,就如同一个静态变量一般。正因为如此,单例模式让许多设计模式都不得不需要更改,这将在我的《“单例”更改了我们的设计模式》中详细讨论。那么,我们在单例模式下如何设计呢?

(三)  单例模式下的MultiDataSource
在单例模式下,由于我们在每次调用MultiDataSource的方法的时候,dataSource都可能是不同的,所以我们不能将dataSource放在实例变量dataSource中,最简单的方式就是在方法getDataSource()中增加参数,告诉MultiDataSource我到底调用的是哪个dataSource:
public DataSource getDataSource(String dataSourceName){   
		log.debug("dataSourceName:"+dataSourceName);   
		try{   
			if(dataSourceName==null||dataSourceName.equals("")){   
				return this.dataSource;   
            }   
			return (DataSource)this.applicationContext.getBean(dataSourceName);   
		}catch(NoSuchBeanDefinitionException ex){   
			throw new DaoException("There is not the dataSource 
		}   
	} 

值得一提的是,我需要的数据源已经都在spring的配置文件中注册,dataSourceName就是其对应的id。
xml 代码
 <bean id="dataSource1"  
     class="org.apache.commons.dbcp.BasicDataSource">  
     <property name="driverClassName">  
         <value>oracle.jdbc.driver.OracleDrivervalue>  
     property> 
     ......   
 bean>  
 <bean id="dataSource2"  
     class="org.apache.commons.dbcp.BasicDataSource">  
     <property name="driverClassName">  
         <value>oracle.jdbc.driver.OracleDrivervalue> 
     property>   
     ......   
 bean> 

为了得到spring的ApplicationContext,MultiDataSource类必须实现接口org.springframework.context.ApplicationContextAware,并且实现方法:
java 代码
private ApplicationContext applicationContext = null;   
     public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {   
         this.applicationContext = applicationContext;   
     } 

如此这样,我就可以通过this.applicationContext.getBean(dataSourceName)得到dataSource了。
(四)    通过线程传递dataSourceName
查看以上设计,MultiDataSource依然无法运行,因为用户在发出请求时,他需要连接什么数据库,其数据源名是放在request中的,要将request中的数据源名传给MultiDataSource,需要经过BUS和DAO,也就是说为了把数据源名传给MultiDataSource,BUS和DAO的所有方法都要增加dataSourceName的参数,这是我们不愿看到的。写一个类,通过线程的方式跳过BUS和DAO直接传递给MultiDataSource是一个不错的设计:


java 代码
    public class SpObserver {   
        private static ThreadLocal local = new ThreadLocal();   
        public static void putSp(String sp) {   
            local.set(sp);   
        }   
        public static String getSp() {   
            return (String)local.get();   
        }   
    }   

做一个filter,每次客户发出请求的时候就调用SpObserver.petSp(dataSourceName),将request中的dataSourceName传递给SpObserver对象。最后修改MultiDataSource的方法getDataSource():
java 代码
 public DataSource getDataSource(){   
      String sp = SpObserver.getSp();   
      return getDataSource(sp);   
 }  

完整的MultiDataSource代码在附件中。
(五)    动态添加数据源
通过以上方案,我们解决了动态分配数据源的问题,但你可能提出疑问:方案中的数据源都是配置在spring的ApplicationContext中,如果我在程序运行过程中动态添加数据源怎么办?这确实是一个问题,而且在我们的项目中也确实遇到。spring的ApplicationContext是在项目启动的时候加载的。加载以后,我们如何动态地加载新的bean到ApplicationContext中呢?我想到如果用spring自己的方法解决这个问题就好了。所幸的是,在查看spring的源代码后,我找到了这样的代码,编写了DynamicLoadBean类,只要调用loadBean()方法,就可以将某个或某几个配置文件中的bean加载到ApplicationContext中(见附件)。不通过配置文件直接加载对象,在spring的源码中也有,感兴趣的朋友可以自己研究。
(六)    在spring中配置
在完成了所有这些设计以后,我最后再唠叨一句。我们应当在spring中做如下配置:
 <bean id="dynamicLoadBean" class="com.htxx.service.dao.DynamicLoadBean">bean>  
 <bean id="dataSource" class="com.htxx.service.dao.MultiDataSource">  
         <property name="dataSource">  
             <ref bean="dataSource1" />  
         property>  
     bean>  
     <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
         <property name="dataSource">  
             <ref bean="dataSource" />  
         property>  
         ......   
     bean>

其中dataSource属性实际上更准确地说应当是defaultDataSource,即spring启动时以及在客户没有指定数据源时应当指定的默认数据源。

该方案的优势
以上方案与其它方案相比,它有哪些优势呢?

首先,这个方案完全是在spring的框架下解决的,数据源依然配置在spring的配置文件中,sessionFactory依然去配置它的dataSource属性,它甚至都不知道dataSource的改变。唯一不同的是在真正的dataSource与sessionFactory之间增加了一个MultiDataSource。

其次,实现简单,易于维护。这个方案虽然我说了这么多东西,其实都是分析,真正需要我们写的代码就只有MultiDataSource、SpObserver两个类。MultiDataSource类真正要写的只有getDataSource()和getDataSource(sp)两个方法,而SpObserver类更简单了。实现越简单,出错的可能就越小,维护性就越高。

最后,这个方案可以使单数据源与多数据源兼容。这个方案完全不影响BUS和DAO的编写。如果我们的项目在开始之初是单数据源的情况下开发,随着项目的进行,需要变更为多数据源,则只需要修改spring配置,并少量修改MVC层以便在请求中写入需要的数据源名,变更就完成了。如果我们的项目希望改回单数据源,则只需要简单修改配置文件。这样,为我们的项目将增加更多的弹性。

特别说明:实例中的DynamicLoadBean在web环境下运行会出错,需要将类中AbstractApplicationContext改为 org.springframework.context.ConfigurableApplicationContext。

http://www.iteye.com/topic/91667
---------------------------------------------------------------------------------
  • 大小: 12.1 KB
  • 大小: 16.4 KB
  • 大小: 9.7 KB
分享到:
评论

相关推荐

    spring+hibernate解决多数据源问题3.pdf

    综上所述,解决多数据源问题主要涉及对Spring与Hibernate整合配置的理解、装饰器设计模式的应用,以及动态数据源管理的实现。通过将这些知识点结合起来,开发人员可以灵活地为复杂业务需求下的应用配置和管理多个...

    如何在spring框架中解决多数据源的问题

    为了解决上述问题,本文将介绍一种利用Spring框架结合Decorator设计模式来实现多数据源动态切换的方法。 #### 解决方案设计 ##### 1. 设计思路 - **多数据源配置**:在Spring的`applicationContext`中配置所有的...

    简单数据源演示示例,基于设计模式

    设计模式则是软件工程中经过验证的最佳实践,用于解决常见的编程问题。 描述中的“NULL”没有提供额外信息,但我们可以从标签“源码”和“工具”推断,这个压缩包可能包含一个简单的Java实现,用于展示如何创建和...

    springmvc_mybatis_多数据源

    总结来说,"springmvc_mybatis_多数据源"项目展示了如何在SpringMVC和MyBatis框架下有效地管理和使用多个数据源。这涉及到Spring的事务管理、数据源路由、MyBatis的配置等多个方面,对于理解和实践企业级应用开发中...

    设计模式之我见源代码

    设计模式是软件工程中的一种重要思想,它是在特定情境下,为解决常见问题而提炼出的可复用的解决方案模板。这些模式描述了在特定上下文中如何进行对象间的交互和结构,以达到良好的软件设计原则,如开闭原则、单一...

    装饰模式 切换 ibatis 多数据源

    装饰模式是一种设计模式,它允许在不修改原有对象的情况下,通过添加新的行为或扩展功能来改变对象。在Java等面向对象编程语言中,装饰模式通常通过继承和组合来实现。在IT领域,尤其是在数据库访问层,装饰模式经常...

    Spring多数据源解决方案

    Spring多数据源解决方案是针对大型应用中数据分片和分布式数据库管理的需求而设计的一种策略。在这样的场景下,为了提高系统的可扩展性和性能,数据通常会被分散到多个数据库实例上。这种架构如图1所示,每个数据库...

    spring boot mybatis多数据源最简解决方案

    3. **配置多数据源路由**:引入`AbstractRoutingDataSource`抽象类,创建自定义的数据源路由类,这个类会根据某些条件(如事务类型、方法注解等)动态选择使用哪个数据源。 4. **MyBatis配置**:为每个数据源创建...

    基于Spring多数据源实例

    在IT行业中,数据库是系统的核心组成部分,特别是...通过理解上述步骤和原理,开发者可以更好地设计和实施适用于各种业务场景的多数据源解决方案。在实际操作中,应根据项目需求灵活调整,以达到最佳的性能和可维护性。

    java 多数据源

    总之,多数据源是解决复杂数据库架构问题的重要手段,通过Spring、SpringMVC和MyBatis的组合,我们可以灵活地管理和操作多个数据库,提升系统的稳定性和扩展性。在实际开发中,需要根据具体需求进行详细设计和优化。

    基于设计模式构建数据访问中间件

    具体来说,通过采用工厂方法模式、抽象工厂模式等设计模式,中间件能够更好地支持多数据源和多样化的数据处理需求。此外,策略模式和观察者模式的应用也为数据访问中间件带来了更多的可能性。总之,基于设计模式构建...

    《C#设计模式》源代码

    设计模式是软件工程中经过实践验证的解决常见问题的最佳方案,它们是经验的结晶,能够帮助程序员在面对复杂系统设计时有条不紊地进行。 源代码文件包含了书中各个设计模式的实际实现,这对于学习和理解设计模式有着...

    研磨设计模式全部源代码

    设计模式是软件工程中的一种最佳实践,它是在特定上下文中解决常见问题的模板。这个压缩包“研磨设计模式全部源代码”包含了多种设计模式的实现,这些模式可以帮助开发者写出更可维护、可扩展和可复用的代码。下面将...

    多数据源事务解决方案,单应用多数据库保证事务

    在IT行业中,尤其是在大型分布式系统的设计与开发中,多数据源事务处理是一个常见的挑战。当一个应用程序需要连接并操作多个数据库以实现业务需求时,如何保证事务的一致性、隔离性和持久性,就显得尤为重要。本文将...

    Java多线程设计模式_清晰完整PDF版 Java多线程设计模式源代码

    本资料集包含了清晰完整的PDF版书籍和源代码,为学习和理解Java多线程设计模式提供了丰富的素材。 首先,我们要理解多线程的核心概念。在Java中,线程是程序执行的最小单位,每个线程都有自己的程序计数器、虚拟机...

    Spring+SpringMvc+MybatisPlus+Aop(自定义注解)动态切换数据源

    本项目“Spring+SpringMvc+MybatisPlus+Aop(自定义注解)动态切换数据源”正是针对这一需求提供的一种解决方案。下面将详细介绍这个项目中的关键技术点和实现原理。 首先,Spring框架是Java企业级应用开发的核心...

    c#设计模式 设计模式源代码

    在软件开发领域,设计模式是一种经过时间和实践验证的解决方案,用于解决常见的编程问题。C#设计模式是将这些通用解决方案应用于C#编程语言的具体实践。本文将深入探讨C#中的几种主要设计模式,并通过源代码示例进行...

    多数据源应用

    在IT行业中,多数据源应用是一种常见的架构设计模式,它涉及到如何在一个系统中同时管理多个不同的数据源。这种设计在大型分布式系统、企业级应用以及需要处理多种数据类型的场景中非常常见。本篇将深入探讨多数据源...

    设计模式源码.zip

    设计模式是软件开发中的重要概念,它是一种在特定情境下解决常见问题的模板,为软件设计提供可重用的解决方案。这些模式已经被广泛接受并应用于各种编程语言中,包括C#。本压缩包"设计模式源码.zip"包含了基于C#的...

Global site tag (gtag.js) - Google Analytics