詳談XCode學(xué)習(xí)基礎(chǔ)之ORM組件
詳談XCode學(xué)習(xí)基礎(chǔ)之ORM組件是本文熬介紹的內(nèi)容,之前,XCode總是若隱若現(xiàn),耐性好的同學(xué)想知道它還有啥特點,沉不住氣的則認(rèn)為不過是CURD耳!不多說,先來看詳細(xì)內(nèi)容。
XCode開發(fā)模式是靈魂,XCode組件通過具體實現(xiàn)對其支持!
XCode的特點如下:
1、基本的CURD功能
實在想不出來不支持CURD的ORM算不算ORM;也實在想不出來僅有CURD的ORM算不算ORM。因而,這是0號功能!
XCode的CURD通過反射實體類生成查詢和操作SQL實現(xiàn),數(shù)據(jù)庫結(jié)構(gòu)信息通過特性附在實體類上。之所以選擇SQL而不是DbCommand,因為XCode的實體層和數(shù)據(jù)訪問層是分開的,目前是為了實現(xiàn)一級緩存,將來會在這里實現(xiàn)分布式數(shù)據(jù)訪問。
2、完美支持ObjectDataSource
XCode實現(xiàn)充血模型(脹血模型)的實體類,提供ObjectDataSource需要的所有方法和參數(shù),特別支持分頁和排序功能!
詳見《與ObjectDataSource共舞》
3、全面分頁支持
只有從小處開始培養(yǎng)分頁的思想,任何查詢都指定所需獲取數(shù)據(jù)范圍,才能保證系統(tǒng)數(shù)據(jù)變大時系統(tǒng)不會拓機。
XCode的分頁以任意查詢語句為基礎(chǔ),支持統(tǒng)計等非常復(fù)雜的查詢分頁。并且會根據(jù)當(dāng)前數(shù)據(jù)庫類型以及版本選擇最佳分頁方案。
詳見《撬動千萬級數(shù)據(jù)》
4、實體集合支持
實體集合EntityList<TEntity>繼承自List<TEntity>,提供了實體的批量操作。實際上還是通過遍歷集合逐個進(jìn)行實體操作,因為充血模型的實體類可能是通過重載修改CURD的行為,所以不能使用一個SQL語句操作一批實體,XCode不會做這種可能會影響使用的小把戲。
實體集合還提供了一些方便查詢和排序的簡便方法,實體緩存中將會大量使用。
5、萬能的一級緩存
一級緩存由數(shù)據(jù)訪問層實現(xiàn),以查詢SQL為鍵,返回的數(shù)據(jù)集為值,查詢的表名數(shù)組為依賴項,進(jìn)行緩存。執(zhí)行SQL時同樣需要指定影響的表名數(shù)組,從而清空所有影響到的緩存。
緩存生命周期分為請求級、定期和永久三種。如果只有當(dāng)前應(yīng)用系統(tǒng)使用該數(shù)據(jù)庫,并且服務(wù)器內(nèi)存足夠大,可以開啟永久緩存,在數(shù)據(jù)沒有更新時,基本緩存在內(nèi)存中,適用于網(wǎng)站;一般設(shè)定一個緩存過期期限,定期清理緩存,適用于內(nèi)存不是很足,或者允許數(shù)據(jù)更新有一定延后的分布式系統(tǒng);如果上面兩種均不合適,而又需要提高系統(tǒng)響應(yīng)速度時,可以采用請求級緩存,在該次頁面請求生命周期內(nèi)對數(shù)據(jù)進(jìn)行緩存,特別適用于在不同地方進(jìn)行相同查詢的場合(有時候是程序員功力不夠?qū)戝e的)。
XCode的開發(fā)模式建議使用盡可能簡單的單表查詢,實際大部分查詢都是簡單SQL語句,緩存命中率很高!
6、漂亮的實體緩存
實體緩存又成為二級緩存。盡管有了一級緩存,但它只是緩存了數(shù)據(jù)集而已,使用的時候還是要加載數(shù)據(jù)集成為實體集合。
實體緩存通過指定一個查詢實體集合的方法,一般是查詢本表所有實體的FindAll()方法,把查詢返回的實體集合緩存起來(默認(rèn)緩存一分鐘),供上層代碼使用。取數(shù)據(jù)的過程完全是隱式進(jìn)行,實體緩存通過提供一個靜態(tài)實體集合供上層代碼查詢,實體集合屬性內(nèi)部進(jìn)行查詢數(shù)據(jù)和緩存過期檢查等操作。查詢方法通過委托傳遞,還有參數(shù)可以指定是否異步獲取緩存數(shù)據(jù)。
總之,使用實體緩存就是使用一個靜態(tài)的實體集合屬性(大多數(shù)時候使用默認(rèn)配置,所以不需要配置),進(jìn)行查詢排序等操作,無需關(guān)心緩存的具體實現(xiàn)。當(dāng)然,對實體進(jìn)行修改操作時將會清空緩存,保證數(shù)據(jù)的新鮮性。
單表數(shù)據(jù)量不大(建議1000以下,不超過10000),并且極少改動的數(shù)據(jù)表使用實體緩存。比如權(quán)限、角色、菜單、系統(tǒng)參數(shù)等使用非常頻繁的數(shù)據(jù)。實體緩存的命中率可以高達(dá)99.98%
7、飄逸的單對象緩存
單對象緩存又層三級緩存,因為它一般構(gòu)建于二級緩存之上。對于數(shù)據(jù)量大(大概幾萬到幾十萬),并且查詢又非常頻繁的數(shù)據(jù)表,任意兩行數(shù)據(jù)之間關(guān)系不大時,可以酌情使用單對象緩存。比如會員表,一般會根據(jù)賬號進(jìn)行查找,并且很頻繁,此時可以以賬號為鍵,會員對象為值,對數(shù)據(jù)進(jìn)行緩存。設(shè)置與實體緩存類似。取數(shù)據(jù)時先去緩存中找,有則直接返回,沒有則調(diào)用預(yù)設(shè)的方法進(jìn)行查詢,并且緩存起來。
單對象緩存里面的實體對象,修改數(shù)據(jù)時,如非必要,不要手工調(diào)用更新方法,單對象緩存有自動保存的功能。該特性適用于更新非常頻繁的場合,比如在線用戶表,可以讓多次更新積累在一起,然后最后自動更新一下。
8、出色的性能
XCode不支持多表查詢,一般的多表關(guān)聯(lián)查詢都可以拆分成為1+X的多次單表查詢。比如學(xué)生和班級的關(guān)聯(lián)查詢,可以先查10個學(xué)生信息,再分別查他們的班級信息,就成了1+10=11次單表查詢。每一次單表查詢肯定會比多表關(guān)聯(lián)查詢要快,但是11次單表查詢很多時候都會比一次多表關(guān)聯(lián)查詢慢。
回過頭來看看上面的緩存,如果這10個學(xué)生是同班,那么在一級緩存的作用下,實際查詢數(shù)據(jù)庫將會是1+1=2,后面9次班級查詢被一級緩存攔截了。在高并發(fā)的系統(tǒng)中,后面這個1就趨向于0了,因為緩存是全局共享的。
再來看看實體緩存,一個學(xué)校的班級不會很多,符合條件使用實體緩存。也就是一次性讀取所有班級信息,緩存到實體集合中。即使在最糟糕的情況下,10個學(xué)生都處于不同班級,實體緩存也是百分百命中,實際查詢僅僅是對學(xué)生表的單表查詢,此時肯定比多表關(guān)聯(lián)查詢快。
在學(xué)生資料界面等地方,學(xué)生表查詢是非常頻繁的。顯然,這是一個非常適用單實體緩存的場合。學(xué)生附屬屬性(關(guān)聯(lián)表)等信息,可以通過擴展屬性“掛”在學(xué)生實體對象上,“享受”到緩存的待遇。
數(shù)據(jù)庫層面也有一個緩存,可以算是0級緩存吧。我們所有的查詢都是單表查詢,對數(shù)據(jù)庫而言非常簡單,同時,因為簡單的SQL,數(shù)據(jù)庫緩存命中率極高,并且非常便于建立索引進(jìn)行優(yōu)化。
基于分頁和緩存,XCode提供了一套高性能的解決方案,這種方案遠(yuǎn)勝于傳統(tǒng)的多表關(guān)聯(lián)查詢,并且是系統(tǒng)并發(fā)越高,這種優(yōu)勢越明顯。
9、臟數(shù)據(jù)支持
在更新數(shù)據(jù)的時候,往往業(yè)務(wù)需求是只更新我們修改過的數(shù)據(jù)。比如會員資料修改表單,可以設(shè)置會員信息等資料,但是不能修改最后登錄時間。這個時候,我們就需要知道哪個屬性的數(shù)據(jù)被我們修改過!
XCode的實體類中,每個數(shù)據(jù)屬性的set方法,都會先調(diào)用OnPropertyChange方法,其實就是為了設(shè)置該字段的臟屬性,說明這個字段的數(shù)據(jù)曾經(jīng)被修改過。生成Update語句的時候,只修改帶有臟屬性的字段。
實體類中,除了直接修改屬性外,還可以通過索引器進(jìn)行修改,兩種的區(qū)別就在于通過索引器修改屬性時,不影響臟數(shù)據(jù)設(shè)置。實際上,加載數(shù)據(jù)行到實體類中,使用的正是索引器,所以剛加載完成數(shù)據(jù)的情況下臟數(shù)據(jù)是空的。
10、多數(shù)據(jù)庫支持
- (MSSQL2000、MSSQL2005/2008、Oracle、MySQL、Access、SQLite)
與大多數(shù)ORM一樣,XCode通過接口的方式支持多種數(shù)據(jù)庫。在XCode中,為每一個數(shù)據(jù)庫實現(xiàn)了一個數(shù)據(jù)庫操作類,繼承自數(shù)據(jù)庫接口。數(shù)據(jù)訪問層DAL根據(jù)數(shù)據(jù)庫連接的配置識別是哪一種數(shù)據(jù)庫,然后創(chuàng)建該數(shù)據(jù)庫操作類的實例,并通過操作接口來操作數(shù)據(jù)庫。
數(shù)據(jù)庫操作類以Access數(shù)據(jù)庫為藍(lán)本,設(shè)計了一個基類,其它數(shù)據(jù)庫操作類僅需要繼承該類,重載功能點不一致的屬性和方法,大大減小了操作類的大小。
數(shù)據(jù)庫操作接口包含的功能有:查詢、執(zhí)行、分頁、事務(wù)、獲取架構(gòu)、DDL操作、數(shù)據(jù)庫版本等。實際上,各個數(shù)據(jù)庫的差異點都可以設(shè)計在操作接口中,而上層代碼根本不需要改動。
很多ORM都為各個數(shù)據(jù)庫的差異大而苦惱,XCode開發(fā)模式則不然。我們的原則是一切從簡,只使用SQL,不適用DbCommand和存儲過程。而所使用的SQL,基本上也是標(biāo)準(zhǔn)SQL,不會使用數(shù)據(jù)庫特性,并且都是單表操作。當(dāng)然,這種方法也不是萬能的,不得已的時候,可以在業(yè)務(wù)層判斷當(dāng)前數(shù)據(jù)庫類型,根據(jù)不同數(shù)據(jù)庫編寫不同的SQL,但自XCode使用以來,還沒需要這樣做過。
11、獲取數(shù)據(jù)庫架構(gòu)
- (DAL.Tables)
在XCode中,數(shù)據(jù)庫架構(gòu)主要包含XTable和XField類,顧名思義,它們代表著表和字段信息。數(shù)據(jù)訪問層DAL中有個成員屬性Tables可以取得該數(shù)據(jù)庫連接的所有表信息,也就是一個XTable集合。同理,每一個XTable中,都會有一個XField集合。
這樣設(shè)計,簡單明了,使用者可以很容易的找到自己需要的東西。我們的代碼生成器XCoder就是依賴于XCode來獲取數(shù)據(jù)庫架構(gòu)的。有了這個功能,人人都可以寫自己的代碼生成器了!
12、反向生成數(shù)據(jù)庫架構(gòu)
- (DatabaseSchema)
這是一個很另類的功能,極少有ORM提供。在開發(fā)和維護(hù)的過程中,難免需要修改表結(jié)構(gòu),重新生成實體類(僅生成實體類數(shù)據(jù)文件部分)。在團(tuán)隊開發(fā)的時候,如果不是共用數(shù)據(jù)庫,則還需要通知隊員做相應(yīng)的修改。維護(hù)的時候,還需要到生產(chǎn)環(huán)境做更新,如果客戶不允許直接操作數(shù)據(jù)庫,那就更麻煩了。
在數(shù)據(jù)庫操作接口中,其中一個功能就是DDL操作,各個數(shù)據(jù)庫進(jìn)行重載后,可以使用DDL語句操作數(shù)據(jù)庫的結(jié)構(gòu)。常用的功能有:創(chuàng)建表、修改字段屬性、添加字段、刪除字段等。
XCode除了會從數(shù)據(jù)庫生成數(shù)據(jù)庫架構(gòu)外,還會從實體類生成一套數(shù)據(jù)庫架構(gòu),然后進(jìn)行對比,發(fā)現(xiàn)存在差異后,直接修改或者寫日志提醒(由設(shè)置決定)。修改開關(guān)沒打開時,僅僅寫日志提醒,日志中寫出了完整的用于修正數(shù)據(jù)庫架構(gòu)的DDL語句,拿到數(shù)據(jù)庫中執(zhí)行即可。
基于XCode開發(fā)的系統(tǒng),在發(fā)布的時候,從來不帶數(shù)據(jù)庫,因為XCode會自動根據(jù)連接字符串創(chuàng)建數(shù)據(jù)庫、數(shù)據(jù)表和字段。即使開發(fā)使用的是A數(shù)據(jù)庫,發(fā)布的時候修改數(shù)據(jù)庫連接字符串為B數(shù)據(jù)庫,XCode將會按照B數(shù)據(jù)庫的規(guī)范來創(chuàng)建數(shù)據(jù)庫。如果系統(tǒng)發(fā)布還需要附帶數(shù)據(jù),那就不可能做到發(fā)布時更換數(shù)據(jù)庫了,除非發(fā)布多個數(shù)據(jù)庫的版本。
也許有人會說,不帶數(shù)據(jù)庫的話,初始化的數(shù)據(jù)怎么辦?在XCode的開發(fā)理念中,建議在實體類增加靜態(tài)構(gòu)造函數(shù),用于檢測數(shù)據(jù)表數(shù)據(jù),如果沒有數(shù)據(jù)時,是否需要創(chuàng)建一個默認(rèn)數(shù)據(jù),比如在管理員表創(chuàng)建一個用戶名和密碼都是admin的管理員。
13、動態(tài)修改連接和表名
- (Meta.ConnName,Meta.TableName)
數(shù)據(jù)量增大到一定程度時,很多企業(yè)的方案都是給數(shù)據(jù)表改名或者把該表遷移到別的數(shù)據(jù)庫中去,僅用于查詢統(tǒng)計。
在XCode中,生成實體類時,就指定了實體類所對應(yīng)的表名,但是我們并不需要為多個具有相同表結(jié)構(gòu)的表生成多個實體類,因為實體類可以動態(tài)修改所指向的表名,使得操作的目標(biāo)表發(fā)生改變。為了避免多線程環(huán)境所帶來的影響,該修改僅影響當(dāng)前線程。
連接名的修改方式與表名相同。
14、弱類型訪問
(IEntity,IEntityOperate)
有時候,我們并不知道需要操作的是哪一個實體類,只有在運行時才能確定下來。常用的做法就是反射!
為了避免不必要的性能損耗,以及避免很不美觀的編碼設(shè)計方式,XCode提供了弱類型訪問的能力??梢酝ㄟ^指定類型或者表名,反射找到實體類,創(chuàng)建一個IEntityOperate操作對象,從而完成對實體類的各種操作。
IEntityOperate所提供的方法跟實體類的靜態(tài)方法基本一致,在使用上不會遇到任何困難。IEntityOperate進(jìn)行數(shù)據(jù)查詢時,返回的是IEntity集合,因為每一個實體類,都實現(xiàn)了IEntity接口,足以完成基本的CURD操作。
15、動態(tài)生成代碼
(CodeDOM,內(nèi)存實體)
在弱類型訪問的支持下,有些簡單的數(shù)據(jù)庫操作并不一定需要生成實體類,XCode在找不到實體類時,將會根據(jù)表架構(gòu)在內(nèi)存中生成一個實體類,然后編譯使用。
動態(tài)生成的另一目標(biāo)是讓使用者通過調(diào)用一些方法來生成實體類代碼,而不是一定要通過XCoder來生成。
16、擴展加載
(把查詢中的字段映射到擴展屬性)
XCode支持的是充血模型,從面向?qū)ο蟮慕嵌壬蟻碇v,這個對象的所有特點(屬性)和能力(方法)都應(yīng)該在實體類上實現(xiàn)。除了基本的與數(shù)據(jù)庫字段對應(yīng)的數(shù)據(jù)屬性外,還有一些跟別的實體對象關(guān)聯(lián)的屬性,我們稱之為擴展屬性。比如:Article.Board.Manager.UserName,可以直接得到一個帖子的版主的用戶名。
XCode不支持多表關(guān)聯(lián)查詢,但是它有擴展屬性,所有的多表關(guān)聯(lián)查詢,都可以通過擴展屬性來編碼實現(xiàn)。Article中的Board擴展屬性,是在使用的時候才去加載的,加上Board可以使用實體緩存,基本上沒有數(shù)據(jù)庫操作。Manager不能使用實體緩存,但是它作為擴展屬性“掛”在Board上,間接“享受”了緩存。
還可以編寫一個普通的屬性作為擴展屬性,然后執(zhí)行查詢的時候,通過selects參數(shù)把數(shù)據(jù)映射到該擴展屬性上。比如:增加一個Total的整型屬性,然后執(zhí)行查詢Article.FindAll("BoardID=123",null,"Count(*) as Total",0,1),該查詢是取得欄目編號為123的所有帖子數(shù),然后把結(jié)果映射到Total屬性上,返回的記錄集只有一個實體對象,該實體對象的Total屬性就是所要查詢的帖子數(shù),此時別的屬性沒有意義。
該功能一般用于查詢統(tǒng)計中。使用一個實體來表現(xiàn)數(shù)據(jù),比數(shù)據(jù)集方便多了。
擴展屬性是充血模型所特有的東西,也是相對于貧血模型(含失血模型)的最大優(yōu)勢所在!
17、泛型基類模型
- (Entity<TEntity>)
XCode從v1.2起,就進(jìn)入了第二代,關(guān)鍵點就在于泛型基類Entity<TEntity>的使用。
在第一代XCode中,因為充血模型,實體類上要附帶大量的方法,而當(dāng)它們的返回類型是實體類或者實體類集合時,這些方法就必須實現(xiàn)于實體類的代碼中,實際上是通過代碼生成器來生成。
在第二代XCode,引入了泛型基類技術(shù),實體類通過泛型參數(shù)TEntity指定最終返回類型,編寫查詢方法的時候,返回類型使用泛型參數(shù)TEntity即可。所以,第二代實體類只有屬性和索引器,基本不需要生成查詢和操作的方法,因為它們都在泛型基類里面實現(xiàn)了。
大多數(shù)情況下,實體類指定的基類泛型參數(shù)就是它自己,因為它需要以它自己作為返回類型。但XCode開發(fā)模式是面向?qū)ο蟮?,包括實體類,也希望能夠繼承,增加一些功能,該功能可以通過改變泛型參數(shù)來實現(xiàn)。
18、實體類的繼承與重載
- (NewLife.CommonEntity)
通過改變泛型參數(shù)的具體類型,實現(xiàn)實體類的繼承和重載,是XCode進(jìn)入第三代的標(biāo)志。它標(biāo)志著我們可以封裝一些基礎(chǔ)數(shù)據(jù)實體來供多個項目使用。
通用實體組件NewLife.CommonEntity就是該功能的代表,封裝了地區(qū)、管理員、角色、菜單、授權(quán)等實體類。具體項目可以直接使用它們,也可以通過繼承與重載來實現(xiàn)擴展。
以封裝的地區(qū)表為例,它在靜態(tài)構(gòu)造函數(shù)中檢測數(shù)據(jù)表行數(shù),當(dāng)然,在這之前XCode會自動檢測并創(chuàng)建地區(qū)表。如果地區(qū)表中沒有數(shù)據(jù),則會調(diào)用一個方法進(jìn)行數(shù)據(jù)初始化操作。地區(qū)表業(yè)務(wù)類代碼通過硬編碼方式,內(nèi)置了全國幾千個地區(qū)的區(qū)域編碼和名稱。
一旦國家修改區(qū)域劃分,只需要修改該類,所有使用該組件的項目都將使用上全新的區(qū)域數(shù)據(jù)。如果這些功能代碼都復(fù)制到每一個使用的項目中去,將形成一個非常難以維護(hù)的狀況。
小結(jié):詳談XCode學(xué)習(xí)基礎(chǔ)之ORM組件的內(nèi)容介紹完了,希望本文對你有所幫助。更多相關(guān)xcode的內(nèi)容,請參考編輯推薦。