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