版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、起因:在当前我手上的一个项目中需要多个数据源,并且来自于不同类型的数据库. 因为很多历史原因.这个项目的住数据源是MySQL,整个系统的CURD都是操作的这个数据库.但是还有另外两个用于数据采集的数据库: MSSQL,ACCESS.还好只是用于数据采集,在事务上可以不要跨数据库了,这一点节省了好多的工作量. 环境:我搭建的测试环境是 spring2.5.6+hibernate3.2 思路:动态切换数据源确切的来说是在同一类型数据库的情况下的。意思就是说 , 在系统中的使用的数据库分布在多台数据库服务器或者在同台服务器上的多个数据库. 在运行时期间根据某种标识符来动态的选择当前操作的数据库. 1
2、.数据源是相同类型的数据库: 一个SessionFactory+动态数据源+一个事务管理器 2.数据源是不同类型的数据库: 根据类型 配置多套SessionFactory 模拟:两个mysql数据源+一个Access数据源 实现: 1.切换数据源需要标识符,标识符是Object类型package lhp.example.context;public enum DBType dataSource1, dataSource2; 2.然后创建一个用于切换数据源(设置或者获得上下文)的工具类复制代码package lhp.example.context;public class ContextHold
3、er private static final ThreadLocal<Object> holder = new ThreadLocal<Object>(); public static void setDbType(DBType dbType) holder.set(dbType); public static DBType getDbType() return (DBType) holder.get(); public static void clearDbType() holder.remove(); 复制代码 3.创建动态数据源类,继承org.springfra
4、mework.jdbc.datasource.lookup.AbstractRoutingDataSource这个类.复制代码package lhp.example.context;import java.util.logging.Logger;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;public class DynamicDataSource extends AbstractRoutingDataSource public static final Logger logger =
5、Logger.getLogger(DynamicDataSource.class.toString(); Override protected Object determineCurrentLookupKey() DBType key = ContextHolder.getDbType();/获得当前数据源标识符 /("当前数据源 :" + key); return key; 复制代码 4.然后配置多个数据源复制代码<!- 数据源1 : mysql -> <bean id="dataSource1" class=&
6、quot;com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <property name="jdbcUrl" value="jdbc:mysql:/:3306/dec"/> <property name="user" value="root"/> &
7、lt;property name="password" value=""/> </bean> <!- 数据源2 : mysql -> <bean id="dataSource2" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver"/> <propert
8、y name="jdbcUrl" value="jdbc:mysql:/:3306/lms"/> <property name="user" value="root"/> <property name="password" value=""/> </bean> <!- 数据源3 : access -> <bean id="dataSource3" class="com.
9、mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="sun.jdbc.odbc.JdbcOdbcDriver"/> <property name="jdbcUrl" value="jdbc:odbc:accessTest"/> <property name="user" value="administrator"/> <
10、property name="password" value="XLZX0309"/> </bean> <!- mysql 动态数据源设置-> <bean id="mysqlDynamicDataSource" class="lhp.example.context.DynamicDataSource"> <property name="targetDataSources"> <!- 标识符类型 -> <map key-typ
11、e="lhp.example.context.DBType"> <entry key="dataSource1" value-ref="dataSource1"/> <entry key="dataSource2" value-ref="dataSource2"/> </map> </property> <property name="defaultTargetDataSource" ref="dataSo
12、urce1"/> </bean> 复制代码 5.配置sessionFactory复制代码<!- mysql sessionFactory -> <bean id="mysqlSessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource" ref="mysqlDynamicDataSource"/>
13、; <property name="hibernateProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">update</prop>&
14、lt;!-create validate -> <prop key="hibernate.query.substitutions">true 1, false 0</prop> </props> </property> </bean> <!- access sessionFactory -> <bean id="aceessSessionFactory" class="org.springframework.orm.hibernate3.LocalSession
15、FactoryBean"> <property name="dataSource" ref="dataSource3"/> <property name="hibernateProperties"> <props> <!- access 语法和MSSQL相似 所以用的MSSQL方言,或者可以使用第三方方言 -> <prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDial
16、ect</prop> <prop key="hibernate.jdbc.batch_size">30</prop> <prop key="hibernate.jdbc.fetch_size">50</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">false</prop> <prop k
17、ey="hibernate.hbm2ddl.auto">update</prop><!-create validate -> <prop key="hibernate.query.substitutions">true 1, false 0</prop> <prop key="hibernate.cglib.use_reflection_optimizer">true</prop> <!- <prop key="hibernate.ca
18、che.use_second_level_cache">true</prop> -> <!- <prop key="vider_class">org.hibernate.cache.EhCacheProvider</prop> -> <!- <prop key="hibernate.cache.use_query_cache">true</prop> -> <!- <prop key="hi
19、bernate.generate_statistics">true</prop> -> <!- <prop key="vider_configuration_file_resource_path">classpath:ehcache.xml</prop> -> </props> </property> </bean>复制代码 6.测试用例复制代码package lhp.example.junit;import static org.
20、junit.Assert.*;import java.sql.DatabaseMetaData;import lhp.example.context.ContextHolder;import lhp.example.context.DBType;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.junit.Before;import org.junit.Test;import org.springframework.context.ApplicationContext;import org.s
21、pringframework.context.support.ClassPathXmlApplicationContext;public class ServiceTest private ApplicationContext context; /三个数据源的URL private String dataSource1_URL = "jdbc:mysql:/:3306/dec" private String dataSource2_URL = "jdbc:mysql:/:3306/lms" private String
22、 dataSource3_URL = "jdbc:odbc:accessTest" private SessionFactory mysqlSessionFactory; private SessionFactory aceessSessionFactory; Before public void setUp() throws Exception / 选择数据源初始化spring ContextHolder.setDbType(DBType.dataSource1); / String xmlFiles = new String "applicationConte
23、xt-dataSource.xml", "applicationContext-hibernate.xml", "applicationContext-spring.xml" ; / context = new ClassPathXmlApplicationContext(xmlFiles); / mysqlSessionFactory = (SessionFactory) context.getBean("mysqlSessionFactory"); aceessSessionFactory = (SessionFacto
24、ry) context.getBean("aceessSessionFactory"); SuppressWarnings("deprecation") Test public void mysqlDataSourceTest() try Session mysqlSession = mysqlSessionFactory.openSession(); / 获得数据库元数据 DatabaseMetaData meatData = mysqlSession.connection().getMetaData(); / 默认启动数据源 dataSource1
25、/断言当前数据源URL是否是dataSource1的URL assertEquals(dataSource1_URL, meatData.getURL(); / 切换到数据源 dataSource2 ContextHolder.setDbType(DBType.dataSource2); mysqlSession = mysqlSessionFactory.openSession(); meatData = mysqlSession.connection().getMetaData(); /断言当前数据源URL是否是dataSource2的URL assertEquals(dataSource
26、2_URL, meatData.getURL(); catch (Exception e) e.printStackTrace(); SuppressWarnings("deprecation") Test public void accessDataSourceTest() try Session accessSession = aceessSessionFactory.openSession(); / 获得数据库元数据 DatabaseMetaData meatData = accessSession.connection().getMetaData(); /断言当前数
27、据源URL是否是dataSource3的URL assertEquals(dataSource3_URL, meatData.getURL(); catch (Exception e) e.printStackTrace(); 复制代码 7.测试结果spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性。而这样的方案就会不同于常见的单一数据实例的方案,这就要程序在运行时根据当时的请求及系统状态来动态的决定将数据存储在哪个数据库实例中,以及从哪个数据库提取数据。
28、60; Spring2.x以后的版本中采用Proxy模式,就是我们在方案中实现一个虚拟的数据源,并且用它来封装数据源选择逻辑,这样就可以有效地将数据源选择逻辑从Client中分离出来。Client提供选择所需的上下文(因为这是Client所知道的),由虚拟的DataSource根据Client提供的上下文来实现数据源的选择。实现具体的实现就是,虚拟的DataSource仅需继承AbstractRoutingDataSource实现determineCurrentLookupKey()在其中封装数据源的选择逻辑。 一、动态配置多数据源1. 数据源的名称常量类:java vie
29、w plain copy print?1. /* 2. * 动态配置多数据源 3. * 数据源的名称常量类 4. * author LONGHUI_LUO 5. * 6. */ 7. public class DataSourceConst 8. public static final
30、 String TEST="test" 9. public static final String USER="User" 10. 2. 建立一个获得和设置上下文环境的类,主要负责改变上下文数据源的名称: java view plain copy print?1. /* 2. * 获得和设置上下文环境
31、;主要负责改变上下文数据源的名称 3. * 4. * author LONGHUI_LUO 5. * 6. */ 7. public class DataSourceContextHolder 8. private static final ThreadLocal contextHolder
32、= new ThreadLocal(); / 线程本地环境 9. 10. / 设置数据源类型 11. public static void setDataSourceType(String dataSourceType) 12.
33、; contextHolder.set(dataSourceType); 13. 14. 15. / 获取数据源类型 16. public static String getDataSourceType() 17.
34、160; return (String) contextHolder.get(); 18. 19. 20. / 清除数据源类型 21. public static void clearDataSourceType() 22.
35、; contextHolder.remove(); 23. 24. 25. 3. 建立动态数据源类,注意,这个类必须继承AbstractRoutingDataSource,且实现方法determineCurrentLookupKey,该方法返回一个Object,一般是返回字符串: java view plain copy print?1. /*&
36、#160;2. * 建立动态数据源 3. * 4. * author LONGHUI_LUO 5. * 6. */ 7. public class DynamicDataSource extends AbstractRoutingDataSource 8. 9. protected Object
37、0;determineCurrentLookupKey() 10. / 在进行DAO操作前,通过上下文环境变量,获得数据源的类型 11. return DataSourceContextHolder.getDataSourceType(); 12. 13. 14. 4. 编写spring的配置文件配置多个数据源html view plain cop
38、y print?1. <!- 数据源相同的内容 -> 2. <bean 3. id="parentDataSource" 4. class="mons.dbcp.Bas
39、icDataSource" 5. destroy-method="close"> 6. <property 7. name="driverClas
40、sName" 8. value="com.microsoft.sqlserver.jdbc.SQLServerDriver" /> 9. <property name="username" value="sa&qu
41、ot; /> 10. <property name="password" value="net2com" /> 11. </bean> html view plain copy print?1. <!- start以下配置各个数据源的特性 ->
42、;2. <bean parent="parentDataSource" id="testDataSource"> 3. <propertynamepropertyname="url" value="jdbc:sqlserver:/localhost:1433;databaseName=test" />
43、4. </bean> 5. <bean parent="parentDataSource" id="UserDataSource"> 6. <property 7. &
44、#160; name="url" 8. value="jdbc:sqlserver:/localhost:1433;databaseName=User" /> 9. </bean> html view plain copy print?1. <!-
45、160;end 配置各个数据源的特性 -> 5. 编写spring配置文件配置多数据源映射关系html view plain copy print?1. <bean class="com.xxxx.datasouce.DynamicDataSource" id="dataSource"> 2. <property name="targetDataSou
46、rces"> 3. <map key-type="java.lang.String"> 4. <entry value-ref="testDataSource" key="test">&
47、lt;/entry> 5. <entry value-ref="UserDataSource" key="User"></entry> 6. </map> 7.
48、60;</property> 8. <property name="defaultTargetDataSource" ref="testDataSource" ></property> 9. </bean> 在这个配置中第一个property属性配置目标数据源,<
49、map key-type="Java.lang.String">中的key-type必须要和静态键值对照类DataSourceConst中的值的类型相 同;<entry key="User" value-ref="userDataSource"/>中key的值必须要和静态键值对照类中的值相同,如果有多个值,可以配置多个< entry>标签。第二个property属性配置默认的数据源。 动态切换是数据源java view plain copy print?1. Dat
50、aSourceContextHolder.setDataSourceType(DataSourceConst.TEST); 该方案的优势 首先,这个方案完全是在spring的框架下解决的,数据源依然配置在spring的配置文件中,sessionFactory依然去配置它的dataSource属性,它甚至都不知道dataSource的改变。唯一不同的是在真正的dataSource与sessionFactory之间增加了一个MultiDataSource。其次,实现简单,易于维护。这个方案虽然
51、我说了这么多东西,其实都是分析,真正需要我们写的代码就只有MultiDataSource、SpObserver两个类。MultiDataSource类真正要写的只有getDataSource()和getDataSource(sp)两个方法,而SpObserver类更简单了。实现越简单,出错的可能就越小,维护性就越高。最后,这个方案可以使单数据源与多数据源兼容。这个方案完全不影响BUS和DAO的编写。如果我们的项目在开始之初是单数据源的情况下开发,随着项目的进行,需要变更为多数据源,则只需要修改spring配置,并少量修改MVC层以便在请求中写入需要的数据源名,变更就完成了。如果我们的项目希望改
52、回单数据源,则只需要简单修改配置文件。这样,为我们的项目将增加更多的弹性。该方案的缺点 没有能够解决多用户访问单例“sessionFactory”时共享“dataSource”变量,导致产生争抢“dataSource”的结果,本质类似于操作系统中的“生产者消费者”问题。因此当多用户访问时,多数据源可能会导致系统性能下降的后果。转载我首先想到在spring但是,我很快发现一个问题:当多用户同时并发访问数据库的时候会出现资源争用的问题。这都是“单例模式”惹的祸。众所周知,我们在使用spring通过以上的分析,解决多数据源访问问
53、题的关键,就集中在sessionFactory(一) 采用Decorator设计模式要解决这个问题,我的思路锁定在了这个dataSource什么是“(二) 设计MultiDataSource类现在回到我们的问题,我们需要对dataSource对比原Decorator<span times="" span="" private DataSource dataSource = null; public MultiDataSource(DataSource dataSource) this.dataSource = dataSource; /* (non
54、-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 = da
55、taSource; 客户在发出请求的时候,将dataSourceName放到request中,然后把request中的数据源名通过调用new MultiDataSource(dataSource)时可以告诉客户需要的数据源,就可以实现动态切换数据源了。但细心的朋友会发现这在单例的情况下就是问题的,因为在系统中只有一个对象,它的实例变量也只有一个,就如同一个静态变量一般。正因为如此,(三) 单例模式下的MultiDataSource在单例模式下,由于我们在每次调用MultiDataSource log.debug("dataSourceName:"+dataSourceNam
56、e); 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就是其对应的<span couri
57、er="" span="" new?;bean id="dataSource1" class="mons.dbcp.BasicDataSource"> <property name="driverClassName"> <value>oracle.jdbc.driver.OracleDrivervalue> property> . bean> <bean id="dataSource2" class="mons.
58、dbcp.BasicDataSource"> <property name="driverClassName"> <value>oracle.jdbc.driver.OracleDrivervalue> property> . bean> 为了得到spring,并且实现方法:java 代码private ApplicationContext applicationContext = null; public void setApplicationContext(ApplicationContext applicati
59、onContext) throws BeansException this.applicationContext = applicationContext; 如此这样,我就可以通过得到了。(四) 通过线程传递dataSourceName查看以上设计,MultiDataSource<span times="" span="" roman?;class SpObserver private static ThreadLocal local = new ThreadLocal(); public static void putSp(String sp)
60、local.set(sp); public static String getSp() return (String)local.get(); 做一个filter,将request中的dataSourceName对象。 String sp = SpObserver.getSp(); return getDataSource(sp); 完整的MultiDataSource(五) 动态添加数据源通过以上方案,我们解决了动态分配数据源的问题,但你可能提出疑问:方案中的数据源都是配置在spring中(见附件)。不通过配置文件直接加载对象,在的源码中也有,感兴趣的朋友可以自己研究。(六) 在spring
61、中配置在完成了所有这些设计以后,我最后再唠叨一句。我们应当在spring<span times="" span="" roman?;bean id="dynamicLoadBean" class="com.htxx.service.dao.DynamicLoadBean">bean> <bean id="dataSource" class="com.htxx.service.dao.MultiDataSource"> <property n
62、ame="dataSource"> <ref bean="dataSource1" /> property> bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="dataSource"> <ref bean="dataSource" /&g
63、t; property> . bean> 其中dataSource以上方案与其它方案相比,它有哪些优势呢?首先,这个方案完全是在spring其次,实现简单,易于维护。这个方案虽然我说了这么多东西,其实都是分析,真正需要我们写的代码就只有MultiDataSource最后,这个方案可以使单数据源与多数据源兼容。这个方案完全不影响BUS相关博客:再析在spring框架中解决多数据源的问题example.rar 描述: 源码及示例 下载 文件名: example.rar 文件大小: 32 KB 下载过的: 文件被下载或查看 521 次 再析在spring框架中解决多数据源的问题关键字:
64、Spring Hibernate Decorator 设计模式 如何在spring总结多数据源的问题,其实它需要分为以下三种情况:各个数据源的数据结构不同、各个数据源的数据结构相同、各个数据源的数据结构部分相同又有部分不同。对于第二种情况,各个数据源的数据结构相同,我们使用一个sessionFactory对于各个数据源的数据结构不同的情况,使用一个sessionFactory与MultiDataSource在该方案中,SessionFactory接口,Decorator就是MultiSessionFactory,SessionFactory1和SessionFactory2往往是spring的
65、。细心的朋友可能会注意,实际上并不是SessionFactory的时候其实并不是真正地得到了它,而是得到了一个SessionFactory重写了getObject()。在整个这个方案中,我们需要实现的只有MultiSessionFactory。MultiSessionFactory<span times="" span="" roman?;class MultiSessionFactory implements SessionFactory, ApplicationContextAware private static final long serialVersionUID = 2064557324203496378L; private static final Log log = LogFactory.getLog(MultiSessionFactory.class); priva
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 服装专柜供货合同范本
- 填泥工程合同范本
- 2026基因检测在慢病管理中的价值体现与支付模式创新报告
- 2026中国自动驾驶传感器行业标准制定与市场格局演变报告
- 2025年法宣在线练习题及答案
- 2026年蔬菜种植公司绿丰极端天气生产应急预案
- 网络策划活动方案海报
- 化肥农资营销方案
- 船舶行业合同管理制度
- 完善消防应急预案
- 室内消火栓系统安装技术交底
- 胸腔闭式引流术临床技能操作指南
- 2023胶圈电熔双密封聚乙烯复合供水管道工程技术规程
- 低压单体设备的停送电操作规程
- 幼儿园讲故事小鸭子找朋友
- ZZ029-养老照护赛项赛题(10套)-2023年全国职业院校技能大赛拟设赛项赛题(10套)
- 实验安全你我他智慧树知到答案章节测试2023年内蒙古农业大学
- 眼眶病眼眶肿瘤七制讲课4
- 2023年陕西领导干部任前廉政考试题库
- 2023年全国中学生英语能力竞赛NEPCS高一组决赛含答案和听力
- 2022年新整理《研究生中国特色社会主义理论与实践研究》考题附答案
评论
0/150
提交评论