Saturday, September 24, 2005

使用DBCopier進行不同數據庫間的數據移植

DBCopier同樣是在多數據庫環境中,?了適應數據整理和拷貝而開發的可重用構件。不過,實際上,更多的情況是用在不同的開發過程中由于表結構的修改,而需要對曆史數據進行整理歸一的過程。

DBCopier的工作核心是mission.xml,每一個mission是一個拷貝任務;不同的數據庫連接在connectionset元素中定義,實際上,這與ConnextionManger和DAO中的定義和解釋是一樣的。
如:




uri="jdbc:oracle:thin:@127.0.0.1:1521:dkt0" username="conn" password="
abcd" />
uri="jdbc:oracle:thin:@127.0.0.1:1521:dkt0" username="reso" password="abcd" />
uri="jdbc:oracle:thin:@127.0.0.1:1521:dkt0" username="daifu" password="abcdef" />
uri="jdbc:oracle:thin:@127.0.0.1:1521:dkt0" username="system" password="abcdefg" />


定義了若幹個數據庫連接定義。根據需要,DBCopier可以把不同的數據庫中的表拷貝到另一個數庫的某個或幾個表上,也可以在同一個數據庫中拷貝。

每一個mission是一個任務:


= ${MIN} and id < ${MAX}]]>


insert into sectart (name,chn,deleted,on_off_line,section,type,remark) values (${name},${chn},${deleted},${on_off_line},${section},${type},${remark})






詳細定義是:
mission代表一?拷?任?;
mission.(target/source) 分?表明拷?的目?和?源,?构是(?据源.表名),意?是直接??表操作,?管主要由Query????,或者是Clazz,但是在?算步?和?量?仍需要?里的表名。反而是target可以直接?据源名?。
mission.index 指示是以什么域作?拷???的指?,如id;
mission.range;采用1-2000??的形式,前者是起始,后是??(?照index);如果是“*”,表示全部拷?(默?)。
mission.step:步?,即每次?作?程拷?多少,??相?于?置??大小;每次?行step的?目,或????(小于step?)
mission.condition是拷??象的附加?件;可以直接?作cdata形式;
mission.query元素: 每?mission目前可以定?多?Query?象,但至少包括分?命名?write的Query?象,如果?有mission.clazz作??入源, ?必?有一?名?read的Query?象;特?注意read?象,如果其中有"WHERE"一定要大?,特?是在condition有?西的?侯;$ {MIN}/${MAX}是程序?定的大值和小值??,与range配合,即使是“*”,???值也是必?的;因?步???的?侯需要???值
注意:query.sql中如果有域是long/clob型的,一定要?在最前面,否?可能出?;

mission.clazz元素:{
?于一些复??构的?据?象?型,很?确定?一?表?表的抄?,同?也?包含有多?的互相?束,??侯需要使用?据?象作??据源;即每一??象?例是相 ?于一行主表的??,它的?性是各?基表或子表值;clazz?象tools.xmlobj.Clazz必???tools.COPIER接口,并用?准 的setter/getter取/置值;
clazz.attribute?象;主要包括三??性,name,value,和type,type用于反射?的class型?得,因此要求完全的名?,以便在程序中Class.forName();
}

mission.field;{
DBCopier??上是使用Query.write中的?量?得一?串,然后通???串?索qread或者是cread形成一?名值?填充好一?完整的 sql,最后?行??batchsql完成移植的目的。每??量串中的域?得值的方式各不相同,特?是在?多情?下?移的?程本身就是?原?不合理的?据 ??方式的重整,(?范化或非?范化),filed就是????需求?行?置。如果?有相?的field?置,?采用默??置,即名?相同且都是字符串 值;field的??是首先采?value值,然后按query取值,最后按常?取值;

field.name:?名??与qwrite中的?量名?之一匹配;否?DBCopier?加以忽略;
field.sname:?名?指老表中的域名?,DBCopier通????cread或qread的rs中??据;默?地与field.name相同;
field.stype:?入的?型,???于使用qread特?重要,尤其?于date?型;
field.ttype:?出的?型,是指插入的?型,主要是因?生成sql?字符串需要使用‘’括起?;
filed.value: value是?定的值;可用此直接?一行?值;如果是“system.time”,意思是取System.currentTimeMillis()的值;
field.query:{
如果?域必???特?的查?得到值,就?定所用的query名?,??query??已?定?在同一?mission中。
field.query??上??了???程,一是以前述的方法?得一?指定的?量;代入query的sql?定,?行??sql再?取它的值;
??field的域含?有重大的修改:
field.sname指代入field.query.sql中的?量名?,一定要与相?的?据?接中的表.域名?相同;
field.stype指???query?果?的?型;其余意思一?。
field.filter; 如果??域在存入新?中?需要??更替其中的一些字符串,?置好filter后用?????用。
}
注意:query的connetion查?到底是那一?的,常常是??的?系;
}

