The important thing in life is to have a great aim , and the determination

          常用鏈接

          統計

          IT技術鏈接

          保險相關

          友情鏈接

          基金知識

          生活相關

          最新評論

          #

          對象數據庫與關系數據庫利弊談

          在20世紀60年代后期引入的面向對象技術引起了一場革命。到20世紀80年代后,面向對象的技術已經成為了行業的主流,其原因多種多樣:面向對象不僅簡化了界面的開發,而且也提供了一種更加靈活、簡單數據處理方法,這種方法從根本上改變了應用程序的構建方法。不再像關系型數據庫一樣用死板的二維表格來表示數據,對象技術使用類對數據進行描述。一個對象是一個類的實例,就像一顆特定的橡樹是橡樹類的實例一樣。

          對象技術使用繼承方案,使得類是按等級設計的。“橡樹”類能夠從更加普遍的類“樹”繼承數據結構和數據行為。

          對象技術能夠更好地描述我們所見的世界,面向對象的語言已經被證實在大多數編程領域更加通用。他們使得編程語言更加接近自然語言和多數軟件開發領域的主流思想。面向對象是一個新的典范,它的影響將持久而深遠。

          面向對象的特性很快被添加到各種成熟的語言中,并因此成就了一些語言,如C++。新的面向對象的開發環境出現了,包括Visual Basic,Visual C++,PowerBuilder,Delphi,以及Caché。盡管面向對象的技術在高級開發環境下受到了廣泛支持,它還是需要花一定的時間形成正規的課程。而且還需要花更長的時間來構建一個真正的基于對象的世界——我們目前還沒有到達這樣一個階段。

          萬維網上對象技術的發展

          隨著萬維網(World Wide Web)轉變為交換各種信息的手段,面向對象的編程語言Java成為Web開發者的最愛。基于C++,Java能夠用來創建可以在瀏覽器執行的小程序(Java applets)。

          Sun為了促進Java的發展免費提供Java環境。在短短幾年內,成百上萬的Java環境被復制下載,Java滲透到世界的每一個角落。同時Java引發了更多的面向對象語言,如JavaScript,C#以及Jscript。Internet的發展也培育了一些新的面向對象語言像Perl和PHP。現在的開發者使用面向對象的技術已經是理所當然的了。

          對象的崛起

          對象技術影響了軟件開發的各個方面。對象建模已經占領了應用建模的市場,標準UML建模方法獨占鰲頭。

          20世紀90年代,面向對象中間件產品的出現為面向對象的應用提供了安全交流服務。當1998年JMS(Java Messaging Serivce)的出現,使得中間件市場向前跨越了一大步。JMS定義了一整套消息傳遞的應用編程接口(APIs),使得經認證的J2EE應用必須引入JMS服務器。這進一步強化了標準化進程,大大降低了中間件的費用,提供了編寫企業范圍基于對象的應用程序平臺。

          XML和Web服務

          1998年,HTML,專門用于網頁設計的標識語言,經過進一步發展并標準化,創造出了XML(擴展的標識語言)。XML提供了一整套語法,能夠用于創建與存儲在數據庫中定義相似的自定義數據格式,可以。有了XML,程序能夠把定義附加在數據上,能夠交換數據和數據含義。XML能夠使得有特定標準的數據模型(如發票或者購買訂單)的定義能夠在公司內部或者公司之間進行數據交換。XML引發了Web服務的興起——在不需要客戶定制的情況下,程序能夠與其他程序立即交互。現在出現了兩種Web服務環境——J2EE和.NET。像SQL一樣,XML為程序員提供了獲取數據的標準,但XML同時還提供了一種在對象層定義數據的標準語言。XML和對象技術一樣迅速成長。結果,數據對象的新標準和基于XML的新的開發產品出現了。

          對象數據庫——缺失的一環
          與軟件開發各個環節中對象技術的快速應用形成鮮明對比,對象數據庫直到現在才開始逐漸被人們所接受。對象數據的遲緩行動原因有很多。

          早期的對象語言沒有考慮數據存儲。程序在內存數據上工作,數據作為文件存儲,當程序下次運行時數據也作為文件被讀取。這種方法使得應用程序之間不可能共享數據,數據的恢復、管理、擴展幾乎不可能。

          目前在市場上已經有大量的面向對象數據庫產品:Versant,Objectivity,ObjectStore,GemStone等等。他們為面向對象的開發環境提供了相應的數據存儲。這些產品滿足了最初的熱情,甚至這些產品被期望能夠打造一個新的數據庫市場——甚至可能成為市場的領袖。

          但不幸的是,這些對象數據庫出現時,關系型數據庫供應商已經積聚了巨大的動力,并占領了大量市場份額。在標準的SQL接口下,訪問關系型數據庫的面向對象程序很容易寫。相反,多數早期的對象數據完全不提供SQL接口,不適合任何查詢應用程序。結果,對象數據庫在商業上沒有建立堅實的基礎。他們在應用領域只創建了一個小市場來管理和存儲復雜對象如CAD/CAM,電信業、多媒體、人工智能,模擬金融設備、病人診治跟蹤系統以及科學應用。

          數據庫市場從未特別關注過對象數據庫,直到對象定義語言XML出現,這種情況才有所改變,促進了對象數據庫的再次呈現,因為他們管理XML定義的數據是最合適的。使用XML,必然會提高存儲復雜數據的需求,將進一步引發對象數據庫的復蘇。

          03年9月份InfoWorld公布了一項開發員調查,其中有一個驚奇的結果,89.2%的被調查者說他們使用關系型數據庫,52%的被調查者說他們使用面向對象或者XML數據庫。當問及有關存儲數據的類型時,40.2%的人說他們存儲持久的對象,58.9%的人說他們存儲XML數據,89%的人說他們存儲關系型數據。Baroudi Bloor相信對象數據庫比我們想象的用的更加廣泛,隨著需求的激增,將進一步擴大市場份額。

          InfoWorld的調查還顯示了面向對象的語言是新應用開發的主流選擇。我們相信這些統計數字反映了當今開發員面臨的困境。他們需要與他們一直使用的面向對象語言有更好協調性的數據庫,但他們有需要關系型數據庫所提供的查詢能力。

          關系型數據庫——另一半是如何存在的
          只要有程序,就會有數據。IT行業最早具有商業價值之一的就是數據管理。自動的數據管理意味著業務能夠擴展、具有競爭力,沒有它就不可能。所以毫無疑問機智的商業技術員很早把目光聚集在數據管理市場。在對象數據庫產生之前的20年,E.F Codd博士提出的關系型理論找到了出路,開發出商業的關系型數據庫產品。在80年中期,在IT領域有一個宗教式的信仰,認為數據的所有理論問題都已經解決,實踐的問題也會隨之解決。然而,很明顯,事實并不是這樣。

          關系型數據庫把數據存儲在簡單的兩維表中,這是一種表達大量數據的有效方法,而且程序員也易于理解。關系型數據庫使用SQL建立了一種標準的數據訪問語言。關系型數據庫有一個邏輯和物理形式清楚的結構,這種結構使得應用程序對數據結構是透明的,而且在很多商業應用程序中工作的很好。

          然而,關系理論的基礎之一是數據和使用數據的程序能夠而且應該是相互獨立的。這與對象技術的整個理念是不一致的。對象技術鼓勵設計者使用對象而不是表來思考數據。對象和使用對象的方法是不可能彼此分開的。

          如果把汽車作為一個復雜的對象來考慮。當你使用汽車時,你使用一輛完整的汽車,作為一個東西——一個對象來使用。與汽車相聯系的有很多動作(也就是面向對象術語中的方法)。你駕駛汽車,進行換檔,發信號,開車燈,等等。如果汽車是一個對象,這些動作就是對象的方法,他們對汽車而言是基礎性的。這些動作獨立于汽車的想法是荒唐的。當你把你的車停在車庫,你把它作為一個東西來存儲。而不是分別在車庫中的某些地方存放方向盤,轉換器,信號器,車燈。數據和它相對應的處理過程也不能而且也不應該被隔離開來。在對象數據庫中他們是不分開的。

          事實上,這兩種觀點各有優缺點。有些處理過程確實是獨立于數據的。尤其是訪問大量數據的查詢操作。簡單的查詢就是根據一些標準來選取數據,而不關心數據是什么,也不用關心數據是如何被組織的,只要它能快速的被取出就可以了。查詢是獨立于數據的,但對象方法則不是。

          關系型數據庫的局限性

          關系型數據庫有比我們想的更多的局限性。存儲和表示一些相當普通的數據結構也是非常困難的。試想一條公交線路——簡單,有序的一組站點。關系型數據庫以無序的方式存放表,只有創建一個特殊的索引,才能提取有序的數據。對象數據庫就沒有這個問題,它有有序的數組,不需要索引——這種索引是因為關系數據結構的局限性而要求創建的人工索引。

          另一個簡單的例子是產品用料單——在制造系統中記錄一個產品和它的組件。組件自身也許還有組件,組件的組件還有組件,以此類推。一個關系型數據表不能表達這種部件與部件的部件之間的關系。而這些關系卻是重要的數據。查詢一個產品數據庫,它的所有組件應該是一目了然的。關系型數據庫結構使得開發員花費很多的工作來回答這種簡單的查詢,非常的復雜、困難。與這個例子類似的例子:地圖和它的路、河、路標;網站和它的頁面、鏈接以及圖像。實際上,搜集的信息越復雜,等級層次和交叉層次就越多,在簡單二維表的關系型數據庫就越不可能表達清楚。對象數據庫沒有這樣的限制,事實上,他們就是為了解決這個問題而設計的。

          雖然關系型數據庫發展成熟,在這十年中發展也非常迅猛,但我們還聽到一些項目因為所使用的關系數據的性能不是很好而導致失敗。通常,是因為關系型數據庫物理上存儲數據的方法導致的。對開發員而言,為了集合他們所需的數據,他們常常不得不進行這個表與另一個表聯接,再與另外的表聯接,然后再與另一個表聯接。為了提取數據,數據庫運行優化程序來判斷提取數據的最好方法,然后再提取數據。這樣的處理常常要花費很長的時間,結果就大大影響了性能。盡管關系型數據庫優化器已經改善了運行時間,但他們還需要比對象數據庫更多的處理時間。

          關系型數據庫和“阻抗不匹配”障礙

          關系型數據的一個問題是他們所使用的基本數據結構是一種二維形式的表。在關系理論中,數據應該被組織成規范的表——也就是數據應該按唯一的方式組織,使得程序員能夠消除冗余,確保數據變化的一致性。這種設計技術的引入確保了關系表中的數據是一組獨立的、通過鍵相關的數據。這種技術來自集合論的數學理論,但問題是集合論不能表達數據之間所有的關系和結構。

          以規范的方式存儲數據常常要求程序員在存入數據庫之前分解對象,并且重新組織數據,但要使用它是,在使用SQL查詢(多重連接)。就像在車庫中存儲車時,你把它的門、椅子、輪子等等分別卸下來存放。這是非常耗時的,而是也是沒有任何意義的。

          但面向對象的語言占主導地位時,問題就越發明顯了。這個問題通常被稱為對象-關系不匹配障礙。這個問題是由于面向對象語言和關系型數據庫使用語言的方法不同導致的,結果這個問題只能有程序員自己來解決。事實上,大多數關系型數據庫在使用的時候并不是完全規范的,但即使是這樣,不匹配問題還是發生,對編程人員的工作造成了很大的困難。我們可以估計使用關系型數據庫的面向對象開發員25%到40%的時間用于編寫代碼來解決對象與關系表的匹配問題。

          也許這個根本性困難產生了對對象數據的強烈需求,但多數對象數據庫也有一個很大的問題:他們對SQL的支持很少。而許多軟件工具需要SQL接口,尤其是商業智能應用。甚至有SQL接口的對象數據庫也不能創建用于管理商業智能應用所產生的這類查詢機制。

          對象-關系數據庫

          關系型數據庫的供應商并沒有忽視對象的出現。顯然,規范復雜數據是沒有意義的。舉個極端的例子,如果你要規范一個位圖形式的圖像——是一系列的象素表示的——你最終要得出一個表,這個表的行是象素,并且主鍵的屬性反映他們的順序。很明顯最好是把這個數據作為一個對象來存儲。

          他們提出了“對象-關系”數據庫的創意,這個創意中保留了關系型數據庫的結構,但允許關系表中的列含有一個復雜的對象。這些對象能夠捆綁處理復雜數據的處理過程(一種存儲過程)。并且SQL能夠允許調用與關系型等同的“對象方法”。

          這種方法是對數據關系理論的一種嘲弄,事實上,它完全忽略了這個理論,但又允許復雜數據(地圖,矢量圖,圖表,甚至整個表格)被定義為一個項目存放在關系結構中。因此,這些功能被實現并商品化。Informix稱它的嵌入過程為DateBlades,Oracle稱之為Cartridges。

          對象-關系數據庫成為存儲數據時對象數據庫的一種替代方案,但根本的問題它并沒有解決。對象-關系數據庫還是受不匹配障礙的困擾。

          對象數據庫與關系型數據庫
          實踐中,對象數據庫相對于關系數據庫有顯著的優勢。

          他們能更快的運行事務處理程序
          他們能夠更有效的處理對象
          他們能夠提供更好的開發效率
          他們能夠管理更容易
          在一些例子中,因為是性能方面的原因,用對象數據庫能夠替代關系型數據庫。在不能存儲復雜對象的大規模的業務處理程序中確實是這樣的——也許有些人會認為這個必然是關系型數據庫的領地。

          對象數據庫最大的性能優勢是他們不必像關系型數據庫一樣在數據使用之前先連接數據。他們就以使用數據的方式存儲數據,這就大大提高了性能。對象數據庫能夠使用緩存技術,這樣就使得在請求數據時數據就已經存放在內存中了。對象數據庫在抽取數據時幾乎不需要進行優化。

          但開發一個新的系統,處理復雜數據如文檔、復雜圖表、網頁、多媒體等的需求不斷增長時,這些需求對象數據庫可以很好的滿足。

          當今面向對象的前景
          在軟件開發的各個方面使用對象技術的人群都在不停地增長。甚至在最后一個領域——數據庫——盡管對象數據庫還沒有取代關系型數據庫,這種增長也是十分顯著的。InfoWorld報告說52%的開發員在使用對象數據庫或者XML數據庫(通常也是一種對象數據庫)。還有一些選擇混合形式的數據庫,這種數據庫能比較容易地使用對象結構。隨著新應用程序開發過程中Web接口成為一個必不可少地部分,Web服務成為應用系統交互地一種可行的機制,構建一個面向對象的世界似乎是當今的現實。

          03年9月的InfoWorld調查也顯示了使用面向對象語言的程序員幾乎無處不在。事實上,盡管有些人宣稱使用C語言,但是面向對象的語言還是成為當今90%的程序員的選擇。調查也顯示了程序員比較喜歡基于Web的應用,易用的對象編程和腳本語言。隨著越來越多的有著正規培訓的軟件工程師進入市場,面向對象技術將成為新應用開發的唯一選擇。

          結論
          也許關系型數據庫將繼續領導數據庫市場,而對象數據庫在市場上只占有一席之地。也許對象數據庫將進一步提升市場份額,因為他們能夠處理當今使用的復雜的數據。然而,我們認為還有其他的可能:數據庫技術可能發展出一種真正的混合型產品,這種產品能提供關系接口和對象接口雙重優勢。我們知道這是有可能的。事實上,至少有一種產品,來自InterSystems的Caché,就是這樣一個產品。(Caché數據庫,描述他自己時,既不是說是關系型的,也不是說是對象的,而是后關系型數據庫)。數據庫供應商——不管他們的產品是屬于關系型還是對象型——都會朝著這個方向前進的。

          這種混合產品的方法包括給數據庫提供一個映射層,程序員通過映射層訪問數據庫。映射層應該基于開發的標準以解決不匹配障礙問題。數據庫的調用能夠用SQL完成,也可以直接請求對象類或者類的集合。映射層能夠把這些調用轉換為對數據庫的物理數據請求以抽取數據。這種方法將消除不匹配的障礙。

          改變任何一種數據類型都是非常大的挑戰。對象數據庫需要快速索引能力,以從龐大的數據集中抽取數據。在這方面做得比較好的關系型數據庫使用位圖索引技術,但數據一旦更新,這些索引就需要重新建立。因為這個原因,很少有對象數據有這個功能。對關系數據庫而言,他們需要提供更加靈活的物理數據結構。在發展過程中,關系型數據庫傾向于在物理層使用表。他們需要放棄這種不靈活的限制,允許存儲多種數據結構。數據庫使用者將獲得最大的收益。試想把對象數據庫的優勢和關系型數據庫的優勢整合在一起:

          好的處理性能
          復雜數據管理
          管理簡便
          快速開發
          靈活的查詢功能
          標準的數據訪問接口
          更好地適用于商業智能應用
          這種混合產品使使用一個數據庫引擎成為可能,并且所有應用只有一個數據定義集。Baroudi Bloor相信企業界需要混合式的數據庫產品。供應商們必須放棄他們對關系數據庫宗教式的傾向,轉向更具優勢的混合式的數據庫,否則的話他們將陷于COBOL以及打孔卡片的深淵而不能自撥。

          posted @ 2006-12-26 13:24 鴻雁| 編輯 收藏

          ETL學習心得:探求數據倉庫關鍵環節ETL的本質(轉載)

          做數據倉庫系統,ETL是關鍵的一環。說大了,ETL是數據整合解決方案,說小了,就是倒數據的工具。回憶一下工作這么些年來,處理數據遷移、轉換的工作倒還真的不少。但是那些工作基本上是一次性工作或者很小數據量,使用access、DTS或是自己編個小程序搞定。可是在數據倉庫系統中,ETL上升到了一定的理論高度,和原來小打小鬧的工具使用不同了。究竟什么不同,從名字上就可以看到,人家已經將倒數據的過程分成3個步驟,E、T、L分別代表抽取、轉換和裝載。


          其實ETL過程就是數據流動的過程,從不同的數據源流向不同的目標數據。但在數據倉庫中,ETL有幾個特點,一是數據同步,它不是一次性倒完數據就拉到,它是經常性的活動,按照固定周期運行的,甚至現在還有人提出了實時ETL的概念。二是數據量,一般都是巨大的,值得你將數據流動的過程拆分成E、T和L。
          現在有很多成熟的工具提供ETL功能,例如datastage、powermart等,且不說他們的好壞。從應用角度來說,ETL的過程其實不是非常復雜,這些工具給數據倉庫工程帶來和很大的便利性,特別是開發的便利和維護的便利。但另一方面,開發人員容易迷失在這些工具中。舉個例子,VB是一種非常簡單的語言并且也是非常易用的編程工具,上手特別快,但是真正VB的高手有多少?微軟設計的產品通常有個原則是“將使用者當作傻瓜”,在這個原則下,微軟的東西確實非常好用,但是對于開發者,如果你自己也將自己當作傻瓜,那就真的傻了。ETL工具也是一樣,這些工具為我們提供圖形化界面,讓我們將主要的精力放在規則上,以期提高開發效率。從使用效果來說,確實使用這些工具能夠非常快速地構建一個job來處理某個數據,不過從整體來看,并不見得他的整體效率會高多少。問題主要不是出在工具上,而是在設計、開發人員上。他們迷失在工具中,沒有去探求ETL的本質。


          可以說這些工具應用了這么長時間,在這么多項目、環境中應用,它必然有它成功之處,它必定體現了ETL的本質。如果我們不透過表面這些工具的簡單使用去看它背后蘊涵的思想,最終我們作出來的東西也就是一個個獨立的job,將他們整合起來仍然有巨大的工作量。大家都知道“理論與實踐相結合”,如果在一個領域有所超越,必須要在理論水平上達到一定的高度


          探求ETL本質之一
          ETL的過程就是數據流動的過程,從不同異構數據源流向統一的目標數據。其間,數據的抽取、清洗、轉換和裝載形成串行或并行的過程。ETL的核心還是在于T這個過程,也就是轉換,而抽取和裝載一般可以作為轉換的輸入和輸出,或者,它們作為一個單獨的部件,其復雜度沒有轉換部件高。和OLTP系統中不同,那里充滿這單條記錄的insert、update和select等操作,ETL過程一般都是批量操作,例如它的裝載多采用批量裝載工具,一般都是DBMS系統自身附帶的工具,例如Oracle SQLLoader和DB2的autoloader等。
          ?
          ETL本身有一些特點,在一些工具中都有體現,下面以datastage和powermart舉例來說。
          ?
          1、靜態的ETL單元和動態的ETL單元實例;一次轉換指明了某種格式的數據如何格式化成另一種格式的數據,對于數據源的物理形式在設計時可以不用指定,它可以在運行時,當這個ETL單元創建一個實例時才指定。對于靜態和動態的ETL單元,Datastage沒有嚴格區分,它的一個Job就是實現這個功能,在早期版本,一個Job同時不能運行兩次,所以一個Job相當于一個實例,在后期版本,它支持multiple instances,而且還不是默認選項。Powermart中將這兩個概念加以區分,靜態的叫做Mapping,動態運行時叫做Session。
          ?
          2、ETL元數據;元數據是描述數據的數據,他的含義非常廣泛,這里僅指ETL的元數據。主要包括每次轉換前后的數據結構和轉換的規則。ETL元數據還包括形式參數的管理,形式參數的ETL單元定義的參數,相對還有實參,它是運行時指定的參數,實參不在元數據管理范圍之內。

          3、數據流程的控制;要有可視化的流程編輯工具,提供流程定義和流程監控功能。流程調度的最小單位是ETL單元實例,ETL單元是不能在細分的ETL過程,當然這由開發者來控制,例如可以將抽取、轉換放在一個ETL單元中,那樣這個抽取和轉換只能同時運行,而如果將他們分作兩個單元,可以分別運行,這有利于錯誤恢復操作。當然,ETL單元究竟應該細分到什么程度應該依據具體應用來看,目前還沒有找到很好的細分策略。比如,我們可以規定將裝載一個表的功能作為一個ETL單元,但是不可否認,這樣的ETL單元之間會有很多共同的操作,例如兩個單元共用一個Hash表,要將這個Hash表裝入內存兩次。

          4、轉換規則的定義方法;提供函數集提供常用規則方法,提供規則定義語言描述規則。
          ?
          5、對數據的快速索引;一般都是利用Hash技術,將參照關系表提前裝入內存,在轉換時查找這個hash表。Datastage中有Hash文件技術,Powermart也有類似的Lookup功能。

          ?探求ETL本質之二(分類)
          昨在IT-Director上閱讀一篇報告,關于ETL產品分類的。一般來說,我們眼中的ETL工具都是價格昂貴,能夠處理海量數據的家伙,但是這是其中的一種。它可以分成4種,針對不同的需求,主要是從轉換規則的復雜度和數據量大小來看。它們包括
          1、交互式運行環境,你可以指定數據源、目標數據,指定規則,立馬ETL。這種交互式的操作無疑非常方便,但是只能適合小數據量和復雜度不高的ETL過程,因為一旦規則復雜了,可能需要語言級的描述,不能簡簡單單拖拖拽拽就可以的。還有數據量的問題,這種交互式必然建立在解釋型語言基礎上,另外他的靈活性必然要犧牲一定的性能為代價。所以如果要處理海量數據的話,每次讀取一條記錄,每次對規則進行解釋執行,每次在寫入一條記錄,這對性能影響是非常大的。
          2、專門編碼型的,它提供了一個基于某種語言的程序框架,你可以不必將編程精力放在一些周邊的功能上,例如讀文件功能、寫數據庫的功能,而將精力主要放在規則的實現上面。這種近似手工代碼的性能肯定是沒話說,除非你的編程技巧不過關(這也是不可忽視的因素之一)。對于處理大數據量,處理復雜轉換邏輯,這種方式的ETL實現是非常直觀的。
          3、代碼生成器型的,它就像是一個ETL代碼生成器,提供簡單的圖形化界面操作,讓你拖拖拽拽將轉換規則都設定好,其實他的后臺都是生成基于某種語言的程序,要運行這個ETL過程,必須要編譯才行。Datastage就是類似這樣的產品,設計好的job必須要編譯,這避免了每次轉換的解釋執行,但是不知道它生成的中間語言是什么。以前我設計的ETL工具大挪移其實也是歸屬于這一類,它提供了界面讓用戶編寫規則,最后生成C++語言,編譯后即可運行。這類工具的特點就是要在界面上下狠功夫,必須讓用戶輕松定義一個ETL過程,提供豐富的插件來完成讀、寫和轉換函數。大挪移在這方面就太弱了,規則必須手寫,而且要寫成標準c++語法,這未免還是有點難為最終用戶了,還不如做成一個專業編碼型的產品呢。另外一點,這類工具必須提供面向專家應用的功能,因為它不可能考慮到所有的轉換規則和所有的讀寫,一方面提供插件接口來讓第三方編寫特定的插件,另一方面還有提供特定語言來實現高級功能。例如Datastage提供一種類Basic的語言,不過他的Job的腳本化實現好像就做的不太好,只能手工繪制job,而不能編程實現Job。
          4、最后還有一種類型叫做數據集線器,顧名思義,他就是像Hub一樣地工作。將這種類型分出來和上面幾種分類在標準上有所差異,上面三種更多指ETL實現的方法,此類主要從數據處理角度。目前有一些產品屬于EAI(Enterprise Application Integration),它的數據集成主要是一種準實時性。所以這類產品就像Hub一樣,不斷接收各種異構數據源來的數據,經過處理,在實施發送到不同的目標數據中去。
          雖然,這些類看似各又千秋,特別在BI項目中,面對海量數據的ETL時,中間兩種的選擇就開始了,在選擇過程中,必須要考慮到開發效率、維護方面、性能、學習曲線、人員技能等各方面因素,當然還有最重要也是最現實的因素就是客戶的意象。

          探求ETL本質之三(轉換)
          ETL探求之一中提到,ETL過程最復雜的部分就是T,這個轉換過程,T過程究竟有哪些類型呢?
          一、宏觀輸入輸出
          從對數據源的整個宏觀處理分,看看一個ETL過程的輸入輸出,可以分成下面幾類:
          1、大小交,這種處理在數據清洗過程是常見了,例如從數據源到ODS階段,如果數據倉庫采用維度建模,而且維度基本采用代理鍵的話,必然存在代碼到此鍵值的轉換。如果用SQL實現,必然需要將一個大表和一堆小表都Join起來,當然如果使用ETL工具的話,一般都是先將小表讀入內存中再處理。這種情況,輸出數據的粒度和大表一樣。
          2、大大交,大表和大表之間關聯也是一個重要的課題,當然其中要有一個主表,在邏輯上,應當是主表Left Join輔表。大表之間的關聯存在最大的問題就是性能和穩定性,對于海量數據來說,必須有優化的方法來處理他們的關聯,另外,對于大數據的處理無疑會占用太多的系統資源,出錯的幾率非常大,如何做到有效錯誤恢復也是個問題。對于這種情況,我們建議還是盡量將大表拆分成適度的稍小一點的表,形成大小交的類型。這類情況的輸出數據粒度和主表一樣。
          3、站著進來,躺著出去。事務系統中為了提高系統靈活性和擴展性,很多信息放在代碼表中維護,所以它的“事實表”就是一種窄表,而在數據倉庫中,通常要進行寬化,從行變成列,所以稱這種處理情況叫做“站著進來,躺著出去”。大家對Decode肯定不陌生,這是進行寬表化常見的手段之一。窄表變寬表的過程主要體現在對窄表中那個代碼字段的操作。這種情況,窄表是輸入,寬表是輸出,寬表的粒度必定要比窄表粗一些,就粗在那個代碼字段上。
          4、聚集。數據倉庫中重要的任務就是沉淀數據,聚集是必不可少的操作,它是粗化數據粒度的過程。聚集本身其實很簡單,就是類似SQL中Group by的操作,選取特定字段(維度),對度量字段再使用某種聚集函數。但是對于大數據量情況下,聚集算法的優化仍是探究的一個課題。例如是直接使用SQL的Group by,還是先排序,在處理。
          二、微觀規則
          從數據的轉換的微觀細節分,可以分成下面的幾個基本類型,當然還有一些復雜的組合情況,例如先運算,在參照轉換的規則,這種基于基本類型組合的情況就不在此列了。ETL的規則是依賴目標數據的,目標數據有多少字段,就有多少條規則。
          1、直接映射,原來是什么就是什么,原封不動照搬過來,對這樣的規則,如果數據源字段和目標字段長度或精度不符,需要特別注意看是否真的可以直接映射還是需要做一些簡單運算。
          2、字段運算,數據源的一個或多個字段進行數學運算得到的目標字段,這種規則一般對數值型字段而言。
          3、參照轉換,在轉換中通常要用數據源的一個或多個字段作為Key,去一個關聯數組中去搜索特定值,而且應該只能得到唯一值。這個關聯數組使用Hash算法實現是比較合適也是最常見的,在整個ETL開始之前,它就裝入內存,對性能提高的幫助非常大。
          4、字符串處理,從數據源某個字符串字段中經常可以獲取特定信息,例如身份證號。而且,經常會有數值型值以字符串形式體現。對字符串的操作通常有類型轉換、字符串截取等。但是由于字符類型字段的隨意性也造成了臟數據的隱患,所以在處理這種規則的時候,一定要加上異常處理。
          5、空值判斷,對于空值的處理是數據倉庫中一個常見問題,是將它作為臟數據還是作為特定一種維成員?這恐怕還要看應用的情況,也是需要進一步探求的。但是無論怎樣,對于可能有NULL值的字段,不要采用“直接映射”的規則類型,必須對空值進行判斷,目前我們的建議是將它轉換成特定的值。
          6、日期轉換,在數據倉庫中日期值一般都會有特定的,不同于日期類型值的表示方法,例如使用8位整型20040801表示日期。而在數據源中,這種字段基本都是日期類型的,所以對于這樣的規則,需要一些共通函數來處理將日期轉換為8位日期值、6位月份值等。
          7、日期運算,基于日期,我們通常會計算日差、月差、時長等。一般數據庫提供的日期運算函數都是基于日期型的,而在數據倉庫中采用特定類型來表示日期的話,必須有一套自己的日期運算函數集。
          8、聚集運算,對于事實表中的度量字段,他們通常是通過數據源一個或多個字段運用聚集函數得來的,這些聚集函數為SQL標準中,包括sum,count,avg,min,max。
          9、既定取值,這種規則和以上各種類型規則的差別就在于它不依賴于數據源字段,對目標字段取一個固定的或是依賴系統的值。

          ?探求ETL本質之四(數據質量)
          “不要絕對的數據準確,但要知道為什么不準確。”
          這是我們在構建BI系統是對數據準確性的要求。確實,對絕對的數據準確誰也沒有把握,不僅是系統集成商,包括客戶也是無法確定。準確的東西需要一個標準,但首先要保證這個標準是準確的,至少現在還沒有這樣一個標準。客戶會提出一個相對標準,例如將你的OLAP數據結果和報表結果對比。雖然這是一種不太公平的比較,你也只好認了吧。
          ?
          首先在數據源那里,已經很難保證數據質量了,這一點也是事實。在這一層有哪些可能原因導致數據質量問題?可以分為下面幾類:
          1、數據格式錯誤,例如缺失數據、數據值超出范圍或是數據格式非法等。要知道對于同樣處理大數據量的數據源系統,他們通常會舍棄一些數據庫自身的檢查機制,例如字段約束等。他們盡可能將數據檢查在入庫前保證,但是這一點是很難確保的。這類情況諸如身份證號碼、手機號、非日期類型的日期字段等。
          2、數據一致性,同樣,數據源系統為了性能的考慮,會在一定程度上舍棄外鍵約束,這通常會導致數據不一致。例如在帳務表中會出現一個用戶表中沒有的用戶ID,在例如有些代碼在代碼表中找不到等。
          3、業務邏輯的合理性,這一點很難說對與錯。通常,數據源系統的設計并不是非常嚴謹,例如讓用戶開戶日期晚于用戶銷戶日期都是有可能發生的,一個用戶表中存在多個用戶ID也是有可能發生的。對這種情況,有什么辦法嗎?
          ?
          構建一個BI系統,要做到完全理解數據源系統根本就是不可能的。特別是數據源系統在交付后,有更多維護人員的即興發揮,那更是要花大量的時間去尋找原因。以前曾經爭辯過設計人員對規則描述的問題,有人提出要在ETL開始之前務必將所有的規則弄得一清二楚。我并不同意這樣的意見,倒是認為在ETL過程要有處理這些質量有問題數據的保證。一定要正面這些臟數據,是丟棄還是處理,無法逃避。如果沒有質量保證,那么在這個過程中,錯誤會逐漸放大,拋開數據源質量問題,我們再來看看ETL過程中哪些因素對數據準確性產生重大影響。
          1、規則描述錯誤。上面提到對設計人員對數據源系統理解的不充分,導致規則理解錯誤,這是一方面。另一方面,是規則的描述,如果無二義性地描述規則也是要探求的一個課題。規則是依附于目標字段的,在探求之三中,提到規則的分類。但是規則總不能總是用文字描述,必須有嚴格的數學表達方式。我甚至想過,如果設計人員能夠使用某種規則語言來描述,那么我們的ETL單元就可以自動生成、同步,省去很多手工操作了。
          2、ETL開發錯誤。即時規則很明確,ETL開發的過程中也會發生一些錯誤,例如邏輯錯誤、書寫錯誤等。例如對于一個分段值,開區間閉區間是需要指定的,但是常常開發人員沒注意,一個大于等于號寫成大于號就導致數據錯誤。
          3、人為處理錯誤。在整體ETL流程沒有完成之前,為了圖省事,通常會手工運行ETL過程,這其中一個重大的問題就是你不會按照正常流程去運行了,而是按照自己的理解去運行,發生的錯誤可能是誤刪了數據、重復裝載數據等。

          ?探求ETL本質之五(質量保證)
          上回提到ETL數據質量問題,這是無法根治的,只能采取特定的手段去盡量避免,而且必須要定義出度量方法來衡量數據的質量是好還是壞。對于數據源的質量,客戶對此應該更加關心,如果在這個源頭不能保證比較干凈的數據,那么后面的分析功能的可信度也都成問題。數據源系統也在不斷進化過程中,客戶的操作也在逐漸規范中,BI系統也同樣如此。本文探討一下對數據源質量和ETL處理質量的應對方法。
          如何應對數據源的質量問題?記得在onteldatastage列表中也討論過一個話題-"-1的處理",在數據倉庫模型維表中,通常有一條-1記錄,表示“未知”,這個未知含義可廣了,任何可能出錯的數據,NULL數據甚至是規則沒有涵蓋到的數據,都轉成-1。這是一種處理臟數據的方法,但這也是一種掩蓋事實的方法。就好像寫一個函數FileOpen(filename),返回一個錯誤碼,當然,你可以只返回一種錯誤碼,如-1,但這是一種不好的設計,對于調用者來說,他需要依據這個錯誤碼進行某些判斷,例如是文件不存在,還是讀取權限不夠,都有相應的處理邏輯。數據倉庫中也是一樣,所以,建議將不同的數據質量類型處理結果分別轉換成不同的值,譬如,在轉換后,-1表示參照不上,-2表示NULL數據等。不過這僅僅對付了上回提到的第一類錯誤,數據格式錯誤。對于數據一致性和業務邏輯合理性問題,這仍有待探求。但這里有一個原則就是“必須在數據倉庫中反應數據源的質量”。
          對于ETL過程中產生的質量問題,必須有保障手段。從以往的經驗看,沒有保障手段給實施人員帶來麻煩重重。實施人員對于反復裝載數據一定不會陌生,甚至是最后數據留到最后的Cube,才發現了第一步ETL其實已經錯了。這個保障手段就是數據驗證機制,當然,它的目的是能夠在ETL過程中監控數據質量,產生報警。這個模塊要將實施人員當作是最終用戶,可以說他們是數據驗證機制的直接收益者。
          首先,必須有一個對質量的度量方法,什么是高質什么是低質,不能靠感官感覺,但這卻是在沒有度量方法條件下通常的做法。那經營分析系統來說,聯通總部曾提出測試規范,這其實就是一種度量方法,例如指標的誤差范圍不能高于5%等,對系統本身來說其實必須要有這樣的度量方法,先不要說這個度量方法是否科學。對于ETL數據處理質量,他的度量方法應該比聯通總部測試規范定義的方法更要嚴格,因為他更多將BI系統看作一個黑盒子,從數據源到展現的數據誤差允許一定的誤差。而ETL數據處理質量度量是一種白盒的度量,要注重每一步過程。因此理論上,要求輸入輸出的指標應該完全一致。但是我們必須正面完全一致只是理想,對于有誤差的數據,必須找到原因。
          在質量度量方法的前提下,就可以建立一個數據驗證框架。此框架依據總量、分量數據稽核方法,該方法在高的《數據倉庫中的數據稽核技術》一文中已經指出。作為補充,下面提出幾點功能上的建議:
          1、提供前端。將開發實施人員當作用戶,同樣也要為之提供友好的用戶界面。《稽核技術》一文中指出測試報告的形式,這種形式還是要依賴人為判斷,在一堆數據中去找規律。到不如用OLAP的方式提供界面,不光是加上測試統計出來的指標結果,并且配合度量方法的計算。例如誤差率,對于誤差率為大于0的指標,就要好好查一下原因了。
          2、提供框架。數據驗證不是一次性工作,而是每次ETL過程中都必須做的。因此,必須有一個框架,自動化驗證過程,并提供擴展手段,讓實施人員能夠增加驗證范圍。有了這樣一個框架,其實它起到規范化操作的作用,開發實施人員可以將主要精力放在驗證腳本的編寫上,而不必過多關注驗證如何融合到流程中,如何展現等工作。為此,要設計一套表,類似于DM表,每次驗證結果數據都記錄其中,并且自動觸發多維分析的數據裝載、發布等。這樣,實施人員可以在每次裝載,甚至在流程過程中就可以觀察數據的誤差率。特別是,如果數據倉庫的模型能夠統一起來,甚至數據驗證腳本都可以確定下來,剩下的就是規范流程了。
          3、規范流程。上回提到有一種ETL數據質量問題是由于人工處理導致的,其中最主要原因還是流程不規范。開發實施人員運行單獨一個ETL單元是很方便的,雖然以前曾建議一個ETL單元必須是“可重入”的,這能夠解決誤刪數據,重復裝載數據問題。但要記住數據驗證也是在流程當中,要讓數據驗證能夠日常運作,就不要讓實施者感覺到他的存在。總的來說,規范流程是提高實施效率的關鍵工作,這也是以后要繼續探求的。

          ?探求ETL本質之六(元數據漫談)
          對于元數據(Metadata)的定義到目前為止沒有什么特別精彩的,這個概念非常廣,一般都是這樣定義,“元數據是描述數據的數據(Data about Data)”,這造成一種遞歸定義,就像問小強住在哪里,答,在旺財隔壁。按照這樣的定義,元數據所描述的數據是什么呢?還是元數據。這樣就可能有元元元...元數據。我還聽說過一種對元數據,如果說數據是一抽屜檔案,那么元數據就是分類標簽。那它和索引有什么區別?
          元數據體現是一種抽象,哲學家從古至今都在抽象這個世界,力圖找到世界的本質。抽象不是一層關系,它是一種逐步由具體到一般的過程。例如我->男人->人->哺乳動物->生物這就是一個抽象過程,你要是在軟件業混會發現這個例子很常見,面向對象方法就是這樣一種抽象過程。它對世界中的事物、過程進行抽象,使用面向對象方法,構建一套對象模型。同樣在面向對象方法中,類是對象的抽象,接口又是對類的抽象。因此,我認為可以將“元”和“抽象”換一下,叫抽象數據是不是好理解一些。
          常聽到這樣的話,“xx領導的講話高屋建瓴,給我們后面的工作指引的清晰的方向”,這個成語“高屋建瓴”,站在10樓往下到水,居高臨下,能砸死人,這是指站在一定的高度看待事物,這個一定的高度就是指他有夠“元”。在設計模式中,強調要對接口編程,就是說你不要處理這類對象和那類對象的交互,而要處理這個接口和那個接口的交互,先別管他們內部是怎么干的。
          元數據存在的意義也在于此,雖然上面說了一通都撤到哲學上去,但這個詞必須還是要結合軟件設計中看,我不知道在別的領域是不是存在Metadata這樣的叫法,雖然我相信別的領域必然有類似的東東。元數據的存在就是要做到在更高抽象一層設計軟件。這肯定有好處,什么靈活性啊,擴展性啊,可維護性啊,都能得到提高,而且架構清晰,只是彎彎太多,要是從下往上看,太復雜了。很早以前,我曾看過backorifice的代碼,我靠,一個簡單的功能,從這個類轉到父類,又轉到父類,很不理解,為什么一個簡單的功能不在一個類的方法中實現就拉到了呢?現在想想,還真不能這樣,這雖然使代碼容易看懂了,但是結構確實混亂的,那他只能干現在的事,如果有什么功能擴展,這些代碼就廢了。

          我從98年剛工作時就開始接觸元數據的概念,當時叫做元數據驅動的系統架構,后來在QiDSS中也用到這個概念構建QiNavigator,但是現在覺得元數據也沒啥,不就是建一堆表描述界面的元素,再利用這些數據自動生成界面嗎。到了數據倉庫系統中,這個概念更強了,是數據倉庫中一個重要的部分。但是至今,我還是認為這個概念過于玄乎,看不到實際的東西,市面上有一些元數據管理的東西,但是從應用情況就得知,用的不多。之所以玄乎,就是因為抽象層次沒有分清楚,關鍵就是對于元數據的分類(這種分類就是一種抽象過程)和元數據的使用。你可以將元數據抽象成0和1,但是那樣對你的業務有用嗎?必須還得抽象到適合的程度,最后問題還是“度”。
          數據倉庫系統的元數據作用如何?還不就是使系統自動運轉,易于管理嗎?要做到這一步,可沒必要將系統抽象到太極、兩儀、八卦之類的,業界也曾定義過一些元數據規范,向CWM、XMI等等,可以借鑒,不過俺對此也是不精通的說,以后再說

          posted @ 2006-12-26 13:22 鴻雁| 編輯 收藏

          程序員,你能掌握你自己嗎?

          程序員,是最聰明的一個群體。以前是這樣,現在我也同樣這么認為。可是,作為最聰明的我們,你又對自己能把握多少呢?
          ?
          程序員是最不善于言語的。任何辛苦,程序員都藏在了心里。市場人員的辛苦,天天在講。就連你都,天天聽得都感覺他們很辛苦。可是卻沒有人來說你。你可能會在位置上發發牢騷,可能會暗地里傷神流淚,也可能在自己的博客上和網友分享。可唯獨不會向領導反映。可惜,領導向來是誰辛苦給誰加薪水。所以,你最好運氣好,遇到一位會體察下情的領導。否則,我只能問你,你能掌握你自己的命運嗎?
          ?
          程序員又是最揉不得沙子的。對于任何開發方面的問題,他們都有自己的獨到意見。任何不同的意見,都會讓他竭盡全力地去找出其中的漏洞。他們的前輩都是從這條路上,踏過無數人的口水,成長起來的。當你看到的沙子在你的領導身上的時候,你會怎么辦呢?是放棄程序員的尊嚴去漠不關心,還是放下一切,去奮力爭取?所以,你最好運氣好,遇到會告訴你如何溝通的領導。否則,我只能問你,你能掌握你自己的命運嗎?
          ?
          程序員也是最不知道計劃的。經常的加班,并不能說明你對計劃掌握很好。反而是說明你被公司剝削地更厲害。你最喜歡的是鋪開做所有事,可往往所有事都不能按時提交。剛開始的時候,任務少還好說。當能力提升,任務繁多的時候,這種情況越來越明顯。于是,你繼續著程序員最經常的行為:加班。加班讓你不需要關心晚餐的來源,加班讓你不需要關心回家的路費,加班讓你不需要尋找男女朋友。當你被加班困的焦頭爛額的時候,你會怎么辦呢?是繼續頂下去,還是放棄跳槽?所以,你最好運氣好,遇到一個能幫你統籌時間的領導。否則,我只能問你,你能掌握你自己的命運嗎?
          ?
          程序員是最最樂觀的。你要讓他估計時間。他往往都是告訴你比最好情況都要好的結果。問他這個系統有多難,哈哈,每一點都很簡單。是的,也許每一點確實很簡單,可是程序員卻總是忘記那些繁雜的關系。你要是領導,會一次一次地失望,一次一次地給他壓力。可是,你認為他這樣的后果會是什么呢?所以,你只能為他祈禱,能遇到一位會幫助學會如何估算的領導。否則,你只能問他,能把握自己的命運嗎?
          ?
          不管如何,程序員總還是樂觀地、不自覺地看著別人身上的沙子。可是除了等待遇到一位好領導,我們還能做什么呢?與其讓別人教我們,還不如我們自己去學習。程序員啊,什么時候你能夠掌握你自己?

          posted @ 2006-12-13 15:13 鴻雁| 編輯 收藏

          學會與高手相處(轉載)

          很多人都在嘆息自己隊伍中高手很少。但是,如果隊中真的有一高手,你以為會一帆風順嗎?

          凡高手者,往往必然是強勢者。他必有其不容質疑的能力體現。一旦這個能力遭到懷疑時,你可能會看到意想不到的破壞力。

          凡高手者,往往也是好為人師者。他必然要體現他的影響力,不管有意無意。一旦有不如他意的地方,他可以完全推倒。然后告訴你他會怎么做。當然了。他的做法必然又很多非常正確的地方。這點也無庸置疑。

          凡高手者,往往是好爭論者。你的意見到他那里,往往會遇到來自各個方面的攻擊。他會告訴你什么地方可能有什么問題。也會告訴你他有更好的方法。總之,爭論不可避免。

          凡高手者,往往又是執著者。也許有時候你會認為你已經完全占有上風。但是他可能會告訴你,“你不管怎么說,我也堅持我的。”不是靠講道理,就能讓他就范的。

          凡高手者,往往是孤獨者。你往往只看到他風光的一面,卻難以體會他的無奈。自己的很多想法,無法與人切磋。說高處不勝寒一點不為過。

          凡高手者,往往是最忙的人。除了本組的事務,很可能牽涉到其他各部門的事情。有時候還有很多求教。真正靜下心來做該做的,嘿嘿,難啊!

          所以,不管幸運還是不幸運,你和一位高手在一起,你可以有十二分的準備。

          假如你是一位新手,那你要隨時準備接受高手的提問、質疑。他隨時可能抓你去,告訴你的問題所在。所以到那個時候,你只要記住一句話:嗯,我就該!

          當然了,也可能問你問題,千萬不要緊張。高手給你的往往不是壓力,更多的是你對你的勉勵。因為關注才會指導你。所以,就算錯了,也不要緊。

          關鍵在于,錯了之后,要將錯誤做一個非常完整的分析,再對比一下正確的方案。然后統一匯報給高手。不為彌補你原來的失誤,而是為了贏取他對你的繼續關注。

          假如你自己感覺狀態很好,嘿嘿,那你要準備好隨時進行戰斗。因為你的每一次發言都是對高手的挑釁。他會非常容易地進入戰斗狀態。這個時候,你只要記住一點,不管爭論結果如何,你都是贏家。

          最最忌諱的就是,將證明自己的觀點作為你戰斗的目標。因為,在高手看來,他往往能清楚的認識到,自己有可能存在的錯誤。而他追求的正是追求真理的快感,一旦你的立場錯了,你會發現你越是面紅耳赤,他卻越是興高采烈。

          假如你也自認為高手。別的什么也不說,送你一句話:英雄惺惺相惜!

          假如非常不幸,你不是一位高手,就像我。又有一位高手的手下。嘿嘿。我只能告訴你,你很走運。至少,你不是一個人在面對這個問題。

          首先要設法滿足高手的種種欲望。高手的求知欲,高手的表現欲,高手的自尊欲。尊敬你的高手,是你必須做的第一步。

          幫助你的高手,安排他的時間。避免不必要的干擾。

          不要隨意將你的壓力反反復復地傳達給你的高手。高手很容易理解壓力。不過他很可能會遇到很多自己無法處理的問題。這個時候,你要做的不是談壓力,而是給他解除干擾。

          你很可能在某方面做的不好,在你意識到的第一時間和你的高手坦白。他會堅決支持你的。

          讓高手成為高手。一定要完全體現高手的地位和尊嚴。否則,干脆就不要高手。這位標桿一定要樹好。他好了,你的一幫子人都會好起來。

          愿你能像我一樣,有一位高手在身旁!那是一件很幸福的事情。

          posted @ 2006-12-13 15:11 鴻雁| 編輯 收藏

          利用AJAX+J2EE開發組織機構管理系統(轉載)五

          五、 數據接口的實現(見Org.jpg文件)

            Org.JSP文件用來在服務器上運行Java的類與前臺web頁之間架起一座橋。取到中間件的接口作用。

            這里分析部分代碼:

          <%@ page contentType="text/html; charset=GBK" %>
          <%@ page import="java.sql.*" %>
          <%@ page import="javax.naming.*" %>
          <%@ page import="javax.sql.*" %>
          <%@ page import="tool.*" %>
          <%@ page import="orgNew.*" %>
          <%@ page import="org.w3c.dom.*" %>
          //上面主要是引用一些java類
          <%
          try{
           //request.setCharacterEncoding("GBK");
           Document doc = XmlTool.createDocumentFromRequest(request);
           //建立web面文檔請求的文檔對象
           Connection conn = ConnTool.getConnectionFromPool();
           //獲取請求的方法名
           String mode=request.getParameter("mode");
           //out.println("ccc");
           //如果方法中沒有其它參數則讀取組織樹數據
           if(mode == null){
            /* int OrgId = Integer.parseInt(request.getParameter("id"));
            String str = orgManager.getChildOrg(OrgId, conn);
            out.println(str);
            */
           String str = orgManager.getTree(conn);
           //out.println(str);
           out.println(str);
          }else if(mode.equals("createOrg")){
           //如果是createOrg方法則建立一個組織
           int parentOrgId = Integer.parseInt(request.getParameter("parentOrgId"));
           //取出傳遞來的第一個參數parentOrgId
           int OrgId = orgManager.createOrg(parentOrgId, conn);
           //調用orgManager 類的createOrg方法來建立一個組織
           out.println(OrgId);
           //返回結果
          }
          conn.close();
          }
          catch(Exception e){
           e.printStackTrace();
          }
          %>

            六、 后臺數據的實現

            1. 數據結構的定義

            這里,我們主要有三個表。一個是組織結構表,一個是人員表person,一個組織人員關聯表orgPerson。組織結構表有OrgCode(組織代碼)、OrgName(組織名稱)、orgId(組織Id), parentOrgId(父Id)。人員表有personCode(人員代碼)、personName(人員名稱), sex(性別)、personId(人員Id)。orgPerson表有orgId, personId。

            2. 數據庫的連接

            WEB應用程序常用MySQL作后臺數據庫,這是因為MySQL簡單、高效。這里我們也用MySQL作為數據庫。Java中用jdbc連接數據庫。下面是連接數據庫的CODE:

          public static Connection getConnectionFromPool() throws Exception {
           Context ctx = new InitialContext();
           DataSource ds = (DataSource) ctx.lookup("java:/erpds");
           return ds.getConnection();
          }

          /**
          * 取數據庫鏈接對象
          * @return Connection 數據庫鏈接對象
          * @throws Exception
          */
          /*
          public static Connection getDirectConnection() throws Exception {
           Class.forName("com.sybase.jdbc2.jdbc.SybDriver");
           String url = "jdbc:sybase:Tds:19.64.13.16:4100/wydb?charset=iso_1";
           String user = "sa";
           String password = "2860008";
           Connection conn = DriverManager.getConnection(url, user, password);
           return conn;
          }
          */

            3. 業務邏輯層的實現

            后臺開發我們用Java類來實現。這里我們開發了一個orgNew包,類名為orgManager。此類封裝了與數據庫操作有關的方法。通過main可調試程序的正確性。

            這里給出了新增加一個組織的全部代碼和通過XML取得樹結構信息的代碼,樹結構通過遞歸實現。

          package orgNew;// Java類所打的包
          import tool.*;
          import java.sql.*;
          import java.util.*; // 引用Java類的
          public class orgManager {
           public orgManager() { }
           public static void main(String[] args) throws Exception {
            Connection conn = tool.ConnTool.getDirectConnection();// 引用數據訪問類
            conn.setAutoCommit(false);

            orgManager orgManager1 = new orgManager();
            orgManager1.createOrg(0, conn); //測試建立組織是否正確
           }
           //建立一個組織
           public static int createOrg(int parentOrgId, Connection conn) throws
           Exception {
            String sql = "insert into Org (OrgName, parentOrgId) values('新組織', ?)";
            PreparedStatement pstat = conn.prepareStatement(sql);
            pstat.setInt(1, parentOrgId);
            pstat.executeUpdate();
            pstat.close();

            Statement stat = conn.createStatement();
            String sql2 = "select max(OrgId) from Org";
            ResultSet rs = stat.executeQuery(sql2);
            rs.next();
            int OrgId = rs.getInt(1);
            rs.close();
            stat.close();
            System.out.println(OrgId);
            return OrgId;
           }
          }
          //通過遞歸得到組織信息的XML格式的數據
          public static String getTree(Connection conn) throws
          Exception {
           StringBuffer ret = new StringBuffer();//定義可緩沖的字符流
           ret.append("<?xml version='1.0' encoding='gb2312'?><tree id='0'>");//定義XML格式的頭信息
           ret.append(" <item child='1' text='組織' id='1' >");//插入結點體。注樹結點以item為標記
           ret.append(getChildTree(1, conn));
           ret.append("</item>");//結點體結束標記
           ret.append("</tree>");//樹結束標記
           return ret.toString();//返回字符流
          }

          public static String getChildTree(int OrgId, Connection conn) throws
          Exception {
           StringBuffer ret = new StringBuffer();
           String sql = "select a.OrgId, a.OrgName, a.OrgCode,count(b.parentOrgId) from Org a " +
            "left join Org b on a.OrgId = b.parentOrgId " +
            "where a.parentOrgId = ? " +
            "group by a.OrgId, a.OrgName";
           PreparedStatement pstat = conn.prepareStatement(sql);
           pstat.setInt(1, OrgId);
           ResultSet rs = pstat.executeQuery();
           while (rs.next()) {
            int childOrgId = rs.getInt(1);
            String childOrgName = rs.getString(2);
            String childOrgCode = rs.getString(3);
            if (childOrgCode == null) {
             childOrgCode = " ";
            }
            if (childOrgName == null) {
             childOrgName = "新組織";
            }
            int childCount = rs.getInt(3);
            if (childCount > 0) {
             childCount = 1;
            }
            ret.append("<item child='" + childCount + "' text='" + childOrgName +
          "' id='" +childOrgId + "' code='"+childOrgCode+"'>");
            ret.append(getChildTree(childOrgId, conn));
            ret.append("</item>");
           }
           rs.close();
           pstat.close();
           return ret.toString();
          }

            其它代碼見orgManager.java文件。

            七、 總結

            本文件通過一個實例全面介紹了Ajax開發的各個細節。通過與J2ee的結合來實現三層分布式開發的層次劃分,后臺與前端的調用。數據的讀取、訪問及展現。

            通過這個實例,我們可見,Ajax使WEB中的界面與應用分離。數據與呈現分離的分離,有利于分工合作、減少非技術人員對頁面的修改造成的WEB應用程序錯誤、提高效率、也更加適用于現在的發布系統。也可以把以前的一些服務器負擔的工作轉嫁到客戶端,利于客戶端閑置的處理能力來處理。

            Ajax是傳統WEB應用程序的一個轉變。以前是服務器每次生成HTML頁面并返回給客戶端(瀏覽器)。Ajax理念的出現,揭開了無刷新更新頁面時代的序幕,并有代替傳統web開發中采用form(表單)遞交方式更新web頁面的趨勢,可以算是一個里程碑。

          posted @ 2006-12-05 21:38 鴻雁| 編輯 收藏

          利用AJAX+J2EE開發組織機構管理系統(轉載)四

          四、 XML 與XSL文件設計

            XML是種可擴展的標記語言,它具有開放的、可擴展的、可自描述的語言結構,它已經成為網上數據和文檔傳輸的標準。XSLT的目的是將信息內容與 Web 顯示分離,HTML 通過按抽象概念(如段落、重點和編號列表)定義顯示來實現設備獨立性。XSLT用來具體顯示控件,設置控件風格。

            Ajax主要使用XML和XSLT進行數據交換與處理。

            1. 樹信息的XML文件(見root.xml文件)

            XML是標記語言,元素必須成對出現。樹結構中以tree為根結點,以item為結點體,屬性text指出結點所顯示的文本,id指出唯一的所標識號。

          <?xml version='1.0' encoding='gb2312'?>
          <tree id="0">
          <item child="1" text="組織" id="1" >
          </item>
          </tree>

            這文件并不是必要的,只是為了系統能獨立運行才加的。事實如果連接了后臺數據是不需要的。只要吧OrgTree.loadXML("root.xml?0")改為OrgTree.loadXML("Org.jsp")就可以了。

            2. 人員信息XML文件(見peorson.xml文件)

            說明![CDATA[]]可在任何顯示任何格式的文本,文本中可插入其它任何字符。這文件也不是必要的。

            3. 人員信息展現的xsl文件(見addOrgPerson.xsl文件)

            xsl文件同樣是XML格式文件。所以一律遵守XML標準。下面對主要的行講解:

          <?xml version="1.0" encoding="gb2312"?>
          //這是定義xml文件的首行。用來指明版本及字符集
          <xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl" language="JavaScript">
          //這里定義了stylesheet 元素。并指出其國際命名的組織及語言。
          <xsl:template match="/">
          <xsl:apply-templates select="peorsones"/>
          </xsl:template>
          //上面是匹配的規則。"/"表示從根結開始去匹配。匹配到下面的peorsones標記。這是正則表達式有關的學問。我們只要理解就可以。
          <xsl:template match="peorsones">
          //當匹配上peorsones時所要做的事情。
          <table id="tbList" border="1" width="100%">
          //定義一個id為"tbList的表格。此表格是顯示在WEB上的
          <xsl:for-each select="peorsone">
          //循環匹配peorsone
          <tr>
          //定義tbList表格的一行,并在行上增加一個叫seqNo的屬性名,值為匹配到的seqNo(序號)
          <xsl:attribute name="seqNo"><xsl:value-of select="@seqNo"/></xsl:attribute>
          <td>
          //定義行上的一列,列又去匹配
          <xsl:apply-templates select="."/>
          </td>
          </tr>
          </xsl:for-each>
          </table>
          </xsl:template>

          <xsl:template match="peorsone">
          <table border="1" width="100%">
          <tr>
          //定義寬為5%的一列,在該列上插入一個checkbox控件
          <td width="5%">
          <input type="checkbox" value="on" size="10"></input>
          </td>
          //定義一個不顯示的列,在該列上插入一個text控件,text的值為匹配到的personId(人員Id)
          <td style="display:none">
          <input type="text" size="25">
          <xsl:attribute name="value"><xsl:value-of select="personId"/></xsl:attribute>
          </input>
          </td>
          <td width="30%">
          <input type="text" size="20">
          <xsl:attribute name="value"><xsl:value-of select="personCode"/></xsl:attribute>
          </input>
          </td>
          <td width="40%">
          <input type="text" size="40">
          <xsl:attributename="value"><xsl:value-of select="personName"/></xsl:attribute>
          </input>
          </td>
          //定義一個width為28%的列,在該列上插入一個下拉列表select 控件,select的值如果匹配到為0時則為"男",1時則為"女"
          <td width="28%">
          <select size="1">
          <option value="0">
          <xsl:if test=".[sex=0]">
          <xsl:attribute name="selected">true</xsl:attribute>
          </xsl:if>

          </option>
          <option value="1">
          <xsl:if test=".[sex=1]">
          <xsl:attribute name="selected">true</xsl:attribute>
          </xsl:if>
          女</option>
          </select>
          </td>
          //定義一列,在該列上插入一個button控件,onclick 事件為自定義的方法,該方法傳遞當前單擊的按紐
          <td width="*">
          <button onclick="openPersonRolePage(this)" style="width: 36; height: 21">角色</button>
          </td>
          </tr>
          </table>
          </xsl:template>
          </xsl:stylesheet>

          posted @ 2006-12-05 21:34 鴻雁| 編輯 收藏

          利用AJAX+J2EE開發組織機構管理系統(轉載)三

          3. 人員管理的實現

            人員可以增加、刪除、編輯。同時當選擇樹結點時應該把人員顯示出來供編輯、查看......

            1) 增加人員

            人員增加實現的原理是在personDom中加入結點peorsone,該結點相當于表的一行,設置屬性。同時在peorsone中不繼地加入其它結點,代表數據庫的字段,且必須與XLT文件的標號同名。這些結點相當該行的列。最后在表中插入一行,行上插入一列,并顯示之。

          function addPerson(){
           var seqNo = nextSeq;
           nextSeq++;
           var peorsonNode = personDom.createNode("1", "peorsone","");
           peorsonNode.setAttribute("isNew", "Y");
           peorsonNode.setAttribute("isDelete", "N");
           peorsonNode.setAttribute("seqNo", seqNo);
           personDom.documentElement.appendChild(peorsonNode);
           var PersonId= personDom.createNode("1", "personId", "");
           peorsonNode.appendChild(PersonId);
           var personCode= personDom.createNode("1", "personCode", "");
           peorsonNode.appendChild(personCode);
           var PersonName= personDom.createNode("1", "personName", "");
           peorsonNode.appendChild(PersonName);
           var Sex= personDom.createNode("1", "sex", "");
           peorsonNode.appendChild(Sex);
           var tr = tbList.insertRow(tbList.rows.length);
           tr.setAttribute("seqNo", seqNo);
           var td = tr.insertCell(0);
           td.innerHTML = peorsonNode.transformNode(stylesheet);
          }

            2) 刪除人員

            人員刪除同樣是調用Org.jsp 文件中的deletePerson方法來實現,該方法傳遞所刪除的人員ID。如何確定人員ID是通過讀取隱藏的ID,并掃描整個表,看那些被選中。這里我們要注意是提供多項選擇的。

          function deletePerson(){
           for(var i=0; i<tbList.rows.length; i++){
            var row=tbList.rows[i].cells[0].children[0].rows[0];
            if(row.cells[0].children[0].checked)
            {
             var personId=row.cells[1].children[0].value;
             if(personId>0)
             {
              var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
              xmlhttp.open("POST","Org.jsp?mode=deletePerson&personId=" + personId, false);
              xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
              xmlhttp.send();
             }
             tbList.deleteRow(i);
             i--;
            }
           }
          }

            3) 編輯人員

            人員修改我們要判定哪些行被修改了。剛增加但沒保存的行應該是新增而不是修改的。

          function save(){
           if( modifyOrg()=="N")
           {
            return;
           }
           for(var i=0; i<tbList.rows.length; i++)
           {
            var row=tbList.rows[i].cells[0].children[0].rows[0];
            var personId=row.cells[1].children[0].value;
            var seqNo = tbList.rows[i].getAttribute("seqNo");
            var staffNode = personDom.selectSingleNode("http://peorsone[@seqNo='" + seqNo + "']");
            var personCode=row.cells[2].children[0].value;
            var personName=row.cells[3].children[0].value;
            var sex=row.cells[4].children[0].value; //alert(staffN;ode );
            if(staffNode.getAttribute("isNew") == "Y")
            {
             createPerson(CurrNodeId,personCode,personName,sex);
            }
            else
            {
             var strXML = "<?xml version='1.0' encoding='gb2312'?>" +
              "<data>" +
              "<personCode><![CDATA[" + personCode+ "]]></personCode>" +
              "<personName><![CDATA[" + personName + "]]></personName>" +
              "<sex><![CDATA[" + sex+ "]]></sex>" +
              "<personId><![CDATA[" + personId+ "]]></personId>" +
              "</data>";
             //alert(strXML );
             var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
             xmlhttp.open("POST","Org.jsp?mode=modifyPerson", false);
             xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
             xmlhttp.send(strXML );
            }
           }
          }

          posted @ 2006-12-05 21:32 鴻雁| 編輯 收藏

          利用AJAX+J2EE開發組織機構管理系統(轉載)二

          三、 前端頁面的主要編碼

            1. 樹的實現

            在WEB上實現樹結構,同樣我們是通過Ajax來實現的。樹上可以顯示自定義的圖標,可以插入、刪除、結點。并且結點可任意移動。這里我們不重點講樹的實現技術,我們已經封裝好了,你只要按要求去改動就是了。

            1) 鍵接樹型文件

            在<head>與</head>之間鍵接我們的與樹有關的文件, 代碼如下:

          <link rel="STYLESHEET" type="text/css" href="css/dhtmlXTree.css">
          <script src="js/dhtmlXCommon.js"></script>
          <script src="js/dhtmlXTree.js"></script>

            2) 裝載方法

            在頁面的文檔打開時裝載自定義方法, preLoadImages方法實現樹控件的圖標定義,doOnLoad實現樹控件的圖標定義代碼如下:

          <body onload="preLoadImages();doOnLoad();">

            3) 編寫方法

          //doOnLoad實現裝載并顯示樹。設置樹屬性等。
          function doOnLoad(){
           OrgTree=new dhtmlXTreeObject(document.getElementById('divTree'),"100%","100%",0);
           //dhtmlXTreeObject是樹對象,通過新建對象,指定樹顯示的DIV可定義樹。
           OrgTree.setImagePath("imgs/");//設置樹的圖片所在位置
           OrgTree.setDragHandler();//設置樹結點拖動
           OrgTree.enableDragAndDrop(true) //設置樹結點是否可拖動
           OrgTree.setDragHandler(myDragHandler); //設置樹結點拖動時所執行的方法
           OrgTree.setOnClickHandler(mySelectHandler); //設置樹單擊時所執行的方法
           //OrgTree.setXMLAutoLoading("Org.jsp");//裝載樹結點數據。數據來源如Org.jsp所返回的XML格式的字符串,數據是動態裝載,且當展開時才裝載。
           OrgTree.loadXML("root.xml?0");//裝載樹結點數據。數據來源root.xml文件,并且從xml文件的ID號為0處讀取數據。
           //OrgTree.loadXML("Org.jsp");//裝載樹結點數據。數據來源如Org.jsp所返回的XML格式的字符串,并且是一次性全部裝載數據。
          }
          //preLoadImages方法實現樹控件的圖標定義
          function preLoadImages(){
           var imSrcAr = new Array("line1.gif","line2.gif","line3.gif","line4.gif","minus2.gif","minus3.gif",
            "minus4.gif","plus2.gif","plus3.gif","plus4.gif","book.gif","books_open.gif","books_close.gif",
            "magazine_open.gif","magazine_close.gif","tombs.gif","tombs_mag.gif","book_titel.gif")
           var imAr = new Array(0);
           for(var i=0;i<imSrcAr.length;i++){
            imAr[imAr.length] = new Image();
            imAr[imAr.length-1].src = "imgs/"+imSrcAr[i]
           }
          }

            2. 組織管理的實現

            組織可以增加、刪除、編輯。同時當選擇樹結點時應該把組織顯示出來供編輯,查看。為了實現這些功能,你只要按要求去改動就是了。

            1) 全局變量的定義

            許多地方我們要用到一些公共變量,我們在<script>與</script>之間定義全局變量, 代碼如下:

          var OrgTree = null; //組織樹Dom
          var nextSeq = 0;//人員管理的順序號(流水號)
          var personDom;//人員Dom
          var CurrNodeId;//當前結點Id

            2) 初始化

            當頁面打開時我們要控件好那部分該顯示,那部分要隱藏。且對全局變量的賦值等,組織類型裝載。在頁面的文檔打開時裝載自定義方法init(), init方法實現初始化。

          <body onload="init();">

            init方法實現如下:

          function init(){
           //定義personDom為一個XMLDOM'對象
           personDom= new ActiveXObject('Microsoft.XMLDOM');
           personDom.async = false;
           //定義stylesheet為一個XMLDOM'對象,且stylesheet為personDom確定顯示風格
           stylesheet = new ActiveXObject('Microsoft.XMLDOM');
           stylesheet.async = false;
           stylesheet.load("addOrgPerson.xsl"); //裝載stylesheet的風格定義文件

           //裝載組織類型數據
           var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
           xmlhttp.open("POST","Org.jsp?mode=GetOrgType", false);
           xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
           xmlhttp.send();
           retXml=xmlhttp.responseText;
           // alert(retXml);
           //把組織類型插入下拉列表控件中
           var OrgDoc = new ActiveXObject('Microsoft.XMLDOM');
           OrgDoc.async = false;
           OrgDoc.loadXML(retXml);
           var root = OrgDoc.documentElement;
           oNodeList = root.childNodes;
           txtType.options.length =oNodeList.length;
           for (var i=0; i<oNodeList.length; i++)
           {
            Item = oNodeList.item(i);
            var OrgTypeId=Item.childNodes(0).text;
            var OrgTypeName=Item.childNodes(1).text;
            txtType.options[i].value=OrgTypeId;
            txtType.options[i].text=OrgTypeName;
            // txtType.options[0].
           }
          }

            3) 編寫樹拖動及選擇結點的方法

          // myDragHandler實現樹結點拖動時重新指定父子關系。
          function myDragHandler(idFrom,idTo){
           var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
           xmlhttp.open("POST","Org.jsp?mode=moveOrg&orgId=" + idFrom + "&newparentOrgId=" + idTo, false);
           xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
           xmlhttp.send();
           retXml=xmlhttp.OrgponseText;

           return true;
          }
          // mySelectHandler實現選擇樹結點對系統的控制,同時顯示組織信息及該組織下的人員。
          function mySelectHandler(id){
           tbOrg.style.display="block";
           divOrgMemo.style.display="none";
           divOrgInfo.style.display="none";

           if(id==1)
           {
            divOrgMemo.style.display="block";
            div1.style.display="none";
            div2.style.display="none";
            div3.style.display="none";
            divContent.style.display="none";
            div5.style.display="none";
           }
           else
           {
            divOrgInfo.style.display="block";
            div1.style.display="block";
            div2.style.display="block";
            div3.style.display="block";
            divContent.style.display="block";
            div5.style.display="block";
           }
           CurrNodeId=id;
           //裝載組織信息并顯示在編碼和名稱的文本控件上。
           loadOrg(id);
           //裝載某組織下人員信息
           var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
           xmlhttp.open("POST","Org.jsp?mode=GetPerson&orgId=" + id, false);
           xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
           xmlhttp.send();

           retXml=xmlhttp.responseText;
           personDom.loadXML (retXml);
           //給人員信息的每行加上序號
           for(var i=0; i<personDom.documentElement.childNodes.length; i++){
            personDom.documentElement.childNodes[i].setAttribute("seqNo", nextSeq);
            nextSeq++;
           }
           //人員信息顯示在divContent上面
           divContent.innerHTML = personDom.transformNode(stylesheet);
          };
          //裝載組織信息并顯示在編碼和名稱的文本控件上。
          function loadOrg(OrgId){
           if(OrgId == null){
            OrgId = OrgTree.getSelectedItemId();
           }
           if(OrgId == ""){
            tbOrg.style.display = "none";
            return;
           }
           var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
           xmlhttp.open("POST","Org.jsp?mode=loadOrg&OrgId=" + OrgId, false);
           xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
           xmlhttp.send();
           retXml=xmlhttp.responseText;
           var OrgDoc = new ActiveXObject('Microsoft.XMLDOM');
           OrgDoc.async = false;
           OrgDoc.loadXML(retXml);
           if(OrgId != 1){
            txtCode.value = OrgDoc.selectSingleNode("http://OrgCode").text;
            txtName.value = OrgDoc.selectSingleNode("http://OrgName").text;
           }
           tbOrg.style.display = "block";
          }

            4) 建立組織

            組織建立主要是通過調用XMLHTTP對象來實現。我們主要學會如何調用XMLHTTP。組織建立應該在后臺實現,把組織信息插入數據庫中。這里我們通過JSP來實現。我們的Org.jsp 文件中有個createOrg方法,該方法傳遞一個父ID。

          function createOrg(parentOrgId){
           var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
           xmlhttp.open("POST","Org.jsp?mode=createOrg&parentOrgId=" + parentOrgId, false);
           xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
           xmlhttp.send();
           retXml=xmlhttp.responseText;
           var orgId = (new Number(retXml)).toString();
           return orgId;
          }

            5) 刪除組織

            組織刪除同樣是調用Org.jsp 文件中的deleteOrg方法來實現,該方法傳遞所刪除的結點ID。

          function deleteOrg(){
           var OrgId = OrgTree.getSelectedItemId();
           var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
           xmlhttp.open("POST","Org.jsp?mode=deleteOrg&OrgId=" + OrgId, false);
           xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
           xmlhttp.send();
          }

            6) 編輯組織

            組織修改是調用Org.jsp 文件中的modifyOrg方法來實現,該方法傳遞所修改的結點ID。同時修改的數據通過自定義的XML格式的字符串傳送,這時通過send字符串來實現。修改前數據一律要驗證其合法性,并提示錯誤信息。

          function modifyOrg(){
           if(OrgTree.getSelectedItemId() == ""){
            return "N";
           }
           if(txtCode.value == ""){
            alert("請輸入編碼!");
            return "N";
           }
           if(txtName.value == ""){
            alert("請輸入名稱!");
            return"N";
           }
           var OrgId = OrgTree.getSelectedItemId();
           var OrgKind;
           //alert(txtType.options[txtType.selectedIndex].value)
           var strModify = "<?xml version='1.0' encoding='gb2312'?>" +
             "<data>" +
             "<OrgCode><![CDATA[" + txtCode.value + "]]></OrgCode>" +
             "<OrgName><![CDATA[" + txtName.value + "]]></OrgName>" +
             "<OrgKind><![CDATA[" + txtType.options[txtType.selectedIndex].value+ "]]></OrgKind>" +
             "</data>";
           var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
           xmlhttp.open("POST","Org.jsp?mode=modifyOrg&OrgId=" + OrgId, false);
           xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
           xmlhttp.send(strModify);
           OrgTree.setItemText(OrgTree.getSelectedItemId(),txtName.value);
          }

          posted @ 2006-12-05 21:30 鴻雁| 編輯 收藏

          利用AJAX+J2EE開發組織機構管理系統(轉載)一

          一、 概述

            AJAX是今年初才問世的新技術,是Asynchronous JavaScript and XML的縮寫。它是一組開發Web應用程序的技術,它使瀏覽器可以為用戶提供更為自然的瀏覽體驗。每當需要更新時,客戶端Web頁面的修改是異步的和逐步增加的。
          這樣,AJAX在提交Web頁面內容時大大提高了用戶界面的速度。在基于AJAX的應用程序中沒有必要長時間等待整個頁面的刷新。頁面中需要更新的那部分才進行更改,如果可能的話,更新是在本地完成的,并且是異步的。

            J2ee是一種用來開發分布式系統的體系結構。它主要是用Java類開發業務實體。通過JSP來連接應用服務器

            本文開發一個組織機構管理小系統,通過這個實例來介紹如何用Ajax開發WEB應用程序。本系統具有增加、修改、刪除組織機構的功能。同時給機構分配人員,能增加、修改、刪除人員。

            二、 界面設計

            樹結構是大多軟件系統中常采用的結構形式。由于樹型結構層次分明、上下級關系清楚、且展開收縮表達信息方便、界面也較美觀,所以是大家熱衷于用此結構。組織機構管理是一般軟件基本具有的。組織機構是指公司的組織結構。集團公司可包括分公子公司,公司下面又有科室。員工歸屬于所在的公司。系統運行后的界面如下:

          利用AJAX+J2EE開發組織機構管理系統(圖一)

            orgManager.htm是組織機構管理的主頁面。WEB應用程序界面設計是非常重要的。如何布局、么樣組織可直接體現一個人的設計水平。
          組織機構主要包括樹結構、組織機構編輯、人員編輯等三大塊,如何分成三塊呢,然而一般樹型結構的窗體常先二塊,樹型結構獨占一塊,另一塊又分成上下二部分,上面是機構編碼,下面是人員編碼。固可以把頁面劃分成如下圖形式:

          樹結構區1
          組織編碼區2
          人員管理區3

            顯然我們是通過表來實現。這是一個二行二列的表,且第一、二行的左邊列合并單元格。代碼如下:

          < TABLE border="1" width="100%" height="100%">
          <TR>
          <TD rowspan="2"></TD>
          <TD></TD>
          </TR>
          <TR>
          <TD></TD>
          </TR>
          </TABLE>

            我們在1區(單元格)上加上一個DIV,因為DIV可以動態地滾動,并且可以插入其它控件。DIV的id為"divTree",且風格設置為溢出時自動滾動,寬與高都為100%,及滿區域。代碼如下:

          <div id="divTree" style="width:100%; height:100%;background-color:#f5f5f5;border :1px solid Silver;overflow:auto;">
          </div>

            我們在2區(單元格)上也加上一個DIV,在DIV里再插入一個表格。表格上放下控件,這很簡單,就不詳細說了。

            我們在3區(單元格)上加上一個DIV。此DIV的id為" divContent ",且風格設置為豎直溢出時自動滾動,寬與高都為100%,及滿區域,此DIV用來裝載人員信息;在DIV里再插入一個表格, 此table的id為" tbList ",是用來輸入、顯示人員作息,同時在此表中插入一些如checkbox 、text、select等控件。說明,表的第二列是用來放人員唯一編號的,不顯示。代碼如下:

          <div id="divContent" style="height:100%; overflow-y:auto;" width="100%">
          <table id="tbList" border="1" width="100%">
          <tr seqNo="1"><td>
          <table border="1" width="100%">
          <tr>
          <td width="5%"><input type="checkbox" value="on"></input> </td>
          <td width="0%" style="display:none"> <input type="text" size="20"></input></td>
          <td width="40%"><input type="text" size="20"></input></td>
          <td width="25%">
          <select size="1" name="D1">
          <option value="0">男</option>
          <option selected="true" value="1">女</option>
          </select>
          </td>
          </tr>
          </table>
          </td></tr>
          </table>
          </div>

          posted @ 2006-12-05 21:28 鴻雁| 編輯 收藏

          一個Jbpm員工請假流程的實例(轉載)

               摘要: 一個 Jbpm 員工請假流程的實例 ? ? 作者:吳大愚 ...  閱讀全文

          posted @ 2006-12-04 17:16 鴻雁| 編輯 收藏

          僅列出標題
          共18頁: First 上一頁 10 11 12 13 14 15 16 17 18 下一頁 
          主站蜘蛛池模板: 襄樊市| 扶余县| 祁东县| 申扎县| 南澳县| 张家口市| 姚安县| 海淀区| 封丘县| 凌云县| 普兰县| 玉门市| 固原市| 临安市| 临城县| 佳木斯市| 南开区| 六枝特区| 微博| 志丹县| 雅安市| 崇左市| 集贤县| 宿州市| 通州区| 沁阳市| 高唐县| 财经| 钦州市| 香港 | 礼泉县| 绩溪县| 东海县| 通州区| 宝山区| 桂林市| 怀仁县| 唐河县| 新宁县| 随州市| 蒙山县|