Spring動態建立,加載,使用多數據源

      項目中咱們常常會遇到多數據源的問題,尤爲是數據同步或定時任務等項目更是如此。多數據源讓人最頭痛的,不是配置多個數據源,而是如何能靈活動態的切換數據源。例如在一個spring和hibernate的框架的項目中,咱們在spring配置中每每是配置一個dataSource來鏈接數據庫,而後綁定給sessionFactory,在dao層代碼中再指定sessionFactory來進行數據庫操做。html

正如上圖所示,每一塊都是指定綁死的,若是是多個數據源,也只能是下圖中那種方式。java

可看出在Dao層代碼中寫死了兩個SessionFactory,這樣往後若是再多一個數據源,還要改代碼添加一個SessionFactory,顯然這並不符合開閉原則。mysql

那麼正確的作法應該是spring

先說一下個人思路:sql

首先作一個建立數據源配置文件的類,專門用於建立數據源xml配置,叫ConfigFileCreator.java 數據庫

其次,編寫一個動態加載剛纔建立的配置文件的類,叫 DynamicLoadBean.java。至此,建立的數據源到內存中已經完成。session

第三,重點編寫這個DynamicDataSource.java,該類主要是用來實現動態切換數據源,並通知容器。在這一塊我選擇了修改,AbstractRoutingDataSource.java,編寫一個支持添加數據源對象的方法public void addDataSource(Object key,Object dataSource)app

第四,寫例子來測試框架

實現過程以下:ide

1.bean.xml配置文件

[html]    view plain   copy   在CODE上查看代碼片   派生到個人代碼片  

  1. <?xml version="1.0" encoding="UTF-8"?>  
    <beans xmlns="http://www.springframework.org/schema/beans"  
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
        xmlns:tx="http://www.springframework.org/schema/tx"  
        xmlns:context="http://www.springframework.org/schema/context"  
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
                    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd  
                    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">  
        <!-- 自動掃描與裝配bean -->  
        <context:component-scan base-package="qilin"></context:component-scan>  
        <!-- 使用外部的配置文件 -->  
        <context:property-placeholder location="classpath:jdbc.properties" />  
        <bean id="defaultDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">  
                    <!-- 數據庫鏈接信息 -->  
                    <property name="jdbcUrl" value="${jdbcUrl}"></property>  
                    <property name="driverClass" value="${driverClass}"></property>  
                    <property name="user" value="${username}"></property>  
                    <property name="password" value="${password}"></property>  
        </bean>  
          
        <bean id="dataSourceA" class="com.mchange.v2.c3p0.ComboPooledDataSource">  
                    <!-- 數據庫鏈接信息 -->  
                    <property name="jdbcUrl" value="jdbc:mysql:///dynamictest"></property>  
                    <property name="driverClass" value="${driverClass}"></property>  
                    <property name="user" value="${username}"></property>  
                    <property name="password" value="${password}"></property>  
        </bean>  
        <bean id="dynamicdatasource" class="qilin.utils.DynamicDataSource">  
            <property name="targetDataSources">  
                <map key-type="java.lang.String">  
                     <entry key="defaultDataSource" value-ref="defaultDataSource" />   
                     <entry key="dataSourceA" value-ref="dataSourceA" />   
                </map>  
            </property>  
            <property name="defaultTargetDataSource" ref="defaultDataSource" />  
        </bean>  
      
        <!-- 配置SessionFactory -->  
        <bean id="sessionFactory"  
            class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">  
            <property name="dataSource" ref="dynamicdatasource" />  
            <property name="hibernateProperties">  
                <props>  
                    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>  
                    <prop key="hibernate.hbm2ddl.auto">none</prop>  
                    <prop key="hibernate.show_sql">true</prop>  
                    <prop key="hibernate.format_sql">false</prop>  
                    <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>  
                </props>  
            </property>  
            <!-- 註解方式配置 -->  
            <property name="annotatedClasses">  
                <list>  
                    <value>qilin.entity.Student</value>  
                </list>  
            </property>  
        </bean>  
      
      
        <!-- 配置聲明式事務管理,採用基於註解的方式 -->  
        <bean id="transactionManager"  
            class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
            <property name="sessionFactory" ref="sessionFactory"></property>  
        </bean>  
        <tx:annotation-driven transaction-manager="transactionManager" />  
        <bean id="dynamicLoadBean" class="qilin.utils.DynamicLoadBean"></bean>  
    </beans>