mission.filter,用于????程中的字符串列?行必要的字符替?和??;由field?用;

在定義好後,執行:
java DBCopier mission=xxxx;;
程序就會按照步長一段一段地完成數據移植任務,不滿意就重新來一遍。之所以要分割步長,是為了避免多個連接游標在大數據表時溢出出錯。

Wednesday, September 21, 2005

簡化多表連接的訪問方式,hanva中的database.service基表映射框架

在Hanva的框架中,多表連接的查詢,一般由以下的方式完成:
1、對於條件限制上的連接,可以直接採用entity讀取,條件上的多表,僅限於條件上的擴充,與一般的單表查詢沒有什麼不同;可以設定list,也可以在entities標簽中直接設定連接條件;
2、對於選定域也是多表的,如果沒有相應的類可選,原則上是依靠database.service把基表的連預先設出來,從而把多表的連接轉化為單表上的連接,簡化了連接操作;
3、其他情況,不反對越過應用層直接使用SQL查詢(還沒有碰到過);

database.service基表轉化系統,在適應原來多表的環境中,是非常關鍵的一個部件,經過近一年的運轉,表明當初的設想是正確的,基本符合應用要求。database.service最早的設想是基於以下幾點:
1、多表連接中最常見的情形是少記錄量的基表和多記錄量的主表的連接;
2、基表的記錄量都比較地少;
3、基表在一個數據庫方案中總是佔了總數的一半以上,對於基表的管理的開發,總是佔用了系統總開發時數的十分一以上,而重復性總是很高,而界面的使用率也是最低的。
4、把數據庫的關系分割成單一的ER後,總是能夠體現出最重要的是主entity的管理,其他部分是極少動作的,包括基表和二級三級子表。

database.service是針對上面幾點的解決方案,主要包括:
1、使用XML放置簡短的基表記錄而不是使用數據庫方案,這樣,就省去了其餘基表的管理部分,使用寫字板就可以完成通常是一次性的管理;
2、基表記錄的訪問量很大,而多表連接意味著多表遍歷,消耗的資源遠遠超過普通的單表連接,因此,把基表記錄放進內存而把多表連接簡化為單表,不但可以簡化應用服務器的工作,也能夠大幅度提高運行效率;
3、對於不便於使用XML文本記錄的類型,支持使用數據庫記錄,然後在有訪問時就把這個基列讀出來,象一般的基表類型一樣工作;如果超過一段時間沒有訪問,就把這個基列清除出內存,騰出空間給即時的其他服務;
4、基表,特別是特殊的基表工作,總是意味著獨立的可抽象的方法,分散到各個模板訪問中實現,會加大各個模板的工作量,把它抽象出來集中到基類服務模塊,可以省卻這部分的工作(可重復調用)。
5、基表系統不考慮子表集合,那是XML/OR構想中解決的課題。

database.service包括以下類組件:
一、MemoryBase:
顧名思義,就是單純存放在內存中的Base類,實際上在啟動、訪問前是存放在xml文件中,由XML結構保存的等同於原數據庫小基表的內容。在XML使用 方式還不熟練的時侯,是所有的基類放在一個大文件中,以減少初始化解釋的成本;目前,已經發現把不同的基表分散到小的文件中,在訪問時再解釋,可以減少對 內存資源的消耗,同時,解釋小文件的單次訪問的成本也在可接受範圍內,這樣的結構比老一版要好。

二、SimpleBase;
使用單一數據庫表存儲的的基表,這是針對某些基類記錄還是比較多的,超出十個以上如果手工在XML中管理會比較麻煩,而套用XContainer的自管理索引的升級也沒有做,這時侯,使用simplebase可以把主要的基列放在數據庫。

三、multibase;;
這是針對多對多的表關系結構及其主要訪問管理方法抽象出來的表類型;

四、treebase/multitree
treebase作為一個依靠parentid的反向遍歷,經反復對模式在實際項目中驗證,表明它的運行和應用效率都比較低,樹結構最合適的做法不是使用 parentid,而是使用listable的鏈表集合,以避開每次訪問的反向parentid的上溯遍歷算法。這條,已經由XML、XOR方式得到解 決,因此,在database.service中這兩項實際上已經被廢棄。(但在開發時它花的時間是最多的)。

結語:
無論是hanva和它的基表服務,以及hanva的前身使用視圖和子查詢來簡化J2EE中的多表查詢的復雜性和困難,其動機都是通過在數據庫或應用層上的 附加工夫,把應用層中的繁複而困難的對關系結構的映射簡化成單表結構。它盡管對數據庫的ER設計,盡可能地減少多表連接有範式上的指示作用,但並不是對關 系數據庫中的外鍵關系的否定,而只是對這種外鍵訪問的傳統的多表SQL鏈接的否定。對於兩層的應用,它沒有任何意義,對於三層的面向對象的應用系統,它很 可能是關系結構向關系對象結構轉化過程中的嘗試,盡管還沒有任何跡象表明它會是一個成功地廣泛得到應用的嘗試。

