感謝偉大的
Java
技術、感謝天才之作《
Java Developers ALMANAC
》
,
它們成就了
PVO
技術的誕生。也許是我個人的偏見,到目前為止,真不知道還有什么技術會像
PVO
這樣給數據庫編程帶來如此之高的效率,使用起來卻是如此的簡單!
我曾經拜讀過
OMR
、
JDO
之類技術方面的文章,見到一大堆復雜的技術真讓人望而卻步;也聽別人介紹過
hibernate
技術,在網上也見到過不少人在談論
hibernate
技術,他們總是津津樂道,而給我的感覺總是一盆霧水。在探索和迷茫的過程中,不禁要問,
Java
本來就是一個高效、強大的象征,為什么會被
OMR
、
JDO
、
hibernate
之類弄得如此復雜?在簡化數據庫編程方面,難道就沒有更好的方法嗎?我與其把大量的精力花在鉆研
OMR
、
JDO
、
hiberlate
之類的技術上,還不如多花些精力鉆研
Java
技術本身,從中去尋求解決之道。于是一個實用的
HashMap
關系數據映射技術歷時半年,在本人開發“
皖南熱線網站
”之前終于誕生了,并在“皖南熱線網站”的開發過程中不斷得到完善、充實、檢驗。
PVO
,
Process Value Object
的縮寫,意為值對象加工,
ProcessVO
是本案的核心類,故將此技術簡稱為
PVO
技術
。
在簡化數據庫編程方面,這是一項重要的技術創新
。無論是與常規的
JDBC
數據庫編程技術相比,還是與現有流行的各種數據映射技術相比,此方案可能會在一個項目開發中,將數據庫的編程效率提高數倍、甚至是數十倍,如果一個表的字段有數十個,就會存在這樣的可能性。數據庫字段越多,
PVO
技術能帶來的編程效率就越明顯
。為什么
PVO
技術會有如此之高的編程效率?主要原因有以下幾點:
1、Java
容器技術、
Java
實用類技術本身就是高效、成熟的技術。
2、
不論是讀取數據,還是存入數據,均使用了
JDBC
元數據技術、反射技術來自動實現記錄與
HashMap
之間的映射、結果集與
Vector
之間的映射。
3、
免去了復雜的
XML
或
Properties
之類的配置。
4、SQL
標準語句參數化生成技術,會將所映射的
HashMap
對象,自動轉化成
SQL
標準語句的一部分,以供
JDBC API
調用;不再需要你去調用
rs.getString(fieldName)
、
pstmt.setString(i , objectValue);
之類的方法了。
5、
如果是進行
Web
應用開發,本方案提供的
BaseMapForm
重載了
Struts ActionForm
的
reset
方法,除了上傳文件,只用一個
Map record
屬性,來取代其它所有的屬性,以不變應萬變,再不需要你去定義一大堆屬性了。
6、
如果一個字段類型發生了變化,甚至是添加了幾個新字段,再不須要你去修改業務邏輯部分的代碼了。
PVO
技術方案符合
DAO
設計模式思想,有效地實現了數據庫訪問操作與業務邏輯的分離。核心類
ProcessVO
封裝了與數據庫相關的所有基本操作,如:插入、更新、刪除、查詢,并且實現了對查詢結果集進行分頁、篩選、排序、備份、獲取子集的多種算法。此案通用性強,在
EJB
、
Application
、
Servlet
、
Applet
等應用中均可不受限制地方便使用。
PVO
不但可以大大提高數據庫編程的效率,同時可以大大減輕數據庫服務器數據流量的負擔。
支持標準的
SQL
操作,如:插入、更新、刪除、查詢(包括多表查詢)。
運用
Java
反射技術來實現
HashMap
與關系型數據之間的映射。
運用
Java
反射技術,參數化生成
SQL
標準語句,供
JDBC API
調用。
針對每一條記錄,使用
HashMap
對象作為數據的載體,這個
HashMap
對象的鍵名是表中的字段名,或是字段的別名,鍵值為字段值,鍵值的類型是字段所對應的
JDBC API
的
Java
類。
針對數據庫的操作,如:插入、更新、刪除、查詢,該方法會將所傳遞的
Map
型參數轉化成
SQL
語句的組成部分,自動生成完整的標準的
SQL
語句,供
JDBC API
調用。
支持復雜的多表查詢,針對每一個查詢結果集,用一個
Vector
對象存貯,而這個
Vector
中的每一個元素均是一個
HashMap
對象,這個對象映射了一條記錄。
充分利用了成熟的豐富的
Java
容器技術,實現了對這個
Vector
查詢結果集進行分頁、篩選、排序、備份、獲取子集的多種算法。
基本技術包括:
JDBC
、
JDBC
元數據技術;
Map
、
Vector
等容器技術;
Java
反射技術;
通用
SQL
標準語句參數化生成技術及其算法;
HashMap
與關系數據庫記錄之間的映射技術及其算法。
1、
包名:核心包
cn.hkm.sql
、
web
應用包
cn.hkm.web
3、
類文件:
cn.hkm.sql
包括
:
ProcessVO
、
DbCon
、
Tool
、
WhereString
、
QueryString
cn.hkm.web
包括:
BaseMapForm
、
PvoPageTag
、
PvoSqlTag
、
PvoRecordTag
、
PvoImgsTag
、
PvoImgTag
、
PvoShowImgTag
、
PvoRecordMapTag
以及標志庫描述文件
pvotag.tld
????
1、
查詢,例:
ProcessVO pvo=new ProcessVO();
pvo.setCon(con);
//
也可用
ProcessVO pvo=new ProcessVO(con);
Vector v=pvo.getSomeRecord(
"
select a.* ,b.name from… where …
"
);
pvo.closeCon();
//
也可省略,
ProcessVO
類重載了
finalize()
,在其中會自動關閉數據庫聯接。
2、
插入,例:
ProcessVO pvo=new ProcessVO(con);
boolean b =insertObjectRecord(tableName, map);
//
也可使用
int i=pvo.insertARecord("insert into …");
pvo.closeCon();
3、
更新,例:
ProcessVO pvo=new ProcessVO(con);
boolean b=pvo.updateARecord("table",map,new WhereString("key_id","=",id));
pvo.closeCon();
4、
刪除,例:
ProcessVO pvo=new ProcessVO(con);
int i=pvo.delRecord(“delete from …”);
pvo.closeCon();
?
設數據庫表如:
create table tel(
tel_id bigint primary key,
clerk_id bigint,
type varchar(8),
code varchar(32)
)
create table clerk(
clerk_id bigint primary key,
name varchar(32),
birthday varchar(10),
descreption varchar(255)
)
本案可以與流行的
Struts
架構完美結合,使用時請將
pvotag.tld
復制到
WEB-INF
目錄中。為了更方便地使用,
cn.hkm.web.BaseMapForm
類繼承了
Struts
中的
org.apache.struts.action.ActionForm
,并且重載了
reset
方法,
增加了一個Map record屬性
,只要您的
ActionForm
繼承
BaseMapForm
,并按以下方式重載
reset
方法:
??? public void reset(ActionMapping actionMapping,HttpServletRequest servletRequest) {
??????? super.reset(actionMapping,servletRequest);
}
這樣,在理論上,除了上傳文件外,
只要有這一個Map record屬性,就足夠了。
1、
查詢,例:
ProcessVO pvo=new ProcessVO(con);
Vector v=pvo.getSomeRecord(
"
select a.* ,b.name from tel as a, clerk as b where a.clerk_id=b.clerk_id
"
);
session.setAttribute("clerk_tel_v",v);
//
在頁面中調用
Vector
結果集,以流行的
Struts
為例
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
??????? <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/pvotag.tld" prefix="pvo" %>
……..
?<table bgcolor="white" width="100%" cellpadding="0" cellspacing="1">
??????????? <logic:present name="clerk_tel_v">
??????????????? <tr align=’center’>
<td>
姓名
</td>
<td>
分類
</td>
<td>
號碼
</td>
</tr>
????????????? <logic:iterate id="record" name="clerk_tel_v">
??????????????? <tr>
????????????????? <td><bean:write name="record" property="name"/></td>
????????????????? <td><bean:write name="record" property="type"/></td>
????????????????? <td><bean:write name="record" property="code"/></td>
??????????????? </tr>
????????????? </logic:iterate>
??????????? </logic:present>
????????? </table>
?
如果表中有記錄,則會有類似以下的結果:
姓名
|
分類
|
號碼
|
張三
|
手機
|
13955668899
|
張三
|
電話
|
0553-6823888
|
張三
|
傳真
|
0553-6823888
|
?
2、
數據錄入(添加記錄與編輯記錄),例:
頁面中的表單,
書寫格式為
record.
字段名
的域,會自動成為
Map record
屬性中的元素,其鍵名就是字段名,其鍵值就是域值。
???
<p><h1 style="color:yellow">
人員信息錄入
</h1></p>
?
<html:form action="/menuAction.do?method=clerk" method="post">
<p>
????????????
姓名
???????????
???????
<html:hidden property="id"/>????????
??????????? <html:text property="record.name"/>
?????
</p>
<p>
出生日期
<html:text property="record.birthday"/></p>
<p>
簡介
<html:text property="record.descreption"/></p>
<p><html:submit>
提交
</html:submit></p>
</html:form>
?
/**MenuAction
中的
clerk
方法
*/
public ActionForward clerk(ActionMapping actionMapping,
???????????????????????????????? ActionForm actionForm,
?????????????????????????? ??????HttpServletRequest servletRequest,
???????????????????????????????? HttpServletResponse servletResponse) {
String id=servletRequest.getParameter("id");
??? ProcessVO pvo=new ProcessVO(con);
??? MapForm form = (MapForm) actionForm;
??? boolean b=false;??
??? Map map=form.getRecord();//
獲取表單中的記錄
??? if ((id == null)||id.equals("")){
//
新插入一條記錄
Long ID = pvo.insertKey("clerk", "clerk_id");???????
??????? b=pvo.updateARecord("clerk",map,new WhereString("clerk_id", "=", ID));
??????? pvo.closeCon();
???????if (b) {}
??? }else {
//
更新一條記錄
??????? pvo.setCon(con);
??????? b = pvo.updateARecord("clerk", map, new WhereString("clerk_id", "=", id));
??????? pvo.closeCon();
??????? if (b) {}
??? }
//
在頁面表單中,隱含域
<html:hidden property="id"/>
//
也可替換成
<html:hidden property="record.clerk_id"/>
,
//
但在更新數據時,請預先保存
clerk_id
值,如
String id=(Long)record.get(“clerk_id”).toString
,
//
然后調用
record.remove(“clerk_id”)
,
//
更新操作完畢后,調用
record.put(“clerk_id”,id)
,以便編輯記錄使用。
//
原因是:
updateARecord()
會自動將
map
中的所有元素作為
SQL
語句中的組
//
成部分,生成類似以下的語句:
//update clerk set clerk_id=?,name=?,birthday=?,descreption=?
//where (clerk_id = '123')
//
在
update
語句中,
set
主鍵名
where
主鍵名
同時出現不被數據庫支持
return actionMapping.findForward("clerk");
?}
?
/**clerk_edit
方法,從數據庫中提取一條指定的記錄,供表單編輯使用
*/
public ActionForward clerk_edit(ActionMapping actionMapping,
???????????????????????????? ActionForm actionForm,
???????????????????????????? HttpServletRequest servletRequest,
???????????????????????????? HttpServletResponse servletResponse) {
??? String id=servletRequest.getParameter("id");
??? ProcessVO pvo=new ProcessVO(con);
??? Map map=pvo.getARecord("clerk","clerk_id",id);???
??? MapForm form = (MapForm) actionForm;
??? form.setId(id);
??? form.setRecord(map);
//
如果用常規的方式,準備數據是一件非常辛苦的工作。而用
PVO
時,是如此簡單。
??? return actionMapping.findForward("clerk");
}
?
請注意無論是插入一條新記錄,還是更新一條記錄以及為表單準備數據,操作都十分簡潔。更值得一提的是,如果數據庫表中的字段類型發生變化,不須修改
clerk
方法,如果表中增加了一些新的字段,也不須修改
clerk
方法,只要在表單中添加相應的屬性。
假如:表中添加了新字段,
age int,
sex varchar(2),
在表單中添加以下兩行即可:
<p>
年齡
<html:text property="
record.age
"/></p>
<p>
性別
<html:text property="
record.sex
"/></p>
注意!使用格式是:
record.
字段名
。
?
《
HashMap
關系數據映射技術》源代碼將在今后適當的時候公布。
凡是有該標志的文章,都是該blog博主Caoer(草兒)原創,凡是索引、收藏
、轉載請注明來處和原文作者。非常感謝。