摘要:基于Spring+Struts+Hibernate的技術架構設計和開發了基于數據庫的樹狀菜單。解決了純用JavaScript代碼寫的樹狀菜單難以維護和效率低的問題。研究出了一種繞過struts控制器,從jsp頁面直接不通過*.do的方式訪問spring管理下的Struts的action實例的技術路線。首先分析了系統的實體。然后,設計和實現了系統的組件。進而,設計和實現了視圖層的各jsp頁面,在jsp頁面中對樹狀菜單的關鍵技術進行了介紹。這種方法設計的樹狀菜單正在實際應用在北京大學播客資源平臺中。
關鍵詞:樹狀菜單;數據庫;Spring;Struts;Hibernate
中圖分類號:TP393文獻標識碼:A文章編號: 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
樹狀菜單是一種很好的導航方式和內容管理方式,目前網上的大部分樹狀菜單是采用JavaScript方式純靜態實現的,這種方式的缺點是增加一條父結點或子結點就需要專業人員手動改寫JavaScript代碼,這種方法繁瑣、容易出錯并且頁面執行效率低。
基于Spring+Struts+Hibernate的技術架構是目前主流的J2ee的web開發架構。這種架構的優點是統一用基于hibernate的DAO組件來獲取和釋放數據庫的連接,并將DAO組件納入Spring的bean工廠中集中管理。SSH架構下基于數據庫的樹狀菜單的實現就有一定的難度,因為這種架構下不象普通的jsp技術那樣任意獲取和釋放數據庫的連接,訪問業務邏輯層需要以*.do的形式通過struts控制器進行轉發。
本文在SSH架構下基于oracle數據庫設計與實現了樹狀菜單。并在北京大學播客資源平臺中進行實際應用(如圖1所示)。
1 系統功能定義
圖1中水平導航條,如“辦公自動化”、“圖形圖像多媒體”等是系統的一級菜單。當單擊任意一個一級菜單項時,例如單擊“辦公自動化”時會在系統左側出現“word2003”、“Outlook2003”、“Excel2003”等二級菜單樹狀結構。當單擊任一個二級菜單,如單擊“Excel2003”時出現三級菜單“第1章基礎操作”和“第2章實務應用”。當單擊三級菜單,如“第1章基礎操作”在系統中右側出現該菜單下的所有內容。系統的一級菜單、二級菜單和三級菜單均是從數據庫中讀出的,系統的應用環境北京大學播客資源平臺是筆者基于SSH架構設計和開發的[1]。
2 系統需要的實體
系統的實體是系統中需要持久化保存的對象,也是系統實現業務邏輯的基礎,通過hibernate技術這些實體將被直接映射為數據表。如圖2所示,系統中包含三個實體:一級菜單實體、二級菜單實體和三級菜單實體,當用戶想在圖1的某個級別的菜單中想加一個菜單項時,就相當于在對應的數據表中增加一條記錄。圖2也顯示了樹狀菜單的實體之間的關系,一個一級菜單項包含多個二級菜單項,故一級菜單實體和二級菜單實體之間存在一對多的關聯關系。同理,二級菜單實體和三級菜單實體之間也存在一對多的關聯關系。
3 系統的組件的設計與實現
本系統采用POVO的設計思想,因為系統規模不大故沒有專門開發DAO組件。系統的組件如圖3所示,控制器層NavtreeAciton,它負責處理來自jsp頁面層的用戶請求,調用NavtreeService完成業務邏輯的操作。NavtreeService依賴PersistenceManager完成對數據庫的訪問和其他業務邏輯操作,最后進行PO和VO的拷貝,如果是保存數據就是完成VO向PO的拷貝,如果是查詢數據就是完成PO向VO的拷貝。圖3中的三個PO分別是圖2中的三個實體的Hibernate持久化對象。Navtree1映射一級菜單實體,Navtree2映射二級菜單實體,Navtree3映射三級菜單實體。圖3中的三個 VO分別是與三個PO對應的值對象,用于給NavtreeAction和Jsp層提供服務。
3.1 三個PO和VO及映射文件的實現。
因為實現方法大同小異,下面以二級菜單的PO和VO為例進行說明。
/**二級菜單PO*/
public class Navtree2implements java.io.Serializable {
private String code;//二級菜單id。
//二級菜單名稱
private String navtree2Name;
//二級菜單描述
private String navtree2Desc;
//三級菜單集合。
private Set
//一級菜單
private Navtree1 navtree1;
省略各屬性的setter和getter方法,下同。}
/**二級菜單的VO*/
public class Navtree2VOimplements java.io.Serializable {
private String code;//二級菜單id。
//二級菜單名稱
private String navtree2Name;
//二級菜單描述
private String navtree2Desc;
//一級菜單主鍵
privateString navtree1_code;
//一級菜單名稱
privateString navtree1_name;}
每個PO對象要通過hibernate映射文件完成實體與關系的映射,二級菜單的PO的hibernate映射文件為Navtree2.hbm.xml,在映射文件中指明二級菜單實體和一級菜單實體的多對一關系,也指明二級菜單實體和三級菜單實體的一對多的關聯關系。它的關鍵代碼如下:
3.2 struts控制器類NavtreeAction的實現
控制器NavtreeAction接收來自jsp層的用戶請求,并調用業務邏輯組件NavtreeService的進行業務邏輯處理。標準的struts控制器方法為返回一個ActionForward對象,例如下面代碼中getListNavtree1和getListNavtree2兩個方法。本系統樹狀菜單的要求是從jsp頁面中能夠不通過*.do的形式直接訪問NavtreeAction,故本類中提供了getListNavtree3方法,它的返回類型List而不是ActionForward。NavtreeAction的關鍵代碼如下:
public class NavtreeAction extends DispatchAction {
private NavtreeService navtreeService;
public void setNavtreeService(NavtreeService navtreeService){this.navtreeService = navtreeService;}
/*查詢:第一級類別,得到鏈表并放入session*/
public ActionForward getListNavtree1(省略struts標準參數) 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;}
/* 根據一級類別的編號,查詢第二級類別,得到鏈表并放入session*/
public ActionForward getListNavtree2(省略struts標準參數)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;}
/**根據二級類別的編號,查詢第三級類別,得到鏈表*/
public List getListNavtree3(String navtree2_code) throws ActionException {
List list=new ArrayList();
list=navtreeService.getListNavtree3(navtree2_code);return list;}
3.3 視圖層各jsp頁面的設計與實現
本文對視圖層的實現是采用jsp頁面實現的。jsp用來接受用戶請求,請提交給NavtreeAction進行處理,同時可以將NavtreeAction的處理結果VO對象輸出。系統中用到的主要jsp頁面包括,/index.jsp,/frame/common/index.jsp框架集,框架頁/frame/navtree/navtree.jsp和topFrame.jsp,下面分別進行說明。
3.3.1 /index.jsp及struts-config.xml
/index.jsp作用是請求NavtreeAction執行getListNavtree1方法,將得到的一級菜單鏈表放入session,跳轉到如圖1所示的主頁。關鍵代碼
當NavtreeAction返回getListNavtree1Ok的ActionForward對象時通過標準的struts-config.xml進行控制,轉發到/frame/common/index.jsp。struts-config.xml的關鍵代碼為:
3.3.2 /frame/common/index.jsp框架集
框架集頁面是如圖1所示的上左右結構的框架頁。
左側的框架內容為表示在該頁面加載時左側樹狀目錄默認初始化為navtree1_code=1的一級菜單項即“辦公自動化”下的二級菜單。
上側的框架內容為
3.3.3 topFrame.jsp
topFrame.jsp的作用是從session中取出一級菜單的鏈表,并水平輸出。當單擊任意一個“一級菜單項”時觸發js函數,動態改變圖1中左側框架頁內容的src中的navtree1_ code的值,進而改變左側樹狀菜單中二級菜單的內容。topFrame.jsp的關鍵代碼為:
<%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架構下讓js從Oracle數據庫中獲取所需數據,并生成如圖4所示的漂亮的樹狀菜單,圖4顯示了當用戶在圖1中上側框架頁中單擊某個一級菜單項如“辦公自動化”時,左側框架頁navtree.jsp中顯示二級菜單列表:“Word2003”和“Outlook2007”。圖4也顯示了單擊“Word2003”時,顯示的三級菜單列表:“第1章基礎操作”和“第2章實務應用”。通過js技術,人性化地改變圖片的src,例如圖4中單擊Word2003時,左側的圖片由折疊的書(images/jia.gif)改為展開的書(images/jian.gif)。再單擊一次時,圖片的src做相反的改變。
navtree.jsp的關鍵代碼如下:
var lastM=\"0\";//上一次用戶單擊了哪個二級菜單項,初始化為0表示沒有單擊任何項。
<%
//取得二級菜單的List和并用Iterator指向它============開始
List Navtree2List=(List)session.getAttribute(\"Navtree2List\");
String Navtree2_code=\"\";
String Navtree2_Name=\"\";
int m=1;//為每個二級菜單項加一個數字索引。
Iterator it=Navtree2List.iterator();
//取得二級菜單的List和并用Iterator指向它============結束
//遍歷二級菜單鏈表============開始。
while(it.hasNext()){
Navtree2VO navtree2VO=(Navtree2VO)it.next();
Navtree2_code= navtree2VO. getCode();//二級菜單項的主鍵
Navtree2_Name=navtree2VO.getNavtree2Name();//二級菜單項的名字。
%>
\">,tr<%=m%>,<%=m%>)\"><%=Navtree2_Name%>
<%
//為了在jsp中不通過*.do的形式直接訪問NavtreeAction,要在jsp中獲得spring容器管理下的bean
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(this.getServletContext());
NavtreeAction navtreeAction=(NavtreeAction)wac.getBean(\"/NavtreeAction\");
//直接調用NavtreeAction的getListNavtree3方法得到該二級菜單項下對應的三級菜單鏈表。
List navtree3List=navtreeAction. getListNavtree3(Navtree2_code);
//遍歷三級菜單的鏈表===========開始
Iterator it2=navtree3List.iterator();
while(it2.hasNext()){
Navtree3VO navtree3VO=(Navtree3 VO)it2.next();
//得到三級菜單項的主鍵
String Navtree3_code=navtree3VO. getCode();
//得到三級菜單項的名字。
String Navtree3_name=navtree3VO. getNavtree3Name();
//得到contextPath
String contextPath=request.get ContextPath();//本項目得到podcast
%>
/informationAction.do?dispatch=getListByTpageno=1pagesize=10subject=<%=Navtree3_code%>\" target=\"mainFrame\"><%=Navtree2_name%> <%}
//遍歷三級菜單鏈表==============結束
m++;//二級菜單項的數字索引值加1
}
//遍歷二級菜單的鏈表==========結束。
%>
//ShowTR(img1,tr1,1);//設置第1個結點為展開狀態
/*單擊某個二級菜單項時觸發的函數:
針對id為objTr的二級菜單項,改變它的圖片的src及alt并控制它的三級菜單是否顯示。
@param objImg二級菜單項的圖片id
@param objTr為objImg二級菜單項的所有三級菜單項。
@paramm二級菜單項的索引。*/
function ShowTR(objImg,objTr,m){
//針對id為objTr的二級菜單項,改變它的圖片的src及alt并控制它的三級菜單是否顯示。
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 = \"折疊\";
}
//改變上一次用戶單擊的二級菜單項,及相應的三級菜單項。如果上一個狀態為打開的,就關閉它。
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;//將當前的二級菜單項的索引暫時fI7hyrhqIaiJ0fL6hUbWhiNWH5lLn67ygLDJ12oLbKk=存儲下來。
}
4 結論
本文實現的樹狀菜單是基于數據庫的,這樣每添加一個父結點或子結點時,就只需要在相應的數據表中增加一條記錄,這種方式比純用編寫的樹狀菜單更容易修改、不容易出錯并且程序運行效率高。因為純用JavaScript寫的代碼全部在客戶端執行,當目錄結構非常復雜時,加載緩慢。
本文的樹狀菜單,是SSH架構下在jsp頁面中從spring的Bean工廠中獲得NavtreeAction的實例,進而調用該對象中的方法,既體現了SSH架構的優勢,又解決了繞過struts控制器,通過非*.do的方式直接訪問spring管理下NavtreeAction實例的技術問題。
本文的樹狀菜單,在北京大學播客資源平臺(http://www.bk.pku.edu.cn/)中得到了應用,很好地組織和管理了整個平臺中所有的資源內容,與該平臺的第一版純用JavaScript寫的樹狀菜單相比運行效率和可維護性顯著提高。
參考文獻:
[1] 楊公義.基于SSH的播客資源平臺的設計與實現[J].現代遠程教育研究,2009,(1):66-68.
[2] 楊公義.大學生創新能力培養的網絡平臺設計與開發[J].遠程教育雜志,2008,(01):59-62.
[3] 楊公義,陳虎,陳飛.一個多功能可擴展的html在線編輯器的技術架構[J].地理與地理信息科學,2009,(25):4-6.