附:
database是??使用一?Database.getInstance().getBase()的方法,提供?一的??base的方法;
database最基本的??是dao.Record,一?MemoryBase和一?SimpleBase??都是一?Record; MemoryBase?承Record的所有方法;同?SimpleBase?承了MemoryBase,其他的Base都?承自SimpleBase。 dao.Record作?所有base的父?,本身?承自vEntity->dao;并使用其中的方法。
??是memorybase,simplebase,multibase都是返回records?果集中的??;但是treebase?果集中按 (flat的真假),如果flat?真,与前面三种base相同,否?只保留isleaf的部分,意即除非是?的基?,否?不允?引用;而 TreeNode与Record是不同的;TreeNode使用另外getNode()方法。?于MemoryBase,SimpleBase在始始?入 ?存后就不需要pro和conllection

?于multibase:
multibase??多?多基型??,目的仍是使用同一?基表,而避免?生多?表型?接。multibase基于一?simplebase???型,但 是??上的?例?象是一?dao.Multibase?象;在多?多?系表上可以使用simple_ref,也可以自行建立?系型表,如 member_role_ref;查??的方法先定?在Query中,然后得到逮?于某?基型的id是那几?,最后使用in()?句得到列表。?然,如果 ?目太多,sql?因?太?而不能?行。

Sunday, September 18, 2005

應用服務器看來應該避免多表查詢

一直沒有仔細考慮在自已的hanva框架中多表查詢的情況。今天算是真實碰到了,並不算太緊,這時侯正好可以較詳細地思考這個比較理論化的問題。事實上, 當表達層變得需要通過應用層去訪問數據庫時,如同J2EE試圖做到的一樣,單個的實體bean不會有太大的問題;而如果是條件性列表,就已經比較麻煩了, 如果是多表的話,原則上無法繼續。

原因就在於這種三層結構需要使用相應的預編寫的java類去承接查詢回來的數據。對於實體bean來說,對象就是數據記錄的虛擬反應,倒也沒有什麼區別; 但如果是多表連接的查詢,同時不應該每寫一個查詢需要特別生成一個java類(否則效率何在,還談何方便?),在邏輯上,基本上是說不通的;因為,那個反 應多表限制的類,它在實際意義上代表的是什麼對象呢?

目前對這種情況我是通過database外置基類加以解決,對於大多數情況下,基表只是一個小小的表,使用XML-內存映射的方式不但可以獲得更高的效 率,而且,還可以把絕大部分列表查詢簡化成高效率的單表查詢,(每一個表連接意味著多一次遍歷),並進一步地可以適應多數據庫的環境,實際運行效果非常 好。但這種代入內存的方式如果基記錄的數量很大,就不劃算了,效率也會直線下降,最終不如使用數據庫的傳統的多表查詢更為合理。

另一種辦法就是使用非規範性的操作,預先把本來需要實時多表連接產生的結果以非規範化的方式寫在相應的表記錄中,這樣,同樣可以把多表查詢簡化為單表查詢。事實上,這也是在系統優化過程中大量使用的提高運行效率的常用的方法。

使用上面這兩種方法,基本上可以避免產生真正的多表查詢,事實上,幾乎過了一年,才碰到可能需要多表連接的情況,就是一種證明;而且即使是目前的需要,也 是可以通過上面的第二種方法解決的。如果仍然想偷懶,那就直接採用傳統的操作sql/resultset的辦法,從而避開中間的應用服驄器的限制是也,也 仍然沒有必要把精力放在讓應用服務器適用多表查詢上。

無論是j2ee,hibernate,libernate難以真正成為開發的標准(得到40%以上的應用),犯的一個共同的錯誤,就是把超過80%的開發精力投入到實際上並不是必須多表連接上,實際上卻是降低了整個方案的可用性和適用性。

Monday, September 12, 2005

壓縮生成jsp的數量,減少系統的編譯負載

近兩天為了搜索引擎橫沖直撞的故事,開始修正多jsp的框架。所謂多,是相對于過去重複性極高的servlet的方案而言的,實際上我的jsp重用程度高 到不能再高。如果說多,比起我見過的一般的jsp網站,jsp的數量一般只是他人的十分一以下。代碼量更是少得可憐,原因在于幾乎所有邏輯都已經封裝了。

即使是這樣仍是有著再次壓縮的空間,而且,盡管重新達到整網一頁沒有必要,(那是使用servlet達到的,運行效果甚佳),但依靠URL重寫的協助,整 網按功能在十來頁以來完成一個極複雜的多用戶多單位商業發布系統還是可以做到的。但問題來出現了,俺當初?什?要做到多目錄多文件自動複雜的JSP框架 呢?

