設(shè)計(jì)邏輯:
從網(wǎng)站建設(shè)到定制行業(yè)解決方案,為提供網(wǎng)站建設(shè)、成都網(wǎng)站制作服務(wù)體系,各種行業(yè)企業(yè)客戶提供網(wǎng)站建設(shè)解決方案,助力業(yè)務(wù)快速發(fā)展。創(chuàng)新互聯(lián)公司將不斷加快創(chuàng)新步伐,提供優(yōu)質(zhì)的建站服務(wù)。
1、要設(shè)計(jì)一個(gè)能夠存儲商品信息和庫存數(shù)量的數(shù)據(jù)結(jié)構(gòu),一般存儲在數(shù)據(jù)庫中
2、通過對應(yīng)的商品進(jìn)行出庫和入庫,對應(yīng)累計(jì)或減少庫存
3、打印全部商品信息和數(shù)量
在我們的項(xiàng)目中遇到這樣一個(gè)問題:我們的項(xiàng)目需要連接多個(gè)數(shù)據(jù)庫,而且不同的客戶在每次訪問中根據(jù)需要會去訪問不同的數(shù)據(jù)庫。我們以往在spring和hibernate框架中總是配置一個(gè)數(shù)據(jù)源,因而sessionFactory的dataSource屬性總是指向這個(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ì)模式可以利用呢?問題的分析我首先想到在spring的applicationContext中配置所有的dataSource。這些dataSource可能是各種不同類型的,比如不同的數(shù)據(jù)庫:Oracle、SQLServer、MySQL等,也可能是不同的數(shù)據(jù)源:比如apache提供的org.apache.commons.dbcp.BasicDataSource、spring提供的org.springframework.jndi.JndiObjectFactoryBean等。然后sessionFactory根據(jù)客戶的每次請求,將dataSource屬性設(shè)置成不同的數(shù)據(jù)源,以到達(dá)切換數(shù)據(jù)源的目的。但是,我很快發(fā)現(xiàn)一個(gè)問題:當(dāng)多用戶同時(shí)并發(fā)訪問數(shù)據(jù)庫的時(shí)候會出現(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的值,必然會出現(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è)Decorator。Decorator與具體的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.BasicDataSource、spring提供的org.springframework.jndi.JndiObjectFactoryBean等,這些類我們不可能修改它們本身,更不可能對它們一個(gè)個(gè)地修改以實(shí)現(xiàn)動(dòng)態(tài)分配數(shù)據(jù)源的功能,同時(shí),我們又希望使用dataSource的sessionFactory根本就感覺不到這樣的變化。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)類,如BasicDataSource、JndiObjectFactoryBean等。MultiDataSource封裝了具體的dataSource,并實(shí)現(xiàn)了數(shù)據(jù)源動(dòng)態(tài)切換:java代碼publicclassMultiDataSourceimplementsDataSource{privateDataSourcedataSource=null;publicMultiDataSource(DataSourcedataSource){this.dataSource=dataSource;}publicConnectiongetConnection()throwsSQLException{returngetDataSource().getConnection();}//其它DataSource接口應(yīng)當(dāng)實(shí)現(xiàn)的方法publicDataSourcegetDataSource(){returnthis.dataSource;}}publicvoidsetDataSource(DataSourcedataSource){this.dataSource=dataSource;}}客戶在發(fā)出請求的時(shí)候,將dataSourceName放到request中,然后把request中的數(shù)據(jù)源名通過調(diào)用newMultiDataSource(dataSource)時(shí)可以告訴MultiDataSource客戶需要的數(shù)據(jù)源,就可以實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源了。但細(xì)心的朋友會發(fā)現(xiàn)這在單例的情況下就是問題的,因?yàn)镸ultiDataSource在系統(tǒng)中只有一個(gè)對象,它的實(shí)例變量dataSource也只有一個(gè),就如同一個(gè)靜態(tài)變量一般。正因?yàn)槿绱?,單例模式讓許多設(shè)計(jì)模式都不得不需要更改,這將在我的《“單例”更改了我們的設(shè)計(jì)模式》中詳細(xì)討論。那么,我們在單例模式下如何設(shè)計(jì)呢?(三)單例模式下的MultiDataSource在單例模式下,由于我們在每次調(diào)用MultiDataSource的方法的時(shí)候,dataSource都可能是不同的,所以我們不能將dataSource放在實(shí)例變量dataSource中,最簡單的方式就是在方法getDataSource()中增加參數(shù),告訴MultiDataSource我到底調(diào)用的是哪個(gè)dataSource:java代碼publicDataSourcegetDataSource(StringdataSourceName){log.debug("dataSourceName:"+dataSourceName);try{if(dataSourceName==null||dataSourceName.equals("")){returnthis.dataSource;}return(DataSource)this.applicationContext.getBean(dataSourceName);}catch(NoSuchBeanDefinitionExceptionex){thrownewDaoException("ThereisnotthedataSource}}值得一提的是,我需要的數(shù)據(jù)源已經(jīng)都在spring的配置文件中注冊,dataSourceName就是其對應(yīng)的id。xml代碼oracle.jdbc.driver.OracleDrivervaluepropertybeanoracle.jdbc.driver.OracleDrivervaluepropertybean為了得到spring的ApplicationContext,MultiDataSource類必須實(shí)現(xiàn)接口org.springframework.context.ApplicationContextAware,并且實(shí)現(xiàn)方法:java代碼privateApplicationContextapplicationContext=null;publicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{this.applicationContext=applicationContext;}如此這樣,我就可以通過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)過BUS和DAO,也就是說為了把數(shù)據(jù)源名傳給MultiDataSource,BUS和DAO的所有方法都要增加dataSourceName的參數(shù),這是我們不愿看到的。寫一個(gè)類,通過線程的方式跳過BUS和DAO直接傳遞給MultiDataSource是一個(gè)不錯(cuò)的設(shè)計(jì):java代碼publicclassSpObserver{privatestaticThreadLocallocal=newThreadLocal();publicstaticvoidputSp(Stringsp){local.set(sp);}publicstaticStringgetSp(){return(String)local.get();}}做一個(gè)filter,每次客戶發(fā)出請求的時(shí)候就調(diào)用SpObserver.petSp(dataSourceName),將request中的dataSourceName傳遞給SpObserver對象。最后修改MultiDataSource的方法getDataSource():java代碼publicDataSourcegetDataSource(){Stringsp=SpObserver.getSp();returngetDataSource(sp);}完整的MultiDataSource代碼在附件中。(五)動(dòng)態(tài)添加數(shù)據(jù)源通過以上方案,我們解決了動(dòng)態(tài)分配數(shù)據(jù)源的問題,但你可能提出疑問:方案中的數(shù)據(jù)源都是配置在spring的ApplicationContext中,如果我在程序運(yùn)行過程中動(dòng)態(tài)添加數(shù)據(jù)源怎么?這確實(shí)是一個(gè)問題,而且在我們的項(xiàng)目中也確實(shí)遇到。spring的ApplicationContext是在項(xiàng)目啟動(dòng)的時(shí)候加載的。加載以后,我們?nèi)绾蝿?dòng)態(tài)地加載新的bean到ApplicationContext中呢?我想到如果用spring自己的方法解決這個(gè)問題就好了。所幸的是,在查看spring的源代碼后,我找到了這樣的代碼,編寫了DynamicLoadBean類,只要調(diào)用loadBean()方法,就可以將某個(gè)或某幾個(gè)配置文件中的bean加載到ApplicationContext中(見附件)。不通過配置文件直接加載對象,在spring的源碼中也有,感興趣的朋友可以自己研究。(六)在spring中配置在完成了所有這些設(shè)計(jì)以后,我最后再嘮叨一句。我們應(yīng)當(dāng)在spring中做如下配置:xml代碼beanpropertybeanpropertybean其中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的改變。唯一不同的是在真正的dataSource與sessionFactory之間增加了一個(gè)MultiDataSource。其次,實(shí)現(xiàn)簡單,易于維護(hù)。這個(gè)方案雖然我說了這么多東西,其實(shí)都是分析,真正需要我們寫的代碼就只有MultiDataSource、SpObserver兩個(gè)類。MultiDataSource類真正要寫的只有g(shù)etDataSource()和getDataSource(sp)兩個(gè)方法,而SpObserver類更簡單了。實(shí)現(xiàn)越簡單,出錯(cuò)的可能就越小,維護(hù)性就越高。最后,這個(gè)方案可以使單數(shù)據(jù)源與多數(shù)據(jù)源兼容。這個(gè)方案完全不影響B(tài)US和DAO的編寫。如果我們的項(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)行會出錯(cuò),需要將類中AbstractApplicationContext改為org.springframework.context.ConfigurableApplicationContext。
高級工程師。做這樣的東西,最核心的分析人員至少是高級工程師。因?yàn)閺募夹g(shù)上來說都不是很簡單的事
既然是商品庫存系統(tǒng),那么最少有各種商品的單件信息,1:需要有商品的進(jìn)貨價(jià)格,賣出價(jià)格,剩余數(shù)量,每月的銷售數(shù)量,進(jìn)貨時(shí)間等,在對應(yīng)的數(shù)據(jù)庫表創(chuàng)建相應(yīng)的字段。2:商品管理就是對多種商品的管理,所以還要有各種商品的分類,比如煙酒類,飲料類,小吃類,將其分類好管理,同樣數(shù)據(jù)庫里面建立相對的數(shù)據(jù)表。具體需要根據(jù)自己需求來編寫。3:界面的設(shè)計(jì),這里可分為登陸界面,其中一個(gè)是用戶登陸后查看的界面,和管理員登陸后查看的界面,用戶登錄只能查看對應(yīng)的商店的物品管理,并且能進(jìn)行修改自家商品。管理員登陸可查看所有的用戶的商店物品,及修改物品信息。而物品分類欄就可以用jQuery來實(shí)現(xiàn)局部的刷新界面。左邊為物品分類欄,右邊為選中物品類的信息。點(diǎn)擊右邊分類物品的某件物品,可跳轉(zhuǎn)到該類物品的單個(gè)信息,如第1點(diǎn)提到的。