Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 8248|回复: 0

Spring动态切换多数据源解决方案

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-4-19 13:17:32 | 显示全部楼层 |阅读模式

     

           Spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性。而这样的方案就会不同于常见的单一数据实例的方案,这就要程序在运行时根据当时的请求及系统状态来动态的决定将数据存储在哪个数据库实例中,以及从哪个数据库提取数据。

           Spring2.x以后的版本中采用Proxy模式,就是我们在方案中实现一个虚拟的数据源,并且用它来封装数据源选择逻辑,这样就可以有效地将数据源选择逻辑从Client中分离出来。Client提供选择所需的上下文(因为这是Client所知道的),由虚拟的DataSource根据Client提供的上下文来实现数据源的选择。

    实现

    具体的实现就是,虚拟的DataSource仅需继承AbstractRoutingDataSource实现determineCurrentLookupKey()在其中封装数据源的选择逻辑。
     
    一、动态配置多数据源
    1. 数据源的名称常量类:
    [java]  view plain copy print ?
     
    1. /** 
    2.  * 动态配置多数据源 
    3.  * 数据源的名称常量类 
    4.  * @author LONGHUI_LUO 
    5.  * 
    6.  */  
    7. public class DataSourceConst {  
    8.     public static final String TEST="test";  
    9.     public static final String USER="User";  
    10. }  

    2. 建立一个获得和设置上下文环境的类,主要负责改变上下文数据源的名称:
     
    [java]  view plain copy print ?
     
    1. /** 
    2.  * 获得和设置上下文环境 主要负责改变上下文数据源的名称 
    3.  *  
    4.  * @author LONGHUI_LUO 
    5.  *  
    6.  */  
    7. public class DataSourceContextHolder {  
    8.     private static final ThreadLocal contextHolder = new ThreadLocal(); // 线程本地环境  
    9.   
    10.     // 设置数据源类型  
    11.     public static void setDataSourceType(String dataSourceType) {  
    12.         contextHolder.set(dataSourceType);  
    13.     }  
    14.   
    15.     // 获取数据源类型  
    16.     public static String getDataSourceType() {  
    17.         return (String) contextHolder.get();  
    18.     }  
    19.   
    20.     // 清除数据源类型  
    21.     public static void clearDataSourceType() {  
    22.         contextHolder.remove();  
    23.     }  
    24.   
    25. }  


    3. 建立动态数据源类,注意,这个类必须继承AbstractRoutingDataSource,且实现方法determineCurrentLookupKey,该方法返回一个Object,一般是返回字符串:

     
    [java]  view plain copy print ?
     
    1. /** 
    2.  * 建立动态数据源 
    3.  *  
    4.  * @author LONGHUI_LUO 
    5.  *  
    6.  */  
    7. public class DynamicDataSource extends AbstractRoutingDataSource {  
    8.   
    9.  protected Object determineCurrentLookupKey() {  
    10.   // 在进行DAO操作前,通过上下文环境变量,获得数据源的类型  
    11.   return DataSourceContextHolder.getDataSourceType();  
    12.  }  
    13.   
    14. }  


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

    [html]  view plain copy print ?
     
    1.         <!-- 数据源相同的内容 -->  
    2. <bean  
    3.         id="parentDataSource"  
    4.         class="org.apache.commons.dbcp.BasicDataSource"  
    5.         destroy-method="close">  
    6.         <property  
    7.             name="driverClassName"  
    8.             value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />  
    9.         <property name="username" value="sa" />  
    10.         <property name="password" value="net2com" />  
    11. </bean>  
    [html]  view plain copy print ?
     
    1. <!-- start以下配置各个数据源的特性 -->  
    2. <bean parent="parentDataSource" id="testDataSource">   
    3.         <propertynamepropertyname="url" value="jdbc:sqlserver://localhost:1433;databaseName=test" />  
    4. </bean>   
    5. <bean parent="parentDataSource" id="UserDataSource">   
    6.             <property  
    7.             name="url"  
    8.             value="jdbc:sqlserver://localhost:1433;databaseName=User" />  
    9. </bean>   
    [html]  view plain copy print ?
     
    1. <!-- end 配置各个数据源的特性 -->  


    5. 编写spring配置文件配置多数据源映射关系

    [html]  view plain copy print ?
     
    1. <bean class="com.xxxx.datasouce.DynamicDataSource" id="dataSource">  
    2.     <property name="targetDataSources">   
    3.        <map key-type="java.lang.String">   
    4.            <entry value-ref="testDataSource" key="test"></entry>  
    5.            <entry value-ref="UserDataSource" key="User"></entry>  
    6.        </map>   
    7.     </property>   
    8.     <property name="defaultTargetDataSource" ref="testDataSource" ></property>  
    9. </bean>  

            在这个配置中第一个property属性配置目标数据源,<map key-type="java.lang.String">中的key-type必须要和静态键值对照类DataSourceConst中的值的类型相 同;<entry key="User" value-ref="userDataSource"/>中key的值必须要和静态键值对照类中的值相同,如果有多个值,可以配置多个< entry>标签。第二个property属性配置默认的数据源。

     

    动态切换是数据源

    [java]  view plain copy print ?
     
    1. DataSourceContextHolder.setDataSourceType(DataSourceConst.TEST);  


     

    该方案的优势

           首先,这个方案完全是在spring的框架下解决的,数据源依然配置在spring的配置文件中,sessionFactory依然去配置它的dataSource属性,它甚至都不知道dataSource的改变。唯一不同的是在真正的dataSource与sessionFactory之间增加了一个MultiDataSource。
    其次,实现简单,易于维护。这个方案虽然我说了这么多东西,其实都是分析,真正需要我们写的代码就只有MultiDataSource、SpObserver两个类。MultiDataSource类真正要写的只有getDataSource()和getDataSource(sp)两个方法,而SpObserver类更简单了。实现越简单,出错的可能就越小,维护性就越高。
    最后,这个方案可以使单数据源与多数据源兼容。这个方案完全不影响BUS和DAO的编写。如果我们的项目在开始之初是单数据源的情况下开发,随着项目的进行,需要变更为多数据源,则只需要修改spring配置,并少量修改MVC层以便在请求中写入需要的数据源名,变更就完成了。如果我们的项目希望改回单数据源,则只需要简单修改配置文件。这样,为我们的项目将增加更多的弹性。

    该方案的缺点

           没有能够解决多用户访问单例“sessionFactory”时共享“dataSource”变量,导致产生争抢“dataSource”的结果,本质类似于操作系统中的“生产者消费者”问题。因此当多用户访问时,多数据源可能会导致系统性能下降的后果。
    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-5 08:06 , Processed in 0.058417 second(s), 28 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表