摘要:基于Spring+Struts+Hibernate的技術(shù)架構(gòu)設(shè)計(jì)和開發(fā)了基于數(shù)據(jù)庫(kù)的樹狀菜單。解決了純用JavaScript代碼寫的樹狀菜單難以維護(hù)和效率低的問(wèn)題。研究出了一種繞過(guò)struts控制器,從jsp頁(yè)面直接不通過(guò)*.do的方式訪問(wèn)spring管理下的Struts的action實(shí)例的技術(shù)路線。首先分析了系統(tǒng)的實(shí)體。然后,設(shè)計(jì)和實(shí)現(xiàn)了系統(tǒng)的組件。進(jìn)而,設(shè)計(jì)和實(shí)現(xiàn)了視圖層的各jsp頁(yè)面,在jsp頁(yè)面中對(duì)樹狀菜單的關(guān)鍵技術(shù)進(jìn)行了介紹。這種方法設(shè)計(jì)的樹狀菜單正在實(shí)際應(yīng)用在北京大學(xué)播客資源平臺(tái)中。
關(guān)鍵詞:樹狀菜單;數(shù)據(jù)庫(kù);Spring;Struts;Hibernate
中圖分類號(hào):TP393文獻(xiàn)標(biāo)識(shí)碼:A文章編號(hào): 1009-3044(2009)25-7308-04
A Tree Menu's Design and Implement Based on Database in SSH Framework
YANG Gong-yi
(New Technology Research Development Office, Modern Education Technology Center, Peking University, Peking 100871, China)
Abstract: The paper presents the design and implement about a tree menu basing on database in the framework of Spring +Struts+Hibernate. The paper solves the problem of the JavaScript tree menu’s low efficiency and hard to maintain. The paper presents a kind of technology route to directly access struts’ action instance managed by spring container not from *.do. Firstly, the paper analyses the entities in the system. Secondly, the paper designs and implements the module in the system. Thirdly, the paper designs and implements the jsp pages in the system. In these jsp pages the paper introduces the key technology about the tree menu. The tree menu in this paper is using in podcast resource platform in Peking University.
Key words: tree menu; database; spring; struts; hibernate
樹狀菜單是一種很好的導(dǎo)航方式和內(nèi)容管理方式,目前網(wǎng)上的大部分樹狀菜單是采用JavaScript方式純靜態(tài)實(shí)現(xiàn)的,這種方式的缺點(diǎn)是增加一條父結(jié)點(diǎn)或子結(jié)點(diǎn)就需要專業(yè)人員手動(dòng)改寫JavaScript代碼,這種方法繁瑣、容易出錯(cuò)并且頁(yè)面執(zhí)行效率低。
基于Spring+Struts+Hibernate的技術(shù)架構(gòu)是目前主流的J2ee的web開發(fā)架構(gòu)。這種架構(gòu)的優(yōu)點(diǎn)是統(tǒng)一用基于hibernate的DAO組件來(lái)獲取和釋放數(shù)據(jù)庫(kù)的連接,并將DAO組件納入Spring的bean工廠中集中管理。SSH架構(gòu)下基于數(shù)據(jù)庫(kù)的樹狀菜單的實(shí)現(xiàn)就有一定的難度,因?yàn)檫@種架構(gòu)下不象普通的jsp技術(shù)那樣任意獲取和釋放數(shù)據(jù)庫(kù)的連接,訪問(wèn)業(yè)務(wù)邏輯層需要以*.do的形式通過(guò)struts控制器進(jìn)行轉(zhuǎn)發(fā)。
本文在SSH架構(gòu)下基于oracle數(shù)據(jù)庫(kù)設(shè)計(jì)與實(shí)現(xiàn)了樹狀菜單。并在北京大學(xué)播客資源平臺(tái)中進(jìn)行實(shí)際應(yīng)用(如圖1所示)。
1 系統(tǒng)功能定義
圖1中水平導(dǎo)航條,如“辦公自動(dòng)化”、“圖形圖像多媒體”等是系統(tǒng)的一級(jí)菜單。當(dāng)單擊任意一個(gè)一級(jí)菜單項(xiàng)時(shí),例如單擊“辦公自動(dòng)化”時(shí)會(huì)在系統(tǒng)左側(cè)出現(xiàn)“word2003”、“Outlook2003”、“Excel2003”等二級(jí)菜單樹狀結(jié)構(gòu)。當(dāng)單擊任一個(gè)二級(jí)菜單,如單擊“Excel2003”時(shí)出現(xiàn)三級(jí)菜單“第1章基礎(chǔ)操作”和“第2章實(shí)務(wù)應(yīng)用”。當(dāng)單擊三級(jí)菜單,如“第1章基礎(chǔ)操作”在系統(tǒng)中右側(cè)出現(xiàn)該菜單下的所有內(nèi)容。系統(tǒng)的一級(jí)菜單、二級(jí)菜單和三級(jí)菜單均是從數(shù)據(jù)庫(kù)中讀出的,系統(tǒng)的應(yīng)用環(huán)境北京大學(xué)播客資源平臺(tái)是筆者基于SSH架構(gòu)設(shè)計(jì)和開發(fā)的[1]。
2 系統(tǒng)需要的實(shí)體
系統(tǒng)的實(shí)體是系統(tǒng)中需要持久化保存的對(duì)象,也是系統(tǒng)實(shí)現(xiàn)業(yè)務(wù)邏輯的基礎(chǔ),通過(guò)hibernate技術(shù)這些實(shí)體將被直接映射為數(shù)據(jù)表。如圖2所示,系統(tǒng)中包含三個(gè)實(shí)體:一級(jí)菜單實(shí)體、二級(jí)菜單實(shí)體和三級(jí)菜單實(shí)體,當(dāng)用戶想在圖1的某個(gè)級(jí)別的菜單中想加一個(gè)菜單項(xiàng)時(shí),就相當(dāng)于在對(duì)應(yīng)的數(shù)據(jù)表中增加一條記錄。圖2也顯示了樹狀菜單的實(shí)體之間的關(guān)系,一個(gè)一級(jí)菜單項(xiàng)包含多個(gè)二級(jí)菜單項(xiàng),故一級(jí)菜單實(shí)體和二級(jí)菜單實(shí)體之間存在一對(duì)多的關(guān)聯(lián)關(guān)系。同理,二級(jí)菜單實(shí)體和三級(jí)菜單實(shí)體之間也存在一對(duì)多的關(guān)聯(lián)關(guān)系。
3 系統(tǒng)的組件的設(shè)計(jì)與實(shí)現(xiàn)
本系統(tǒng)采用POVO的設(shè)計(jì)思想,因?yàn)橄到y(tǒng)規(guī)模不大故沒有專門開發(fā)DAO組件。系統(tǒng)的組件如圖3所示,控制器層NavtreeAciton,它負(fù)責(zé)處理來(lái)自jsp頁(yè)面層的用戶請(qǐng)求,調(diào)用NavtreeService完成業(yè)務(wù)邏輯的操作。NavtreeService依賴PersistenceManager完成對(duì)數(shù)據(jù)庫(kù)的訪問(wèn)和其他業(yè)務(wù)邏輯操作,最后進(jìn)行PO和VO的拷貝,如果是保存數(shù)據(jù)就是完成VO向PO的拷貝,如果是查詢數(shù)據(jù)就是完成PO向VO的拷貝。圖3中的三個(gè)PO分別是圖2中的三個(gè)實(shí)體的Hibernate持久化對(duì)象。Navtree1映射一級(jí)菜單實(shí)體,Navtree2映射二級(jí)菜單實(shí)體,Navtree3映射三級(jí)菜單實(shí)體。圖3中的三個(gè) VO分別是與三個(gè)PO對(duì)應(yīng)的值對(duì)象,用于給NavtreeAction和Jsp層提供服務(wù)。
3.1 三個(gè)PO和VO及映射文件的實(shí)現(xiàn)。
因?yàn)閷?shí)現(xiàn)方法大同小異,下面以二級(jí)菜單的PO和VO為例進(jìn)行說(shuō)明。
/**二級(jí)菜單PO*/
public class Navtree2implements java.io.Serializable {
private String code;//二級(jí)菜單id。
//二級(jí)菜單名稱
private String navtree2Name;
//二級(jí)菜單描述
private String navtree2Desc;
//三級(jí)菜單集合。
private Set
//一級(jí)菜單
private Navtree1 navtree1;
省略各屬性的setter和getter方法,下同。}
/**二級(jí)菜單的VO*/
public class Navtree2VOimplements java.io.Serializable {
private String code;//二級(jí)菜單id。
//二級(jí)菜單名稱
private String navtree2Name;
//二級(jí)菜單描述
private String navtree2Desc;
//一級(jí)菜單主鍵
privateString navtree1_code;
//一級(jí)菜單名稱
privateString navtree1_name;}
每個(gè)PO對(duì)象要通過(guò)hibernate映射文件完成實(shí)體與關(guān)系的映射,二級(jí)菜單的PO的hibernate映射文件為Navtree2.hbm.xml,在映射文件中指明二級(jí)菜單實(shí)體和一級(jí)菜單實(shí)體的多對(duì)一關(guān)系,也指明二級(jí)菜單實(shí)體和三級(jí)菜單實(shí)體的一對(duì)多的關(guān)聯(lián)關(guān)系。它的關(guān)鍵代碼如下:
3.2 struts控制器類NavtreeAction的實(shí)現(xiàn)
控制器NavtreeAction接收來(lái)自jsp層的用戶請(qǐng)求,并調(diào)用業(yè)務(wù)邏輯組件NavtreeService的進(jìn)行業(yè)務(wù)邏輯處理。標(biāo)準(zhǔn)的struts控制器方法為返回一個(gè)ActionForward對(duì)象,例如下面代碼中g(shù)etListNavtree1和getListNavtree2兩個(gè)方法。本系統(tǒng)樹狀菜單的要求是從jsp頁(yè)面中能夠不通過(guò)*.do的形式直接訪問(wèn)NavtreeAction,故本類中提供了getListNavtree3方法,它的返回類型List而不是ActionForward。NavtreeAction的關(guān)鍵代碼如下:
public class NavtreeAction extends DispatchAction {
private NavtreeService navtreeService;
public void setNavtreeService(NavtreeService navtreeService){this.navtreeService = navtreeService;}
/*查詢:第一級(jí)類別,得到鏈表并放入session*/
public ActionForward getListNavtree1(省略struts標(biāo)準(zhǔn)參數(shù)) throws ActionException {
ActionForward af = 1;
List list=new ArrayList();
list=navtreeService.getListNavtree1();
HttpSession session = request.getSession();
session.setAttribute(\"Navtree1List\",list);
af = mapping.findForward (\"getListNavtree1Ok\");return af;}
/* 根據(jù)一級(jí)類別的編號(hào),查詢第二級(jí)類別,得到鏈表并放入session*/
public ActionForward getListNavtree2(省略struts標(biāo)準(zhǔn)參數(shù))throws ActionException {
ActionForward af = 1;
String navtree1_code=reques t.getParameter(\"navtree1_code\");
List list=new ArrayList();
list=navtreeService.getListNavtree2(navtree1_code);
HttpSession session = request.getSession();
session.setAttribute(\"Navtree2List\",list);af = mapping.findForward(\"getListNavtree2Ok\");return af;}
/**根據(jù)二級(jí)類別的編號(hào),查詢第三級(jí)類別,得到鏈表*/
public List getListNavtree3(String navtree2_code) throws ActionException {
List list=new ArrayList();
list=navtreeService.getListNavtree3(navtree2_code);return list;}
3.3 視圖層各jsp頁(yè)面的設(shè)計(jì)與實(shí)現(xiàn)
本文對(duì)視圖層的實(shí)現(xiàn)是采用jsp頁(yè)面實(shí)現(xiàn)的。jsp用來(lái)接受用戶請(qǐng)求,請(qǐng)?zhí)峤唤oNavtreeAction進(jìn)行處理,同時(shí)可以將NavtreeAction的處理結(jié)果VO對(duì)象輸出。系統(tǒng)中用到的主要jsp頁(yè)面包括,/index.jsp,/frame/common/index.jsp框架集,框架頁(yè)/frame/navtree/navtree.jsp和topFrame.jsp,下面分別進(jìn)行說(shuō)明。
3.3.1 /index.jsp及struts-config.xml
/index.jsp作用是請(qǐng)求NavtreeAction執(zhí)行g(shù)etListNavtree1方法,將得到的一級(jí)菜單鏈表放入session,跳轉(zhuǎn)到如圖1所示的主頁(yè)。關(guān)鍵代碼
當(dāng)NavtreeAction返回getListNavtree1Ok的ActionForward對(duì)象時(shí)通過(guò)標(biāo)準(zhǔn)的struts-config.xml進(jìn)行控制,轉(zhuǎn)發(fā)到/frame/common/index.jsp。struts-config.xml的關(guān)鍵代碼為:
3.3.2 /frame/common/index.jsp框架集
框架集頁(yè)面是如圖1所示的上左右結(jié)構(gòu)的框架頁(yè)。
左側(cè)的框架內(nèi)容為表示在該頁(yè)面加載時(shí)左側(cè)樹狀目錄默認(rèn)初始化為navtree1_code=1的一級(jí)菜單項(xiàng)即“辦公自動(dòng)化”下的二級(jí)菜單。
上側(cè)的框架內(nèi)容為
3.3.3 topFrame.jsp
topFrame.jsp的作用是從session中取出一級(jí)菜單的鏈表,并水平輸出。當(dāng)單擊任意一個(gè)“一級(jí)菜單項(xiàng)”時(shí)觸發(fā)js函數(shù),動(dòng)態(tài)改變圖1中左側(cè)框架頁(yè)內(nèi)容的src中的navtree1_ code的值,進(jìn)而改變左側(cè)樹狀菜單中二級(jí)菜單的內(nèi)容。topFrame.jsp的關(guān)鍵代碼為:
<%List list=new ArrayList();
list=(List)session.getAttribute(\"Navtree1List\");
for(int i=0;i Navtree1VO navtree1VO=(Navtree1V O)list.get(i); String Navtree1Name=navtree1VO. getNavtree1Name(); String navtree1_code=navtree1VO. getCode();%>')\"><%=Navtree1Name%>
<%}%>
3.3.4 /frame/navtree/navtree.jsp
navtree.jsp是本文的核心,完成在SSH架構(gòu)下讓js從Oracle數(shù)據(jù)庫(kù)中獲取所需數(shù)據(jù),并生成如圖4所示的漂亮的樹狀菜單,圖4顯示了當(dāng)用戶在圖1中上側(cè)框架頁(yè)中單擊某個(gè)一級(jí)菜單項(xiàng)如“辦公自動(dòng)化”時(shí),左側(cè)框架頁(yè)navtree.jsp中顯示二級(jí)菜單列表:“Word2003”和“Outlook2007”。圖4也顯示了單擊“Word2003”時(shí),顯示的三級(jí)菜單列表:“第1章基礎(chǔ)操作”和“第2章實(shí)務(wù)應(yīng)用”。通過(guò)js技術(shù),人性化地改變圖片的src,例如圖4中單擊Word2003時(shí),左側(cè)的圖片由折疊的書(images/jia.gif)改為展開的書(images/jian.gif)。再單擊一次時(shí),圖片的src做相反的改變。
navtree.jsp的關(guān)鍵代碼如下:
var lastM=\"0\";//上一次用戶單擊了哪個(gè)二級(jí)菜單項(xiàng),初始化為0表示沒有單擊任何項(xiàng)。
<%
//取得二級(jí)菜單的List和并用Iterator指向它============開始
List Navtree2List=(List)session.getAttribute(\"Navtree2List\");
String Navtree2_code=\"\";
String Navtree2_Name=\"\";
int m=1;//為每個(gè)二級(jí)菜單項(xiàng)加一個(gè)數(shù)字索引。
Iterator it=Navtree2List.iterator();
//取得二級(jí)菜單的List和并用Iterator指向它============結(jié)束
//遍歷二級(jí)菜單鏈表============開始。
while(it.hasNext()){
Navtree2VO navtree2VO=(Navtree2VO)it.next();
Navtree2_code= navtree2VO. getCode();//二級(jí)菜單項(xiàng)的主鍵
Navtree2_Name=navtree2VO.getNavtree2Name();//二級(jí)菜單項(xiàng)的名字。
%>
\">,tr<%=m%>,<%=m%>)\"><%=Navtree2_Name%>
<%
//為了在jsp中不通過(guò)*.do的形式直接訪問(wèn)NavtreeAction,要在jsp中獲得spring容器管理下的bean
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(this.getServletContext());
NavtreeAction navtreeAction=(NavtreeAction)wac.getBean(\"/NavtreeAction\");
//直接調(diào)用NavtreeAction的getListNavtree3方法得到該二級(jí)菜單項(xiàng)下對(duì)應(yīng)的三級(jí)菜單鏈表。
List navtree3List=navtreeAction. getListNavtree3(Navtree2_code);
//遍歷三級(jí)菜單的鏈表===========開始
Iterator it2=navtree3List.iterator();
while(it2.hasNext()){
Navtree3VO navtree3VO=(Navtree3 VO)it2.next();
//得到三級(jí)菜單項(xiàng)的主鍵
String Navtree3_code=navtree3VO. getCode();
//得到三級(jí)菜單項(xiàng)的名字。
String Navtree3_name=navtree3VO. getNavtree3Name();
//得到contextPath
String contextPath=request.get ContextPath();//本項(xiàng)目得到podcast
%>
/informationAction.do?dispatch=getListByTpageno=1pagesize=10subject=<%=Navtree3_code%>\" target=\"mainFrame\"><%=Navtree2_name%> <%}
//遍歷三級(jí)菜單鏈表==============結(jié)束
m++;//二級(jí)菜單項(xiàng)的數(shù)字索引值加1
}
//遍歷二級(jí)菜單的鏈表==========結(jié)束。
%>
//ShowTR(img1,tr1,1);//設(shè)置第1個(gè)結(jié)點(diǎn)為展開狀態(tài)
/*單擊某個(gè)二級(jí)菜單項(xiàng)時(shí)觸發(fā)的函數(shù):
針對(duì)id為objTr的二級(jí)菜單項(xiàng),改變它的圖片的src及alt并控制它的三級(jí)菜單是否顯示。
@param objImg二級(jí)菜單項(xiàng)的圖片id
@param objTr為objImg二級(jí)菜單項(xiàng)的所有三級(jí)菜單項(xiàng)。
@paramm二級(jí)菜單項(xiàng)的索引。*/
function ShowTR(objImg,objTr,m){
//針對(duì)id為objTr的二級(jí)菜單項(xiàng),改變它的圖片的src及alt并控制它的三級(jí)菜單是否顯示。
if(objTr.style.display == \"\"){
objTr.style.display = \"none\";
objImg.src = \"images/jia.gif\";
objImg.alt = \"展開\";
}else if(objTr.style.display == \"none\"){
objTr.style.display = \"\";
objImg.src = \"images/jian.gif\";
objImg.alt = \"折疊\";
}
//改變上一次用戶單擊的二級(jí)菜單項(xiàng),及相應(yīng)的三級(jí)菜單項(xiàng)。如果上一個(gè)狀態(tài)為打開的,就關(guān)閉它。
if(lastM!=\"0\" lastM!=m){
if(document.getElementById(\"tr\"+lastM).style.display == \"\"){document.getElementById(\"tr\"+lastM).style.display = \"none\";document.getElementById(\"img\"+lastM).src = \"images/jia.gif\";
} }lastM=m;//將當(dāng)前的二級(jí)菜單項(xiàng)的索引暫時(shí)fI7hyrhqIaiJ0fL6hUbWhiNWH5lLn67ygLDJ12oLbKk=存儲(chǔ)下來(lái)。
}
4 結(jié)論
本文實(shí)現(xiàn)的樹狀菜單是基于數(shù)據(jù)庫(kù)的,這樣每添加一個(gè)父結(jié)點(diǎn)或子結(jié)點(diǎn)時(shí),就只需要在相應(yīng)的數(shù)據(jù)表中增加一條記錄,這種方式比純用編寫的樹狀菜單更容易修改、不容易出錯(cuò)并且程序運(yùn)行效率高。因?yàn)榧冇肑avaScript寫的代碼全部在客戶端執(zhí)行,當(dāng)目錄結(jié)構(gòu)非常復(fù)雜時(shí),加載緩慢。
本文的樹狀菜單,是SSH架構(gòu)下在jsp頁(yè)面中從spring的Bean工廠中獲得NavtreeAction的實(shí)例,進(jìn)而調(diào)用該對(duì)象中的方法,既體現(xiàn)了SSH架構(gòu)的優(yōu)勢(shì),又解決了繞過(guò)struts控制器,通過(guò)非*.do的方式直接訪問(wèn)spring管理下NavtreeAction實(shí)例的技術(shù)問(wèn)題。
本文的樹狀菜單,在北京大學(xué)播客資源平臺(tái)(http://www.bk.pku.edu.cn/)中得到了應(yīng)用,很好地組織和管理了整個(gè)平臺(tái)中所有的資源內(nèi)容,與該平臺(tái)的第一版純用JavaScript寫的樹狀菜單相比運(yùn)行效率和可維護(hù)性顯著提高。
參考文獻(xiàn):
[1] 楊公義.基于SSH的播客資源平臺(tái)的設(shè)計(jì)與實(shí)現(xiàn)[J].現(xiàn)代遠(yuǎn)程教育研究,2009,(1):66-68.
[2] 楊公義.大學(xué)生創(chuàng)新能力培養(yǎng)的網(wǎng)絡(luò)平臺(tái)設(shè)計(jì)與開發(fā)[J].遠(yuǎn)程教育雜志,2008,(01):59-62.
[3] 楊公義,陳虎,陳飛.一個(gè)多功能可擴(kuò)展的html在線編輯器的技術(shù)架構(gòu)[J].地理與地理信息科學(xué),2009,(25):4-6.