spring动态配置多数据源.docx_第1页
spring动态配置多数据源.docx_第2页
spring动态配置多数据源.docx_第3页
spring动态配置多数据源.docx_第4页
spring动态配置多数据源.docx_第5页
已阅读5页,还剩25页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

起因:在当前我手上的一个项目中需要多个数据源,并且来自于不同类型的数据库. 因为很多历史原因.这个项目的住数据源是MySQL,整个系统的CURD都是操作的这个数据库.但是还有另外两个用于数据采集的数据库: MSSQL,ACCESS.还好只是用于数据采集,在事务上可以不要跨数据库了,这一点节省了好多的工作量. 环境:我搭建的测试环境是 spring2.5.6+hibernate3.2 思路:动态切换数据源确切的来说是在同一类型数据库的情况下的。意思就是说 , 在系统中的使用的数据库分布在多台数据库服务器或者在同台服务器上的多个数据库. 在运行时期间根据某种标识符来动态的选择当前操作的数据库. 1.数据源是相同类型的数据库: 一个SessionFactory+动态数据源+一个事务管理器 2.数据源是不同类型的数据库: 根据类型 配置多套SessionFactory 模拟:两个mysql数据源+一个Access数据源 实现: 1.切换数据源需要标识符,标识符是Object类型package lhp.example.context;public enum DBType dataSource1, dataSource2; 2.然后创建一个用于切换数据源(设置或者获得上下文)的工具类复制代码package lhp.example.context;public class ContextHolder private static final ThreadLocal holder = new ThreadLocal(); 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.springframework.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 = Logger.getLogger(DynamicDataSource.class.toString(); Override protected Object determineCurrentLookupKey() DBType key = ContextHolder.getDbType();/获得当前数据源标识符 /(当前数据源 : + key); return key; 复制代码 4.然后配置多个数据源复制代码 复制代码 5.配置sessionFactory复制代码 org.hibernate.dialect.MySQLDialect true update true 1, false 0 org.hibernate.dialect.SQLServerDialect 30 50 true false update true 1, false 0 true !- true - !- org.hibernate.cache.EhCacheProvider - !- true - !- true - !- classpath:ehcache.xml - 复制代码 6.测试用例复制代码package lhp.example.junit;import static org.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.springframework.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 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 applicationContext-dataSource.xml, applicationContext-hibernate.xml, applicationContext-spring.xml ; / context = new ClassPathXmlApplicationContext(xmlFiles); / mysqlSessionFactory = (SessionFactory) context.getBean(mysqlSessionFactory); aceessSessionFactory = (SessionFactory) context.getBean(aceessSessionFactory); SuppressWarnings(deprecation) Test public void mysqlDataSourceTest() try Session mysqlSession = mysqlSessionFactory.openSession(); / 获得数据库元数据 DatabaseMetaData meatData = mysqlSession.connection().getMetaData(); / 默认启动数据源 dataSource1 /断言当前数据源URL是否是dataSource1的URL assertEquals(dataSource1_URL, meatData.getURL(); / 切换到数据源 dataSource2 ContextHolder.setDbType(DBType.dataSource2); mysqlSession = mysqlSessionFactory.openSession(); meatData = mysqlSession.connection().getMetaData(); /断言当前数据源URL是否是dataSource2的URL assertEquals(dataSource2_URL, meatData.getURL(); catch (Exception e) e.printStackTrace(); SuppressWarnings(deprecation) Test public void accessDataSourceTest() try Session accessSession = aceessSessionFactory.openSession(); / 获得数据库元数据 DatabaseMetaData meatData = accessSession.connection().getMetaData(); /断言当前数据源URL是否是dataSource3的URL assertEquals(dataSource3_URL, meatData.getURL(); catch (Exception e) e.printStackTrace(); 复制代码 7.测试结果spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性。而这样的方案就会不同于常见的单一数据实例的方案,这就要程序在运行时根据当时的请求及系统状态来动态的决定将数据存储在哪个数据库实例中,以及从哪个数据库提取数据。 Spring2.x以后的版本中采用Proxy模式,就是我们在方案中实现一个虚拟的数据源,并且用它来封装数据源选择逻辑,这样就可以有效地将数据源选择逻辑从Client中分离出来。Client提供选择所需的上下文(因为这是Client所知道的),由虚拟的DataSource根据Client提供的上下文来实现数据源的选择。实现具体的实现就是,虚拟的DataSource仅需继承AbstractRoutingDataSource实现determineCurrentLookupKey()在其中封装数据源的选择逻辑。一、动态配置多数据源1. 数据源的名称常量类:javaview plaincopyprint?1. /*2. *动态配置多数据源3. *数据源的名称常量类4. *authorLONGHUI_LUO5. *6. */7. publicclassDataSourceConst8. publicstaticfinalStringTEST=test;9. publicstaticfinalStringUSER=User;10. 2. 建立一个获得和设置上下文环境的类,主要负责改变上下文数据源的名称:javaview plaincopyprint?1. /*2. *获得和设置上下文环境主要负责改变上下文数据源的名称3. *4. *authorLONGHUI_LUO5. *6. */7. publicclassDataSourceContextHolder8. privatestaticfinalThreadLocalcontextHolder=newThreadLocal();/线程本地环境9. 10. /设置数据源类型11. publicstaticvoidsetDataSourceType(StringdataSourceType)12. contextHolder.set(dataSourceType);13. 14. 15. /获取数据源类型16. publicstaticStringgetDataSourceType()17. return(String)contextHolder.get();18. 19. 20. /清除数据源类型21. publicstaticvoidclearDataSourceType()22. contextHolder.remove();23. 24. 25. 3. 建立动态数据源类,注意,这个类必须继承AbstractRoutingDataSource,且实现方法determineCurrentLookupKey,该方法返回一个Object,一般是返回字符串:javaview plaincopyprint?1. /*2. *建立动态数据源3. *4. *authorLONGHUI_LUO5. *6. */7. publicclassDynamicDataSourceextendsAbstractRoutingDataSource8. 9. protectedObjectdetermineCurrentLookupKey()10. /在进行DAO操作前,通过上下文环境变量,获得数据源的类型11. returnDataSourceContextHolder.getDataSourceType();12. 13. 14. 4. 编写spring的配置文件配置多个数据源htmlview plaincopyprint?1. 2. 6. 9. 10. 11. htmlview plaincopyprint?1. 2. 3. 4. 5. 6. 9. htmlview plaincopyprint?1. 5. 编写spring配置文件配置多数据源映射关系htmlview plaincopyprint?1. 2. 3. 4. 5. 6. 7. 8. 9. 在这个配置中第一个property属性配置目标数据源,中的key-type必须要和静态键值对照类DataSourceConst中的值的类型相 同;中key的值必须要和静态键值对照类中的值相同,如果有多个值,可以配置多个标签。第二个property属性配置默认的数据源。动态切换是数据源javaview plaincopyprint?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”的结果,本质类似于操作系统中的“生产者消费者”问题。因此当多用户访问时,多数据源可能会导致系统性能下降的后果。转载我首先想到在spring但是,我很快发现一个问题:当多用户同时并发访问数据库的时候会出现资源争用的问题。这都是“单例模式”惹的祸。众所周知,我们在使用spring通过以上的分析,解决多数据源访问问题的关键,就集中在sessionFactory(一) 采用Decorator设计模式要解决这个问题,我的思路锁定在了这个dataSource什么是“(二) 设计MultiDataSource类现在回到我们的问题,我们需要对dataSource对比原Decoratorspan times= span= 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 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就是其对应的 oracle.jdbc.driver.OracleDrivervalue property . bean oracle.jdbc.driver.OracleDrivervalue property . bean 为了得到spring,并且实现方法:java 代码private ApplicationContext applicationContext = null; public void setApplicationContext(ApplicationContext applicationContext) throws BeansException this.applicationContext = applicationContext; 如此这样,我就可以通过得到了。(四) 通过线程传递dataSourceName查看以上设计,MultiDataSourcespan times= span= roman?;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,将request中的dataSourceName对象。 String sp = SpObserver.getSp(); return getDataSource(sp); 完整的MultiDataSource(五) 动态添加数据源通过以上方案,我们解决了动态分配数据源的问题,但你可能提出疑问:方案中的数据源都是配置在spring中(见附件)。不通过配置文件直接加载对象,在的源码中也有,感兴趣的朋友可以自己研究。(六) 在spring中配置在完成了所有这些设计以后,我最后再唠叨一句。我们应当在springbean property bean property . bean 其中dataSource以上方案与其它方案相比,它有哪些优势呢?首先,这个方案完全是在spring其次,实现简单,易于维护。这个方案虽然我说了这么多东西,其实都是分析,真正需要我们写的代码就只有MultiDataSource最后,这个方案可以使单数据源与多数据源兼容。这个方案完全不影响BUS相关博客:再析在spring框架中解决多数据源的问题example.rar 描述: 源码及示例 下载 文件名: example.rar 文件大小: 32 KB 下载过的: 文件被下载或查看 521 次 再析在spring框架中解决多数据源的问题关键字: Spring Hibernate Decorator 设计模式 如何在spring总结多数据源的问题,其实它需要分为以下三种情况:各个数据源的数据结构不同、各个数据源的数据结构相同、各个数据源的数据结构部分相同又有部分不同。对于第二种情况,各个数据源的数据结构相同,我们使用一个sessionFactory对于各个数据源的数据结构不同的情况,使用一个sessionFactory与MultiDataSource在该方案中,SessionFactory接口,Decorator就是MultiSessionFactory,SessionFactory1和SessionFactory2往往是spring的。细心的朋友可能会注意,实际上并不是SessionFactory的时候其实并不是真正地得到了它,而是得到了一个SessionFactory重写了getObject()。在整个这个方案中,我们需要实现的只有MultiSessionFactory。MultiSessionFactoryspan times= span= roman?;class MultiSessionFactory implements SessionFactory, ApplicationContextAware private static final long serialVersionUID = 2064557324203496378L; private static final Log log = LogFactory.getLog(MultiSessionFactory.class); private ApplicationContext applicationContext = null; private SessionFactory sessionFactory = null; public ApplicationContext getApplicationContext() return applicationContext; public void setApplicationContext(ApplicationContext applicationContext) this.applicationContext = applicationContext; public SessionFactory getSessionFactory(String sessionFactoryName) log.debug(sessionFactoryName:+sessionFactoryName); try if(sessionFactoryName=null|sessionFactoryName.equals() return sessionFactory; return (SessionFactory)this.getApplicationContext().getBean(sessionFactoryName); catch(NoSuchBeanDefinitionException ex) throw new DaoException(There is not the sessionFactory public SessionFactory getSessionFactory() St

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论