2.AbstractRoutingDataSource.class 修改的部分

[java]    view plain   copy   在CODE上查看代碼片   派生到個人代碼片  

  1. /**@author qilin**/  
        public void addDataSource(Object key,Object dataSource){  
              
            this.targetDataSources.put(key, dataSource);  
              
            setTargetDataSources(this.targetDataSources);  
        }

3.DynamicDataSource.class  這個類中有一個地方須要注意,當咱們添加數據,切換了數據源,要通知當前spring容器,須要調用父類的super.afterPropertiesSet();方法

[java]    view plain   copy   在CODE上查看代碼片   派生到個人代碼片  

  1. package qilin.utils;  
      
    import java.util.Map;  
      
    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;  
    import org.springframework.jdbc.datasource.lookup.DataSourceLookup;  
      
    public class DynamicDataSource extends AbstractRoutingDataSource {  
      
        /*  
         * 該方法必需要重寫  方法是爲了根據數據庫標示符取得當前的數據庫 
         */  
        @Override  
        public Object determineCurrentLookupKey() {  
            return DataSourceContextHolder.getDataSourceName();  
        }  
      
        @Override  
        public void setDataSourceLookup(DataSourceLookup dataSourceLookup) {  
            super.setDataSourceLookup(dataSourceLookup);  
        }  
      
        @Override  
        public void setDefaultTargetDataSource(Object defaultTargetDataSource) {  
            super.setDefaultTargetDataSource(defaultTargetDataSource);  
        }  
      
        @Override  
        public void setTargetDataSources(Map targetDataSources) {  
            super.setTargetDataSources(targetDataSources);  
            //重點  
            super.afterPropertiesSet();  
        }  
      
    }

4.動態加載數據源配置文件DynamicLoadBean.class

[java]    view plain   copy   在CODE上查看代碼片   派生到個人代碼片  

  1. package qilin.utils;  
      
    import java.io.IOException;  
      
    import org.springframework.beans.BeansException;  
    import org.springframework.beans.factory.config.BeanDefinition;  
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;  
    import org.springframework.beans.factory.support.ChildBeanDefinition;  
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;  
    import org.springframework.beans.factory.xml.ResourceEntityResolver;  
    import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;  
    import org.springframework.context.ApplicationContext;  
    import org.springframework.context.ApplicationContextAware;  
    import org.springframework.context.ConfigurableApplicationContext;  
      
    /** 
     * 動�?�加載數據源 
     * @author qilin 
     * 
     */  
    public class DynamicLoadBean implements ApplicationContextAware {  
      
        private ConfigurableApplicationContext applicationContext = null;  
        public void setApplicationContext(ApplicationContext applicationContext)  
                throws BeansException {  
      
                this.applicationContext = (ConfigurableApplicationContext) applicationContext;  
        }  
      
        public ConfigurableApplicationContext getApplicationContext() {    
            return applicationContext;    
        }  
          
        /** 
         * 1.配置文件的位置固�? 
         * 2.配置文件中bean的名字已�? 
         * @param configLocationString 
         */  
         public void loadBean(String fileName)  
            {    
                XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader((BeanDefinitionRegistry)getApplicationContext().getBeanFactory());    
                beanDefinitionReader.setResourceLoader(getApplicationContext());    
                beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(getApplicationContext()));    
                try  
                {    
                    beanDefinitionReader.loadBeanDefinitions(getApplicationContext().getResources(fileName));    
                } catch (BeansException e) {    
                    e.printStackTrace();    
                } catch (IOException e) {    
                    e.printStackTrace();    
                }    
            }    
      
         public void registBean(String beanName, String parentName) {  
             DefaultListableBeanFactory  fcy = (DefaultListableBeanFactory)applicationContext.getAutowireCapableBeanFactory();  
             BeanDefinition beanDefinition  = new ChildBeanDefinition(parentName);  
             fcy.registerBeanDefinition(beanName, beanDefinition);  
         }  
    }

