`
xinklabi
  • 浏览: 1587834 次
  • 性别: Icon_minigender_1
  • 来自: 吉林
文章分类
社区版块
存档分类
最新评论

spring框架中多数据源创建加载并且实现动态切换的配置实例代码(转)

 
阅读更多

下面是转载的一个Spring动态切换数据源的实现,原网站评论给出了Spring已经对此进行实现了,可以参考:http://docs.spring.io/spring/docs/3.0.x/api/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSource.html

 

spring框架中多数据源创建加载并且实现动态切换的配置实例代码

博客分类:

原创不易,转载请注明出处:spring框架中多数据源创建加载并且实现动态切换的配置实例代码

代码下载地址:http://www.zuidaima.com/share/1774074130205696.htm

在我们的项目中遇到这样一个问题:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。我们以往在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模式的结构如图。

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。如图:

 

MultiDataSource

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

java 代码

Java代码  收藏代码
  1. public class MultiDataSource implements DataSource {     
  2.     private DataSource dataSource = null;     
  3. public MultiDataSource(DataSource dataSource){     
  4.         this.dataSource = dataSource;     
  5.     }     
  6.     /* (non-Javadoc)   
  7.      * @see javax.sql.DataSource#getConnection()   
  8.      */    
  9.     public Connection getConnection() throws SQLException {     
  10.         return getDataSource().getConnection();     
  11.     }     
  12.     //其它DataSource接口应当实现的方法     
  13.     
  14.     public DataSource getDataSource(){     
  15.         return this.dataSource;     
  16.         }     
  17.     }     
  18.     public void setDataSource(DataSource dataSource) {     
  19.         this.dataSource = dataSource;     
  20.     }     
  21. }     

 

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

 

  • 单例模式下的MultiDataSource

在单例模式下,由于我们在每次调用MultiDataSource的方法的时候,dataSource都可能是不同的,所以我们不能将dataSource放在实例变量dataSource中,最简单的方式就是在方法getDataSource()中增加参数,告诉MultiDataSource我到底调用的是哪个dataSource:

 

java 代码

Java代码  收藏代码
  1. public DataSource getDataSource(String dataSourceName){     
  2.         log.debug("dataSourceName:"+dataSourceName);     
  3.         try{     
  4.             if(dataSourceName==null||dataSourceName.equals("")){     
  5.                 return this.dataSource;     
  6.             }     
  7.             return (DataSource)this.applicationContext.getBean(dataSourceName);     
  8.         }catch(NoSuchBeanDefinitionException ex){     
  9.             throw new DaoException("There is not the dataSource   
  10.         }     
  11.     }     

 

值得一提的是,我需要的数据源已经都在spring的配置文件中注册,dataSourceName就是其对应的id。

 

xml 代码

Xml代码  收藏代码
  1. <bean id="dataSource1"    
  2.     class="org.apache.commons.dbcp.BasicDataSource">    
  3.     <property name="driverClassName">    
  4.         <value>oracle.jdbc.driver.OracleDrivervalue>    
  5.     property>   
  6.     ......     
  7. bean>    
  8. <bean id="dataSource2"    
  9.     class="org.apache.commons.dbcp.BasicDataSource">    
  10.     <property name="driverClassName">    
  11.         <value>oracle.jdbc.driver.OracleDrivervalue>   
  12.     property>     
  13.     ......     
  14. bean>     

为了得到spring的ApplicationContext,MultiDataSource类必须实现接口org.springframework.context.ApplicationContextAware,并且实现方法:

java 代码

Java代码  收藏代码
  1. private ApplicationContext applicationContext = null;     
  2. public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {     
  3.         this.applicationContext = applicationContext;     
  4.     }    

 

 

如此这样,我就可以通过this.applicationContext.getBean(dataSourceName)得到dataSource了。

 

  • 通过线程传递dataSourceName

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

 

MultiDataSource

java 代码

Java代码  收藏代码
  1. public class SpObserver {     
  2.     private static ThreadLocal local = new ThreadLocal();     
  3.     public static void putSp(String sp) {     
  4.         local.set(sp);     
  5.     }     
  6.     public static String getSp() {     
  7.         return (String)local.get();     
  8.     }     
  9. }     

 

做一个filter,每次客户发出请求的时候就调用SpObserver.petSp(dataSourceName),将request中的dataSourceName传递给SpObserver对象。最后修改MultiDataSource的方法getDataSource():

 

java 代码

Java代码  收藏代码
  1. public DataSource getDataSource(){     
  2.         String sp = SpObserver.getSp();     
  3.         return getDataSource(sp);     
  4.     }     

 

完整的MultiDataSource代码在附件中。

 

  • 动态添加数据源

通过以上方案,我们解决了动态分配数据源的问题,但你可能提出疑问:方案中的数据源都是配置在spring的ApplicationContext中,如果我在程序运行过程中动态添加数据源怎么办?这确实是一个问题,而且在我们的项目中也确实遇到。spring的ApplicationContext是在项目启动的时候加载的。加载以后,我们如何动态地加载新的bean到ApplicationContext中呢?我想到如果用spring自己的方法解决这个问题就好了。所幸的是,在查看spring的源代码后,我找到了这样的代码,编写了DynamicLoadBean类,只要调用loadBean()方法,就可以将某个或某几个配置文件中的bean加载到ApplicationContext中(见附件)。不通过配置文件直接加载对象,在spring的源码中也有,感兴趣的朋友可以自己研究。

 

 

  • 在spring中配置

在完成了所有这些设计以后,我最后再唠叨一句。我们应当在spring中做如下配置:

 

xml 代码

Xml代码  收藏代码
  1. <bean id="dynamicLoadBean" class="com.htxx.service.dao.DynamicLoadBean">bean>    
  2. <bean id="dataSource" class="com.htxx.service.dao.MultiDataSource">    
  3.         <property name="dataSource">    
  4.             <ref bean="dataSource1" />    
  5.         property>    
  6.     bean>    
  7.     <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">    
  8.         <property name="dataSource">    
  9.             <ref bean="dataSource" />    
  10.         property>    
  11.         ......     
  12.     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://dl.iteye.com/topics/download/11b45eb4-9732-4fcb-85de-dd88238958b2

相关博客:再析在spring框架中解决多数据源的问题

分享到:
评论

相关推荐

    spring 动态切换数据源

    在Spring框架中,动态切换数据源是一项重要的功能,它允许应用程序根据业务需求在多个数据库之间灵活切换。这一特性对于多租户系统、读写分离、分布式数据库等场景尤其有用。以下将详细介绍如何实现Spring的动态数据...

    SpringBoot整合Sharding-JDBC,实现从数据库加载sharding-jdbc数据源,实现多种数据库数据源切换,数据库方言动态切换

    这可以通过监听数据库表的变化,获取最新的数据源配置,然后通过Spring的`DataSourceProxy`或`AbstractRoutingDataSource`实现动态数据源切换。 3. **数据库方言动态切换** Sharding-JDBC支持多种数据库,如MySQL...

    SpringBoot操作多数据源(JPA+JdbcTemplate)

    在Spring Boot中配置多数据源,我们需要创建两个或更多的数据源配置,每个数据源对应一个数据库。以下是一个基本的配置示例: ```yaml spring: datasource: primary: url: jdbc:mysql://localhost:3306/db1 ...

    springboot + mybatis(通用mapper) + HikariCP(比durid更快)多数据源

    开发者可以创建多个 `DataSource` 实例,并通过 `@Primary` 或 `@Qualifier` 注解来指定主要数据源或在需要时注入特定数据源。`SpringBoot` 的 `AbstractRoutingDataSource` 类可以进一步帮助我们动态切换数据源,...

    Spring MySQL多数据源

    Spring框架提供了丰富的支持来帮助开发者处理这些需求,本项目聚焦于“Spring MySQL多数据源”这一主题,旨在教你如何在Spring应用中配置和管理多个MySQL数据库。 首先,Spring框架允许我们通过Java配置或XML配置来...

    proxool 多数据源动态切换,刚刚一网友问我,顺便写的一个demo

    在代码中,通常会使用Spring框架的`DataSourceProxy`或者自定义的数据源切换逻辑来实现。例如,通过一个全局变量或配置文件来决定当前使用哪个数据源,然后在需要数据库连接时,根据这个选择来获取对应的Proxool数据...

    SpringMVC +Mybatis 多个数据源配置 DEMO + maven

    这个抽象类可以动态地决定使用哪个数据源,从而实现数据源的切换。在DEMO中,可能是通过实现`AbstractDataSource`的`determineCurrentLookupKey`方法来根据业务需求选择对应的数据源。 配置步骤大致如下: 1. **...

    myBatis多数据源配置

    3. **配置数据源切换**:在 Spring Boot 的配置类中,使用 `@ConfigurationProperties` 注解加载上述配置,并通过 `@Bean` 创建 `DataSource` 实例。然后,可以使用 `@Primary` 标记主数据源,其他为辅助数据源。...

    多数据源样例.rar

    总的来说,"多数据源样例.rar"是一个演示如何在SpringBoot 2.x环境下实现动态数据源切换的实例,特别适用于那些需要处理多种类型数据库或需要灵活切换数据源的项目。开发者可以通过这个样例学习如何优雅地管理复杂的...

    springboot+mybatis-plus动态数据源

    7. **DemoOne项目**:在压缩包中的`DemoOne`可能是实现动态数据源的示例代码,它可能包含了上述所有步骤的实例。通过阅读和分析`DemoOne`的代码,你可以更好地理解如何在实际项目中应用这些概念。 总之,...

    springboot双数据源实战项目

    1. **配置多数据源**:Spring Boot允许我们通过@ConfigurationProperties注解来加载YAML或properties文件中的配置信息。对于双数据源,我们需要定义两个不同的数据源配置,例如`primaryDataSource`和`...

    springboot多数据源、整合mybatis的映射xml例子.zip

    本示例提供了如何在Spring Boot项目中实现多数据源切换以及整合MyBatis的XML映射文件的方法。 首先,我们从`spring-boot-mybatis-mulidatasource`这个项目名称来看,它表明这是一个基于Spring Boot的项目,其中包含...

    java hibernate不同数据库之间的动态切换

    - 使用Spring框架:如果项目中使用了Spring,可以通过Spring的`PropertyPlaceholderConfigurer`或`@Value`注解来读取配置。 2. **动态加载SessionFactory** - 在初始化阶段,根据当前的环境或配置读取相应的...

    SpringBoot整合Oracle数据库使用双数据源

    本文将详细讲解如何利用SpringBoot整合Oracle数据库实现双数据源的配置和使用,帮助开发者构建可扩展、高可用的应用系统。 首先,我们需要理解"双数据源"的概念。双数据源意味着应用程序可以同时访问两个不同的...

    spring框架jar包

    Spring的数据访问抽象层使得在不同数据源之间切换变得更加容易。 4. Spring MVC:Spring MVC是Spring框架的一个模块,用于构建Web应用程序。它提供了一个分层的架构,包括模型、视图和控制器,使得开发者可以更清晰...

    java动态数据源

    Java动态数据源是一种在应用程序运行时能够根据特定条件或需求动态选择和切换数据源的技术。在Java开发中,数据源通常用于管理数据库连接,确保高效、安全地与数据库交互。本示例基于阿里巴巴的Druid数据库连接池,...

    SpringBoot+MyBatis操作多数据源

    总结来说,SpringBoot和MyBatis结合实现多数据源操作涉及的主要步骤包括配置数据源、创建SqlSessionFactory、定义Mapper接口、实现Service以及事务管理。在实际项目中,可以根据业务需求进行相应的调整和优化,确保...

    Springboot + Hutool-db 使用 URLClassLoader 动态加载外部数据库和数据池 jar包实例

    这里的数据源配置可以从加载的jar包中读取,例如,通过读取配置文件或者直接在代码中设置连接参数。 5. 通过创建的数据源,你可以进行数据库的连接、查询、事务处理等一系列操作。 `PostmanTest.json`可能是一个...

    JAVA上百实例源码以及开源项目源代码

    2个目标文件 摘要:Java源码,文件操作,TCP,服务器 Tcp服务端与客户端的JAVA实例源代码,一个简单的Java TCP服务器端程序,别外还有一个客户端的程序,两者互相配合可以开发出超多的网络程序,这是最基础的部分。...

    SpringBoot多数据源动态切换源码

    而当项目涉及多个数据库时,如何实现多数据源的动态切换就成为一个重要的技术问题。本项目“SpringBoot多数据源动态切换源码”正是为了解决这一问题,它允许不同的用户操作不同的数据库,同时保持相同的repository...

Global site tag (gtag.js) - Google Analytics