第一個原因很直接,那是因?沒有使用URL重寫這件武器;當時並沒有意識到這件武器結合一般的JSP可以?生如此威力;這個恐怕是最根本的原因;

第二個原因是對于多用戶多單位達到可信賴的權限控制沒有把握,希望通過不同的目錄設置對應的環境常量設定達成絕對可靠的訪問控制——這條今天隨著使用體驗的加深,證明可以通過單目錄判斷也可以達到。

第三個原因仍然存在,就是由于要保留多用戶多單位系統下的絕對自定義空間,需要提供獨立的文件目錄,在沒有URL重寫的協助,實際上不使用多目錄多文件是做不到的。

唔,大概這是當初設定方案的主要考慮因素。從實際效果上看,在用戶不多,訪問量也不算太大的情況下,的確沒有什?問題;在用戶多,訪問量也大但是運行時間 已經不短的情況下,也沒有什?大問題;最大的問題大于剛剛完成升級到主服務器上或修改了關鍵文件時,大的訪問量造成多個文件同時編譯,而這些JSP文件一 般都會不同程度調用同一個組件,由此?生的編譯死鎖造成一至一次的發布失敗,需要重?甚至幾次才能度過這個發布後的震蕩期。

這樣,就?生了必須加以優化,減輕發布後,以及維護性修改後,系統需要承擔編譯的消耗。如上所言,現在可以壓縮成若幹個高度重用的文件,這樣,無論負載量 有多大,?生編譯死鎖的機會成級數降低了。另外,原來不太注意引用的類別,現在也要注意一點了,根本實際的目的,嚴格使用最低消耗的方案:
1、如果沒有變量共享,就不用file include;
2、靜態內容文件使用c:import;這樣允許頁網人員任意填寫不會造成重新編譯的要求;
3、動態內容但不具備變量共享的,使用c:import動態網頁或jsp:includepage(好象沒有什?區別),同樣可以縮小編譯請求的影響面;

當然,最終的解決方案是完全實現靜態網頁發布。在完成靜態發布的升級以前,上述優化對于提供目前系統的承載量,估計還是有很大作用的。

Thursday, September 01, 2005

在JSP中?构件的抽象和代?重用??以什么?度

這幾天在處理動態網站和博客的應用時,發現效率很低,其中的一個原因就是由于不得不在jsp的代碼層來來去去地進行很接近卻略有不同的編碼。在一般的習 慣,我會傾向于使用其同包含的代碼片結合變量實現重用,只要重複達到三次以上就值得這樣做。這次,有幾個原因令我手工重複處理而不是使用可用用的代碼片。

第 一個原因是這裡重用的一般都是兩三次之間,而沒有出現三次;其次是近來的經驗發現大部分准備重用的代碼片也極少調用超過五次的,從而令我懷疑假想是不是 有誤;第三,當網站訪問量達到相當高的程度時,共用的代碼片很容易因為運行時編譯死鎖造成死機,令我進行重用性處理時要謹慎一點。事實上, tagfiles我已經完全放棄,就是因為這個原因。大量使用tag可以令javabean得到很好的調用和運行量賦值,但如果使用頻率不高而代碼比較簡 單,還不如直接在jsp中調用javalet實現,而不必追求代碼中的乾淨不含javalet。在這個手工處理可重復性代碼中,的確發現放棄重用而進行重 復工作是令人沮喪的,也是低效的。

jsp的代碼可重用比較低的原因在於易於抽象的部分已經基本上包含在 javatag/javabean/EJB中,而不同的頁面最終總是在需求上遷移或多 或少出現不同,最後,就會出現邏輯上完全相異,以致於本來是為了重用節減開發時耗的代碼片重用,最後卻成了重復的編碼工作。

既然加強重用 也可能帶來低效,完全在jsp代碼層單獨重復實現更是低效,那麼,重用的抽象應該以什麼度最合適呢?暫時總結出一個經驗,其中約有20%左右 仍帶有假設:javatag,javabean應該盡可能提供重用度,但以不帶有任何表達層代碼如 html為度;而在jsp層原則上應該以獨立編碼後,最後才考慮是否部分代碼在後面的編碼工作中可能會重用超過三次,以次作為重用化的標準。換言之, bean層是加強抽象,jsp層是放棄抽象,可能,會帶來更高的效率。

而且,也可以很清晰地感覺到,這個原則由於團隊的組成成分不同也有所差別:如果是開發人員少而精,則應該采用更高重用性的開發方式;反之,如果低素質的開發員有一定比例,放棄高代碼的重用要求而接受更多的獨立代碼,開發效率反而會變得高一點。