5.DataSourceContextHolder.class

[java]    view plain   copy   在CODE上查看代碼片   派生到個人代碼片  

  1. package qilin.utils;  
      
      
    public class DataSourceContextHolder {  
      
        private static final ThreadLocal contextHolder=new ThreadLocal();  
          
        public static void setDataSourceType(String dataSourceName){  
            contextHolder.set(dataSourceName);  
        }  
          
        public static String getDataSourceName(){  
            return (String) contextHolder.get();  
        }  
          
        public static void clearDataSourceType(){  
            contextHolder.remove();  
        }  
          
    }

5.測試代碼

[java]    view plain   copy   在CODE上查看代碼片   派生到個人代碼片  

  1. package junit;  
      
    import java.util.HashMap;  
    import java.util.Map;  
      
    import org.springframework.context.ApplicationContext;  
    import org.springframework.context.support.ClassPathXmlApplicationContext;  
      
    import com.mchange.v2.c3p0.ComboPooledDataSource;  
      
    import qilin.entity.Student;  
    import qilin.service.StuService;  
    import qilin.utils.DataSourceContextHolder;  
    import qilin.utils.DynamicDataSource;  
    import qilin.utils.DynamicLoadBean;  
      
    public class Test {  
        public static void main(String[] args) {  
            ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");  
              
            StuService stuService = (StuService) ac.getBean("stuServiceImpl");  
              
            Student stu = new Student();  
              
            stu.setStuName("qilin");  
              
            stuService.save(stu);  
              
            DataSourceContextHolder.setDataSourceType("dataSourceA");  
              
              
            Student _stu = new Student();  
              
            _stu.setStuName("_qilin1");  
              
            stuService.save(_stu);  
              
            //建立一個數據源  
            DynamicLoadBean dynamicBeanLoad =(DynamicLoadBean)ac.getBean("dynamicLoadBean");     
            dynamicBeanLoad.loadBean("classpath:mos/qqq.xml");   
            ComboPooledDataSource dataSource = (ComboPooledDataSource) ac.getBean("qqq");  
            System.err.println(dataSource.getDriverClass());  
              
              
              
            DynamicDataSource dynamicDataSource = (DynamicDataSource) ac.getBean("dynamicdatasource");  
             
    //        Map<String, ComboPooledDataSource> targetDataSources = new HashMap<String, ComboPooledDataSource>();  
              
    //        targetDataSources.put("qqq", dataSource);  
              
    //        Map<Object, Object> targetDataSources = dynamicDataSource.getTargetDataSources();  
              
    //        targetDataSources.put("qqq", dataSource);  
              
    //        dynamicDataSource.setTargetDataSources(targetDataSources);  
              
    //        dynamicDataSource.setDefaultTargetDataSource(dataSource);  
              
            dynamicDataSource.addDataSource("qqq", dataSource);  
              
            DataSourceContextHolder.setDataSourceType("qqq");  
              
            Student __stu = new Student();  
              
            __stu.setStuName("qilin2");  
              
            stuService.save(__stu);  
              
        }  
    }


 

切換數據源就一句話搞定  DataSourceContextHolder.setDataSourceType("qqq");

推薦個 百度網盤搜索神器 https://www.souwp.cn

相關文章
相關標籤/搜索