<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    空間站

    北極心空

      BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
      15 Posts :: 393 Stories :: 160 Comments :: 0 Trackbacks

    在我們的項(xiàng)目中遇到這樣一個(gè)問題:我們的項(xiàng)目需要連接多個(gè)數(shù)據(jù)庫,而且不同的客戶在每次訪問中根據(jù)需要會(huì)去訪問不同的數(shù)據(jù)庫。我們以往在springhibernate框架中總是配置一個(gè)數(shù)據(jù)源,因而sessionFactorydataSource屬性總是指向這個(gè)數(shù)據(jù)源并且恒定不變,所有DAO在使用sessionFactory的時(shí)候都是通過這個(gè)數(shù)據(jù)源訪問數(shù)據(jù)庫。但是現(xiàn)在,由于項(xiàng)目的需要,我們的DAO在訪問sessionFactory的時(shí)候都不得不在多個(gè)數(shù)據(jù)源中不斷切換,問題就出現(xiàn)了:如何讓sessionFactory在執(zhí)行數(shù)據(jù)持久化的時(shí)候,根據(jù)客戶的需求能夠動(dòng)態(tài)切換不同的數(shù)據(jù)源?我們能不能在spring的框架下通過少量修改得到解決?是否有什么設(shè)計(jì)模式可以利用呢?  

    問題的分析

    我首先想到在springapplicationContext中配置所有的dataSource。這些dataSource可能是各種不同類型的,比如不同的數(shù)據(jù)庫:OracleSQL ServerMySQL等,也可能是不同的數(shù)據(jù)源:比如apache 提供的org.apache.commons.dbcp.BasicDataSourcespring提供的org.springframework.jndi.JndiObjectFactoryBean等。然后sessionFactory根據(jù)客戶的每次請求,將dataSource屬性設(shè)置成不同的數(shù)據(jù)源,以到達(dá)切換數(shù)據(jù)源的目的。

    但是,我很快發(fā)現(xiàn)一個(gè)問題:當(dāng)多用戶同時(shí)并發(fā)訪問數(shù)據(jù)庫的時(shí)候會(huì)出現(xiàn)資源爭用的問題。這都是“單例模式”惹的禍。眾所周知,我們在使用spring框架的時(shí)候,在beanFactory中注冊的bean基本上都是采用單例模式,即spring在啟動(dòng)的時(shí)候,這些bean就裝載到內(nèi)存中,并且每個(gè)bean在整個(gè)項(xiàng)目中只存在一個(gè)對象。正因?yàn)橹淮嬖谝粋€(gè)對象,對象的所有屬性,更準(zhǔn)確說是實(shí)例變量,表現(xiàn)得就如同是個(gè)靜態(tài)變量(實(shí)際上“靜態(tài)”與“單例”往往是非常相似的兩個(gè)東西,我們常常用“靜態(tài)”來實(shí)現(xiàn)“單例”)。拿我們的問題來說,sessionFactory在整個(gè)項(xiàng)目中只有一個(gè)對象,它的實(shí)例變量dataSource也就只有一個(gè),就如同一個(gè)靜態(tài)變量一般。如果不同的用戶都不斷地去修改dataSource的值,必然會(huì)出現(xiàn)多用戶爭用一個(gè)變量的問題,對系統(tǒng)產(chǎn)生隱患。

    通過以上的分析,解決多數(shù)據(jù)源訪問問題的關(guān)鍵,就集中在sessionFactory在執(zhí)行數(shù)據(jù)持久化的時(shí)候,能夠通過某段代碼去根據(jù)客戶的需要?jiǎng)討B(tài)切換數(shù)據(jù)源,并解決資源爭用的問題。

    問題的解決

    (一)            采用Decorator設(shè)計(jì)模式

    要解決這個(gè)問題,我的思路鎖定在了這個(gè)dataSource上了。如果sessionFactory指向的dataSource可以根據(jù)客戶的需求去連接客戶所需要的真正的數(shù)據(jù)源,即提供動(dòng)態(tài)切換數(shù)據(jù)源的功能,那么問題就解決了。那么我們怎么做呢?去修改那些我們要使用的dataSource源碼嗎?這顯然不是一個(gè)好的方案,我們希望我們的修改與原dataSource代碼是分離的。根據(jù)以上的分析,使用GoF設(shè)計(jì)模式中的Decorator模式(裝飾者模式)應(yīng)當(dāng)是我們可以選擇的最佳方案。

    什么是“Decorator模式”?簡單點(diǎn)兒說就是當(dāng)我們需要修改原有的功能,但我們又不愿直接去修改原有的代碼時(shí),設(shè)計(jì)一個(gè)Decorator套在原有代碼外面。當(dāng)我們使用Decorator的時(shí)候與原類完全一樣,當(dāng)Decorator的某些功能卻已經(jīng)修改為了我們需要修改的功能。Decorator模式的結(jié)構(gòu)如圖。

    我們本來需要修改圖中所有具體的Component類的一些功能,但卻并不是去直接修改它們的代碼,而是在它們的外面增加一個(gè)DecoratorDecorator與具體的Component類都是繼承的AbstractComponent,因此它長得和具體的Component類一樣,也就是說我們在使用Decorator的時(shí)候就如同在使用ConcreteComponentA或者ConcreteComponentB一樣,甚至那些使用ConcreteComponentA或者ConcreteComponentB的客戶程序都不知道它們用的類已經(jīng)改為了Decorator,但是Decorator已經(jīng)對具體的Component類的部分方法進(jìn)行了修改,執(zhí)行這些方法的結(jié)果已經(jīng)不同了。

    (二)            設(shè)計(jì)MultiDataSource

    現(xiàn)在回到我們的問題,我們需要對dataSource的功能進(jìn)行變更,但又不希望修改dataSource中的任何代碼。我這里指的dataSource是所有實(shí)現(xiàn)javax.sql.DataSource接口的類,我們常用的包括apache 提供的org.apache.commons.dbcp.BasicDataSourcespring提供的org.springframework.jndi.JndiObjectFactoryBean等,這些類我們不可能修改它們本身,更不可能對它們一個(gè)個(gè)地修改以實(shí)現(xiàn)動(dòng)態(tài)分配數(shù)據(jù)源的功能,同時(shí),我們又希望使用dataSourcesessionFactory根本就感覺不到這樣的變化。Decorator模式就正是解決這個(gè)問題的設(shè)計(jì)模式。

    首先寫一個(gè)Decorator類,我取名叫MultiDataSource,通過它來動(dòng)態(tài)切換數(shù)據(jù)源。同時(shí)在配置文件中將sessionFactory的dataSource屬性由原來的某個(gè)具體的dataSource改為MultiDataSource。如圖:

    對比原Decorator模式,AbstractComponent是一個(gè)抽象類,但在這里我們可以將這個(gè)抽象類用接口來代替,即DataSource接口,而ConcreteComponent就是那些DataSource的實(shí)現(xiàn)類,如BasicDataSourceJndiObjectFactoryBean等。MultiDataSource封裝了具體的dataSource,并實(shí)現(xiàn)了數(shù)據(jù)源動(dòng)態(tài)切換:

    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接口應(yīng)當(dāng)實(shí)現(xiàn)的方法   
    13.   
    14.     public DataSource getDataSource(){   
    15.         return this.dataSource;   
    16.         }   
    17.     }   
    18.     public void setDataSource(DataSource dataSource) {   
    19.         this.dataSource = dataSource;   
    20.     }   
    21. }   

    客戶在發(fā)出請求的時(shí)候,將dataSourceName放到request中,然后把request中的數(shù)據(jù)源名通過調(diào)用new MultiDataSource(dataSource)時(shí)可以告訴MultiDataSource客戶需要的數(shù)據(jù)源,就可以實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源了。但細(xì)心的朋友會(huì)發(fā)現(xiàn)這在單例的情況下就是問題的,因?yàn)?/span>MultiDataSource在系統(tǒng)中只有一個(gè)對象,它的實(shí)例變量dataSource也只有一個(gè),就如同一個(gè)靜態(tài)變量一般。正因?yàn)槿绱耍?/span>單例模式讓許多設(shè)計(jì)模式都不得不需要更改,這將在我的《“單例”更改了我們的設(shè)計(jì)模式》中詳細(xì)討論。那么,我們在單例模式下如何設(shè)計(jì)呢?

    (三)            單例模式下的MultiDataSource

    在單例模式下,由于我們在每次調(diào)用MultiDataSource的方法的時(shí)候,dataSource都可能是不同的,所以我們不能將dataSource放在實(shí)例變量dataSource中,最簡單的方式就是在方法getDataSource()中增加參數(shù),告訴MultiDataSource我到底調(diào)用的是哪個(gè)dataSource

    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.     }   

    值得一提的是,我需要的數(shù)據(jù)源已經(jīng)都在spring的配置文件中注冊,dataSourceName就是其對應(yīng)的id

    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>   

    為了得到springApplicationContextMultiDataSource類必須實(shí)現(xiàn)接口org.springframework.context.ApplicationContextAware,并且實(shí)現(xiàn)方法:

    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

    查看以上設(shè)計(jì),MultiDataSource依然無法運(yùn)行,因?yàn)橛脩粼诎l(fā)出請求時(shí),他需要連接什么數(shù)據(jù)庫,其數(shù)據(jù)源名是放在request中的,要將request中的數(shù)據(jù)源名傳給MultiDataSource,需要經(jīng)過BUSDAO,也就是說為了把數(shù)據(jù)源名傳給MultiDataSourceBUSDAO的所有方法都要增加dataSourceName的參數(shù),這是我們不愿看到的。寫一個(gè)類,通過線程的方式跳過BUSDAO直接傳遞給MultiDataSource是一個(gè)不錯(cuò)的設(shè)計(jì):

    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. }   

    做一個(gè)filter,每次客戶發(fā)出請求的時(shí)候就調(diào)用SpObserver.petSp(dataSourceName),將request中的dataSourceName傳遞給SpObserver對象。最后修改MultiDataSource的方法getDataSource()

    java 代碼
    1. public DataSource getDataSource(){   
    2.         String sp = SpObserver.getSp();   
    3.         return getDataSource(sp);   
    4.     }   

    完整的MultiDataSource代碼在附件中。

    (五)            動(dòng)態(tài)添加數(shù)據(jù)源

    通過以上方案,我們解決了動(dòng)態(tài)分配數(shù)據(jù)源的問題,但你可能提出疑問:方案中的數(shù)據(jù)源都是配置在springApplicationContext中,如果我在程序運(yùn)行過程中動(dòng)態(tài)添加數(shù)據(jù)源怎么辦?這確實(shí)是一個(gè)問題,而且在我們的項(xiàng)目中也確實(shí)遇到。springApplicationContext是在項(xiàng)目啟動(dòng)的時(shí)候加載的。加載以后,我們?nèi)绾蝿?dòng)態(tài)地加載新的beanApplicationContext中呢?我想到如果用spring自己的方法解決這個(gè)問題就好了。所幸的是,在查看spring的源代碼后,我找到了這樣的代碼,編寫了DynamicLoadBean類,只要調(diào)用loadBean()方法,就可以將某個(gè)或某幾個(gè)配置文件中的bean加載到ApplicationContext中(見附件)。不通過配置文件直接加載對象,在spring的源碼中也有,感興趣的朋友可以自己研究。

    (六)            spring中配置

    在完成了所有這些設(shè)計(jì)以后,我最后再嘮叨一句。我們應(yīng)當(dāng)在spring中做如下配置:

    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屬性實(shí)際上更準(zhǔn)確地說應(yīng)當(dāng)是defaultDataSource,即spring啟動(dòng)時(shí)以及在客戶沒有指定數(shù)據(jù)源時(shí)應(yīng)當(dāng)指定的默認(rèn)數(shù)據(jù)源。

    該方案的優(yōu)勢

    以上方案與其它方案相比,它有哪些優(yōu)勢呢?

    首先,這個(gè)方案完全是在spring的框架下解決的,數(shù)據(jù)源依然配置在spring的配置文件中,sessionFactory依然去配置它的dataSource屬性,它甚至都不知道dataSource的改變。唯一不同的是在真正的dataSourcesessionFactory之間增加了一個(gè)MultiDataSource

    其次,實(shí)現(xiàn)簡單,易于維護(hù)。這個(gè)方案雖然我說了這么多東西,其實(shí)都是分析,真正需要我們寫的代碼就只有MultiDataSourceSpObserver兩個(gè)類。MultiDataSource類真正要寫的只有getDataSource()getDataSource(sp)兩個(gè)方法,而SpObserver類更簡單了。實(shí)現(xiàn)越簡單,出錯(cuò)的可能就越小,維護(hù)性就越高。

    最后,這個(gè)方案可以使單數(shù)據(jù)源與多數(shù)據(jù)源兼容。這個(gè)方案完全不影響BUSDAO的編寫。如果我們的項(xiàng)目在開始之初是單數(shù)據(jù)源的情況下開發(fā),隨著項(xiàng)目的進(jìn)行,需要變更為多數(shù)據(jù)源,則只需要修改spring配置,并少量修改MVC層以便在請求中寫入需要的數(shù)據(jù)源名,變更就完成了。如果我們的項(xiàng)目希望改回單數(shù)據(jù)源,則只需要簡單修改配置文件。這樣,為我們的項(xiàng)目將增加更多的彈性。

    特別說明:實(shí)例中的DynamicLoadBean在web環(huán)境下運(yùn)行會(huì)出錯(cuò),需要將類中AbstractApplicationContext改為org.springframework.context.ConfigurableApplicationContext。

    相關(guān)博客:再析在spring框架中解決多數(shù)據(jù)源的問題

    • example.rar (32.1 KB)
    • 描述: 源碼及示例
    • 下載次數(shù): 1214

    再析在spring框架中解決多數(shù)據(jù)源的問題

    關(guān)鍵字: hibernate decorator 設(shè)計(jì)模式
    在前面我寫了《如何在spring框架中解決多數(shù)據(jù)源的問題》,通過設(shè)計(jì)模式中的Decorator模式在spring框架中解決多數(shù)據(jù)源的問題,得到了許多網(wǎng)友的關(guān)注。在與網(wǎng)友探討該問題的過程中,我發(fā)現(xiàn)我的方案并不完善,它只解決了一部分問題。

    總結(jié)多數(shù)據(jù)源的問題,其實(shí)它需要分為以下三種情況:各個(gè)數(shù)據(jù)源的數(shù)據(jù)結(jié)構(gòu)不同、各個(gè)數(shù)據(jù)源的數(shù)據(jù)結(jié)構(gòu)相同、各個(gè)數(shù)據(jù)源的數(shù)據(jù)結(jié)構(gòu)部分相同又有部分不同。對于第二種情況,各個(gè)數(shù)據(jù)源的數(shù)據(jù)結(jié)構(gòu)相同,我們使用一個(gè)sessionFactory,而在sessionFactory中通過MultiDataSource來動(dòng)態(tài)切換數(shù)據(jù)源,應(yīng)當(dāng)是一個(gè)不錯(cuò)的方案,既解決了多個(gè)sessionFactory對相同的值對象重復(fù)裝載對內(nèi)存的浪費(fèi),又使數(shù)據(jù)源的切換對客戶程序透明,簡化了代碼的實(shí)現(xiàn)和對客戶程序的影響。但是,對于第一種情況,各個(gè)數(shù)據(jù)源的數(shù)據(jù)結(jié)構(gòu)不同,運(yùn)用這樣的方案存在潛在風(fēng)險(xiǎn)。

    對于各個(gè)數(shù)據(jù)源的數(shù)據(jù)結(jié)構(gòu)不同的情況,使用一個(gè)sessionFactory而在這個(gè)sessionFactory中動(dòng)態(tài)切換數(shù)據(jù)源,可能造成數(shù)據(jù)訪問的張冠李戴。譬如,數(shù)據(jù)源A有表T而數(shù)據(jù)源B沒有,可能造成客戶程序在訪問表T的時(shí)候卻嘗試去連接數(shù)據(jù)源B,因?yàn)榭蛻舫绦蛟L問哪個(gè)數(shù)據(jù)源是在程序運(yùn)行期間由客戶程序決定的,因此這樣的錯(cuò)誤是很難發(fā)現(xiàn)的。也許客戶程序的一個(gè)不經(jīng)意的錯(cuò)誤就可能造成錯(cuò)誤。解決這個(gè)問題的方法有兩個(gè):一是嚴(yán)格要求客戶程序不要寫錯(cuò),這當(dāng)然是可以做到的,但作為框架設(shè)計(jì)者,另一個(gè)解決方法是在框架中就避免出現(xiàn)這樣的情況。因此我祭出了MultiSessionFactory的方案來解決各個(gè)數(shù)據(jù)源的數(shù)據(jù)結(jié)構(gòu)不同的多數(shù)據(jù)源問題。

    問題的分析

    MultiDataSource的方案一樣,MultiSessionFactory同樣是在spring框架下調(diào)用ApplicationContextgetBean()方法而不會(huì)另外創(chuàng)建beanFacoty,也同樣使用Decorator模式來處理切換的問題。MultiSessionFactory的對象關(guān)系如圖:

    在該方案中,SessionFactory就是Hibernateorg.hibernate.SessionFactory接口,Decorator就是MultiSessionFactorySessionFactory1SessionFactory2往往是springorg.springframework.orm.hibernate3.LocalSessionFactoryBean。細(xì)心的朋友可能會(huì)注意,實(shí)際上LocalSessionFactoryBean并不是SessionFactory的實(shí)現(xiàn),這個(gè)方案是否有問題呢?這個(gè)問題其實(shí)也一直困擾了我好久,最后我發(fā)現(xiàn),我們通過ApplicationContextgetBean()得到一個(gè)LocalSessionFactoryBean的時(shí)候其實(shí)并不是真正地得到了它,而是得到了一個(gè)SessionFactory,因?yàn)?/span>springLocalSessionFactoryBean重寫了getObject(),使其返回的是SessionFactory。一個(gè)簡單的明證就是,HibernateDaoSupportsessionFactory屬性的類型是SessionFactory,而我們在spring配置的時(shí)候注入的卻是LocalSessionFactoryBean

    方案的實(shí)現(xiàn)

    在整個(gè)這個(gè)方案中,我們需要實(shí)現(xiàn)的只有MultiSessionFactory類和我們可愛的Spserver,總共就兩個(gè)類,然后呢就是一些spring的配置,就完成了。

    MultiSessionFactory實(shí)現(xiàn)了SessionFactory,同時(shí)為了得到AplicationContext而實(shí)現(xiàn)了ApplicationContextAwareMultiSessionFactory的代碼如下:

    java 代碼
    1. public class MultiSessionFactory implements SessionFactory, ApplicationContextAware {   
    2.     private static final long serialVersionUID = 2064557324203496378L;   
    3.     private static final Log log = LogFactory.getLog(MultiSessionFactory.class);   
    4.     private ApplicationContext applicationContext = null;   
    5.     private SessionFactory sessionFactory = null;   
    6.     public ApplicationContext getApplicationContext() {   
    7.         return applicationContext;   
    8.     }   
    9.     public void setApplicationContext(ApplicationContext applicationContext) {   
    10.        this.applicationContext = applicationContext;   
    11.     }   
    12.     public SessionFactory getSessionFactory(String sessionFactoryName) {   
    13.        log.debug("sessionFactoryName:"+sessionFactoryName);   
    14.        try{   
    15.            if(sessionFactoryName==null||sessionFactoryName.equals("")){   
    16.               return sessionFactory;   
    17.            }   
    18.            return (SessionFactory)this.getApplicationContext().getBean(sessionFactoryName);   
    19.        }catch(NoSuchBeanDefinitionException ex){   
    20.            throw new DaoException("There is not the sessionFactory 
    21.        }   
    22.     }   
    23.   
    24.     public SessionFactory getSessionFactory() {   
    25.        String sessionFactoryName = SpObserver.getSp();   
    26.        return getSessionFactory(sessionFactoryName);   
    27.     }   
    28.   
    29.     public void setSessionFactory(SessionFactory sessionFactory) {   
    30.        this.sessionFactory = sessionFactory;   
    31.     }   
    32.   
    33.     // SessionFactory接口需要實(shí)現(xiàn)的方法   
    34.   
    35. ......   
    36.   
    37. }  

    MultiSessionFactory的完整代碼見我提供的附件。setSessionFactory()實(shí)際上是設(shè)定的默認(rèn)sessionFactory,它在spring裝載的時(shí)候調(diào)用,其對應(yīng)的數(shù)據(jù)源應(yīng)當(dāng)是主數(shù)據(jù)源,即項(xiàng)目初始化中需要讀取初始化數(shù)據(jù)的數(shù)據(jù)源。在任何多數(shù)據(jù)源項(xiàng)目中,都應(yīng)當(dāng)有一個(gè)存放初始化數(shù)據(jù)、系統(tǒng)維護(hù)數(shù)據(jù)、用戶權(quán)限數(shù)據(jù)的數(shù)據(jù)源,這就是主數(shù)據(jù)源。因此MultiSessionFactory的配置應(yīng)當(dāng)這樣寫:

    xml 代碼
    1. <bean id="sessionFactory" class="com.htxx.service.dao.MultiSessionFactory">  
    2.     <property name="sessionFactory"><ref bean="hostSessionFactory"/>property> 
    3. >  

    SpServer的寫法與《如何在spring框架中解決多數(shù)據(jù)源的問題》中的一樣,我就不再累贅了。

    另外,在spring配置中配置多個(gè)數(shù)據(jù)源,每個(gè)數(shù)據(jù)源對應(yīng)一個(gè)sessionFactory,這個(gè)對應(yīng)的sessionFactory中的值對象應(yīng)當(dāng)是該數(shù)據(jù)源的值對象。客戶程序在執(zhí)行數(shù)據(jù)訪問前,通過調(diào)用SpServerputSp()方法,告訴MultiSessionFactory需要切換到哪個(gè)sessionFactory,然后執(zhí)行數(shù)據(jù)訪問。這樣,不同數(shù)據(jù)源的值對象通過放在不同的sessionFactory中,避免了張冠李戴的情況。具體的示例見附件的MultiSessionFactoryTest

    另外的方案

    也許有些朋友對以上方案還不滿意,因?yàn)樵趫?zhí)行數(shù)據(jù)訪問前畢竟還要多做一步指定sessionFactory的工作。實(shí)際上,對于各個(gè)數(shù)據(jù)源的數(shù)據(jù)結(jié)構(gòu)不同的項(xiàng)目,一個(gè)值對象應(yīng)當(dāng)使用哪個(gè)數(shù)據(jù)源有一個(gè)非常確定的對應(yīng)關(guān)系。如果通過配置文件將值對象與它的sessionFactory對應(yīng)起來,那么我們在執(zhí)行數(shù)據(jù)訪問的時(shí)候傳遞的是哪個(gè)值對象,MultiSessionFactory馬上就可以去找到對應(yīng)的sessionFactory。這個(gè)方案你可以通過AOP來制作一個(gè)攔截器攔截所有諸如save()delete()get()load()等方法來實(shí)現(xiàn),也可以擴(kuò)展HibernateDaoSupport來實(shí)現(xiàn)。這樣的方案使客戶程序甚至都不用知道他是在操作的一個(gè)多數(shù)據(jù)源系統(tǒng)。當(dāng)然,這個(gè)方案感興趣的朋友可以自己去實(shí)現(xiàn)。

    另外,在這個(gè)方案中的核心是運(yùn)用Decorator設(shè)計(jì)模式來解決切換sessionFactory的目的,即MultiSessionFactory的實(shí)現(xiàn)。至于通過什么方式來通知MultiSessionFactory應(yīng)當(dāng)切換到哪個(gè)SessionFactory,可以根據(jù)不同項(xiàng)目的情況自由選擇。我在這里給大家提供了通過SpOberver和建立值對象與sessionFactory關(guān)系的配置文件這兩個(gè)方案,你也可以有自己的方案解決。

    第三種情況的解決方案

    前面我已經(jīng)給出了第一種和第二種情況的解決方案:各個(gè)數(shù)據(jù)源的數(shù)據(jù)結(jié)構(gòu)不同的情況用MultiSessionFactory解決;各個(gè)數(shù)據(jù)源的數(shù)據(jù)結(jié)構(gòu)相同的情況用MultiDataSource解決。那么第三種情況,各個(gè)數(shù)據(jù)源的數(shù)據(jù)結(jié)構(gòu)部分相同又有部分不同,又應(yīng)當(dāng)如何解決呢?當(dāng)然是將MultiSessionFactoryMultiDataSource結(jié)合起來解決。對于數(shù)據(jù)結(jié)構(gòu)不同的部分,其分別創(chuàng)建各自的sessionFactory然后通過MultiSessionFactory來切換,而對于數(shù)據(jù)結(jié)構(gòu)相同的部分,建立共同的sessionFactory和多個(gè)不同的dataSource然后通過MultiDataSource來切換就可以了。

    還有的朋友問到這樣的方案其事務(wù)處理和二級(jí)緩存的情況。這個(gè)方案是在spring框架下的解決方案,其事務(wù)處理的能力也是由spring的能力來決定的。目前spring要處理跨數(shù)據(jù)庫的事務(wù)處理是通過JTA來實(shí)現(xiàn)的,這種方式在該方案中同樣可以實(shí)現(xiàn),朋友們可以試一試。另外,本方案能使用二級(jí)緩存嗎?當(dāng)然可以。對于MultiSessionFactory當(dāng)然沒有任何問題,它通過不同的sessionFactory分離開了不同的數(shù)據(jù)源和值對象,我們可以毫無顧忌地使用。對于MultiDataSource來說,就有點(diǎn)問題了。MultiDataSource使多個(gè)數(shù)據(jù)源使用共同的sessionFactory,因此它仿佛就是將多個(gè)數(shù)據(jù)源在邏輯上合并為一個(gè)數(shù)據(jù)源。正因?yàn)槿绱耍覀冃枰WC對于同一個(gè)表在所有數(shù)據(jù)源中都要主鍵唯一。什么意思呢?數(shù)據(jù)源A和數(shù)據(jù)源B都有表T,如果數(shù)據(jù)源A中的表T擁有ID001的一條數(shù)據(jù),那么在數(shù)據(jù)源B的表T中就不能有ID001的記錄。如果你總是通過MultiDataSource來執(zhí)行表的插入操作,并且使用uuid.hex生成主鍵,這當(dāng)然不會(huì)有問題。但如果你有通過其它方式插入表的操作,你應(yīng)當(dāng)保證這樣的唯一性。另外,對于查詢的操作,緩存中存放的既可能是數(shù)據(jù)源A的數(shù)據(jù),也可能是數(shù)據(jù)源B的數(shù)據(jù),因此你應(yīng)當(dāng)對數(shù)據(jù)有一個(gè)規(guī)劃。對于表T的數(shù)據(jù),哪些應(yīng)當(dāng)插入到數(shù)據(jù)源A中,哪些應(yīng)當(dāng)插入到B中,應(yīng)當(dāng)有一個(gè)定義。假如是通過不同單位來決定插入哪個(gè)數(shù)據(jù)源,那么在查詢數(shù)據(jù)源A的表T是,應(yīng)當(dāng)增加條件只查詢數(shù)據(jù)源A應(yīng)當(dāng)有的單位而排除調(diào)其它單位。如此這樣,你只要注意到這兩個(gè)問題,你就可以放心大膽地使用二級(jí)緩存。
    • example.rar (16.4 KB)
    • 描述: 示例文件
    • 下載次數(shù): 773

    本人感覺這是不錯(cuò)的一種多數(shù)據(jù)源的解決方案,當(dāng)然還有其他的辦法,原帖的回復(fù)也有很有意義,建議查看原帖:
    http://fangang.javaeye.com/blog/72486
    http://fangang.javaeye.com/blog/91667

    http://www.javaeye.com/topic/72486?page=1
    posted on 2008-11-13 11:21 蘆葦 閱讀(1023) 評(píng)論(0)  編輯  收藏 所屬分類: Hibernate
    主站蜘蛛池模板: 亚洲春色在线视频| 亚洲精品国产va在线观看蜜芽| 久久久亚洲精品国产| a级毛片免费完整视频| 中文字幕亚洲激情| 97在线免费观看视频| 亚洲人成网亚洲欧洲无码久久| 国产免费一级高清淫曰本片| 亚洲国产主播精品极品网红| 一级特黄a大片免费| 亚洲中文字幕无码久久2017| 不卡视频免费在线观看| 亚洲成AV人片在线观看| 67194成手机免费观看| 伊人久久五月丁香综合中文亚洲| 午夜dj免费在线观看| 免费人成视频在线播放| 亚洲欧洲日产国码av系列天堂 | 亚洲日本中文字幕区| 5555在线播放免费播放| 亚洲欧美日韩一区二区三区在线| 国产免费小视频在线观看| 一级大黄美女免费播放| 亚洲AV日韩精品久久久久久| 午夜性色一区二区三区免费不卡视频| 亚洲乱码在线观看| 亚洲精品国产综合久久一线| 久久久国产精品福利免费| 亚洲成人免费网址| 亚洲国产V高清在线观看| 久久99免费视频| 亚洲中文字幕AV每天更新| 国产成人精品曰本亚洲79ren| 久久亚洲免费视频| 久久无码av亚洲精品色午夜| 亚洲人成色7777在线观看| 最近中文字幕mv免费高清视频7| 黄页网址大全免费观看12网站| 亚洲国产精品无码久久一线| 欧美a级在线现免费观看| 国产精品免费观看视频|