龔永生 (
[email protected]) 2002 年 8 月 本文基於java環境,分析和實現了解決這個問題的方案。首先指出j2ee容器管理持久性實體bean的不足,接著講述了用java實現這個需求的技巧,最後是具體地實現。 1 前言 在軟件項目的分析設計過程中,我們首先分析數據實體,例如確定類,類成員變量或者畫ER圖。再詳細設計UI界面上有哪些輸入框,文本框等,緊接著我們還會確定方法的參數個數和類型。這些過程緊密地依賴於數據實體的穩定性,比如在數據庫設計中,我們需要多少表,每個表的字段有多少,它們的類型是什麼等。但是當這個穩定性失去了怎麼辦?用戶很有可能說目前我只能為我的表大概確定這些字段。項目組是否該等到用戶確定之後再做?如果用戶說字段的變化就是我的一個需求,項目該如何開發?即使所有客戶能確定字段,不同的客戶確定的字段可能不會是一樣的。由於不同的客戶對字段的需求不是一樣的,項目組有時不得不不厭其煩地構造源代碼的版本數。本文基於java環境,分析和實現了解決這個問題的方案。首先指出j2ee容器管理持久性實體bean的不足,接著講述了用java實現這個需求的技巧,最後是具體地實現。 2 固定字段假設和CMP實體BEAN類機制 CMP實體BEAN機制也就是容器管理持久性實體bean機制。CMP實體bean的提供者提供的bean類具有持久性字段(或屬性)的抽象get/set方法。這兩個方法與普通java bean的屬性的get/set方法一樣。下面是一個Personbean實體bean類的name持久性字段的申明。 Class Personbean extends EntityBean { Abstract String getName(); Abstract String setName(String vname); String ebjCreate(String name) { setName(name);}; ------ } 部署時,一般部署工具會產生這個類的子類,子類的申明大概如下: Class PersonbeanSubClass extends Personbean{ Private: String name; Public: String getName(){ return name;} String setName(String vname){name=vname;} ------ } 至於具體的字類實現機制可參見《Mastering Enterprise JavaBeans Second Edition》。容器創建的是子類的實例。通過父子類的比較可知,子類通過一個私有字段和繼承的兩個屬性get/set方法實現了一個實體bean的持久性屬性。部署工具是根據java bean的內省機制生成這個子類的。這樣bean提供者只需規定持久性字段的抽象訪問器函數,其他的持久性實現都有工具輔助完成。但我們必須注意到,為了指定一個持久性字段,提供者必須硬編碼兩個訪問方法。同樣我們注意到為了創建一個實體Bean,我們為ejbCreate方法提供了一個類型為String的參數。這樣的代碼無疑建立在這個實體bean只有一個持久性字段的前提之下。類似假設下的語句還有訪問數據庫時的Statement語句: Statement st = conn.createStatement(); St.execuate("insert into person (name) value('John')"); 廣泛使用類似假設的例子還有Struts的視圖-模型數據交換機制中ActionForm和HTMLTag定制標簽處理類的數據交互。我暫且稱這種假設為固定字段假設,基於這個假設的代碼實現機制為CMP實體BEAN類機制,目的在於重視J2EE中的這個特征。 3 不定字段假設和腳本語言類技術 固定字段假設和CMP實體BEAN類機制硬編碼持久性字段,把字段的名字,個數和類型(本文稱為持久性字段的三屬性)三個中至少一個固定下來了,使得更改持久性字段的工作必然影響源代碼,這就產生了一系列令人討厭的代碼樹。不定字段假設和腳本語言類技術就是要把持久性字段的三屬性和源代碼分開,最終達到客戶可以訂制持久性字段的目的。典型的實現技術有XML,動態編譯技術,元數據技術,字典集合技術等。這些技術有一個共同點就是不固定持久性字段,有一個持久性字段的數據容器和一部分分析代碼。分析代碼解釋數據容器中的持久性字段,最後執行數據庫操作。動態編譯技術是一個過渡技術,它可根據客戶配置,動態生成源代碼,接著及時編譯生成字節代碼,部署到應用中。 3.1 XML XML是一個非常好的數據交換格式,它具有很好的模式定義(DTD),DTD是XML文檔的元數據,定義了文檔中數據的格式和組成。XML文檔中同時包含了數據名稱(元素名或屬性名)和數據值(元素文本或屬性值)。另外JAVA中有很強的XML文檔分析和使用API,包括JAXP,JAXM等。JAXP集合了基於事件分析的簡單XML編程接口SAX和節點數的DOM分析技術。JAXM則是利用XML進行消息發送接收和消息處理的編程接口。XSLT能很容易地把XML文檔轉為其他格式的文檔如HTML,JAVA源代碼等。 3.2 動態編譯技術 利用XML表達用戶配置信息,XSLT把這些信息轉換成相應的JAVA源代碼,接著是用java.lang.Compiler類及時編譯產生字接代碼。當然你也可以生成其他的輔助類,sql語句等。詳細描述請參照http://www.javaworld.com/javaworld/jw-02-2002/jw-0201-xslt.Html 3.3 元數據技術 元數據技術把關於數據的描述放在數據字典中,使用者訪問數據字典可以得到關於數據的信息。數據字典可以放在XML文檔中,也可以在數據庫服務器上。在客戶配置了持久性字段後,開發者訪問數據字典可以得到客戶的當前持久性字段,並生成正確的代碼。 3.4 字典集合技術 java中的哈西表等字典類集合數據結構可以在方法調用之間傳遞變化的持久性字段。平常我們的方法調用是表中有多少字段,填充數據庫的函數一般要接收多少參數,這樣就把持久性字段硬編碼入了源代碼中,持久性字段變化必會造成源代碼的變動。字典集合技術使這樣的函數的接口是固定的。 4 一個簡單任務 為了應用上面的分析,具體體現如何實現與數據庫表字段松散耦合的J2EE應用,在這裡提出一個簡單的任務:做一個采集人員信息的應用程序。 我們粗略分析一下便可得到一個人員類,暫且命名為Person,但字段我們確定不了。采用WAF框架來設計。關於WAF框架可參見http://www-900.ibm.com/developerworks/cn/Java/l-J2EEArch/index.shtml 4.1 設計一、字典集合技術和元數據技術 下面設計圖(圖一)表示:客戶發出http請求,容器定位到person.JSP,這個網頁分成服務器部分和客戶端部分,服務器部分為在容器中運行的指令,這些指令會build在客戶浏覽器上顯示頁面的客戶端部分,客戶端部分聚集(包含)了一個html表單,表單有一個提交按鈕,客戶可以點擊此按鈕發出提交請求。根據WAF的框架流程,我們設計一個personHtmlAction的類來處理用戶的提交請求。下面是這個關鍵類的設計說明:字段: Connection conn 保存了從容器連接池中獲取的數據庫連接; Hashtable reqHashNameValue 保存了從用戶提交的表單中提取的名字-值對; Hashtable targetHashNameType 保存了從數據庫中獲得的關於數據表persion_table的元數據--字段名-類型對; Hashtable finalHashNameValue保存了最後插入到數據庫中的名字-值對;函數或方法: Connect getConnect() 從容器的連接池中獲取數據庫連結; Hashtable getReqHashNameValue() 從用戶提交的表單中提取名字-值對; Hashtable getTargetHashNameType() 從數據庫中獲得關於數據表persion_table的元數據--字段名-類型對; Hashtable getFinalHashNameValue() 根據targetHashNameType中的字段過濾掉reqHashNameValue中過多的字段,得到最後插入到數據庫中的名字-值對; Void insert()根據targetHashNameType中的類型和finalHashNameValue中的名字-值對構造sql語句,操作數據庫。這些函數統一由WAF框架中的這個類的父類HtmlAction的一個函數perform來調用。
圖一:字典集合技術和元數據技術設計
圖二表達了這個設計達到的松散耦合效果。第一處在表單和處理類之間,它們間的數據傳遞充分利用了哈西字典類。達到的直接好處是我們可以開發出定制表單的工具讓客戶自己定制應用的輸入界面,客戶可以增加各種輸入元素到表單上卻不會影響後台的處理類。第二處在處理類和數據庫表格之間,它們間的數據傳遞充分利用了數據庫中的元數據信息,達到的直接好處是我們可以開發出定制數據表的工具讓客戶自己定制數據表的多數字段,客戶可以增加減少或修改字段卻不會影響處理類。 4.2 設計二、xml技術、哈西技術和元數據技術 設計圖(圖三)於圖二不同的是我們在控制層內部加上了JMS技術,用XML作為數據的交換格式。 XMLpersonHTMLAction的類來處理用戶的提交請求,PersonMDB把數據插入到數據庫中去。下面是這兩個關鍵類的設計說明: XMLpersonHtmlAction類函數或方法: String getReqXML() 調用getParameters()獲得客戶的提交數據,產生xml文檔; Void sendXML() 生成一個臨時隊列作為消息的反饋隊列,利用JMS API把getReqXML()返回的xml文檔作為JMS的消息體發送出去。 PersonMDB類字段: Connect conn 保存了從容器連接池中獲取的數據庫連接; Hashtable XMLHashNameValue 保存了從處理類的發送來的消息中提取的名字-值對; Hashtable targetHashNameType 保存了從數據庫中獲得的關於數據表persion_table的元數據--字段名-類型對; Hashtable finalHashNameValue保存了最後插入到數據庫中的名字-值對; 函數或方法: Connect getConnect() 從容器的連接池中獲取數據庫連結; Hashtable getXMLHashNameValue() 從處理類的發送來的消息中提取名字-值對; Hashtable getTargetHashNameType() 從數據庫中獲得關於數據表persion_table的元數據--字段名-類型對; Hashtable getFinalHashNameValue() 根據targetHashNameType中的字段過濾掉XMLHashNameValue中過多的字段,得到最後插入到數據庫中的名字-值對; Void insert()根據targetHashNameType中的類型和finalHashNameValue中的名字-值對構造sql語句,操作數據庫; Void sendReply() 發送反饋消息給處理類。這些函數統一由EBJ 2.0 中的消息驅動BEAN的onMessage函數統一調用。 圖四表達了這個設計達到的松散耦合效果。與圖二相比,這個設計增加了一個松散耦合,從而增強了設計的分布特性。
5 總結 不能確定數據庫表的字段是一個普遍的需求不確定性問題,本文通過對J2EE技術的分析總結出兩個假設:固定字段假設和不定字段假設。有好多關鍵技術是基於固定字段假設的如CMP的實體BEAN技術,眾多框架的視圖-模型數據交換技術。這個假設和基於這個假設的技術往往造成項目的需求不確定風險,而且往往使項目組生成枝葉繁盛的源代碼版本數。不定字段假設把這種不確定性作為一個需求來處理,利用了XML技術、集合技術、元數據技術甚至動態編譯技術解決這個問題,達到數據庫字段和應用松散耦合的最終目的。本文還給出了兩個設計方案供參考。 關於作者: 龔永生,您可以通過email:
[email protected]與他取得聯系。