和爪哇一起走過的日子

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            12 隨筆 :: 0 文章 :: 6 評論 :: 0 Trackbacks

          前言:
              知識準備:首先你需要懂一些struts的基本知識,會用struts-menu,并理解站長對struts-menu的分析那篇文章,還要知道ibatis的基本知識,如果不懂,請去google或者站長的論壇里找相關的文章。

          樹形結構在實際開發中很常用,但是樹形結構的開發往往也是難題,尤其是在顯示這一條上,很難做到通用。通常有兩種典型的樹型結構。一種是論壇的帖子,其結構往往通過父子ID號相連,數據在一張表里。一種是級別,比如論壇中的Category->Forum->Thread這種結構,數據放在不同的表里。因為論壇恰好包含了這兩種結構。因此。我們就能Jive的表結構來做這個例子。首先我們通過RsMetaDataTest來掃描數據庫,得到需要的XML配置文件。拿一個xml為例,解釋一下

          <?xml version="1.0" encoding="UTF-8"?>
          <!DOCTYPE sql-map
          PUBLIC "-//iBATIS.com//DTD SQL Map 1.0//EN"
          "http://www.ibatis.com/dtd/sql-map.dtd">
          <sql-map name="jivecategory">
          <!-- =============================================
          mapped-statement find
          ============================================= -->
          <dynamic-mapped-statement name="findjivecategoryDao" result-class="java.util.HashMap">
          select $listfield$ from JIVECATEGORY
          <dynamic prepend="where">
          <isPropertyAvailable prepend="and" property="CATEGORYID" >
          <isNotNull prepend="" property="CATEGORYID" >
          CATEGORYID=#CATEGORYID#
          </isNotNull>
          </isPropertyAvailable>
          <isPropertyAvailable prepend="and" property="NAME" >
          <isNotNull prepend="" property="NAME" >
          NAME=#NAME#
          </isNotNull>
          </isPropertyAvailable>
          <isPropertyAvailable prepend="and" property="DESCRIPTION" >
          <isNotNull prepend="" property="DESCRIPTION" >
          DESCRIPTION=#DESCRIPTION#
          </isNotNull>
          </isPropertyAvailable>
          <isPropertyAvailable prepend="and" property="CREATIONDATE" >
          <isNotNull prepend="" property="CREATIONDATE" >
          CREATIONDATE=#CREATIONDATE#
          </isNotNull>
          </isPropertyAvailable>
          <isPropertyAvailable prepend="and" property="MODIFIEDDATE" >
          <isNotNull prepend="" property="MODIFIEDDATE" >
          MODIFIEDDATE=#MODIFIEDDATE#
          </isNotNull>
          </isPropertyAvailable>
          <isPropertyAvailable prepend="and" property="LFT" >
          <isNotNull prepend="" property="LFT" >
          LFT=#LFT#
          </isNotNull>
          </isPropertyAvailable>
          <isPropertyAvailable prepend="and" property="RGT" >
          <isNotNull prepend="" property="RGT" >
          RGT=#RGT#
          </isNotNull>
          </isPropertyAvailable>
          </dynamic>
          </dynamic-mapped-statement>

          </sql-map>

          可見有一個名叫findjivecategoryDao,這是一個典型的動態查詢。返回對象是HashMap。其中$listfield$表示動態讀取的字段。可以這么說,通過這個查詢。有關這樣表的任何方式的查詢都已經解決了。由于這個演示只用到四張表,因些我們在sql-map-config-storedb.xml也只加載了四張表的定義。

          <sql-map resource="sqlmap/jivecategory.xml" />
          <sql-map resource="sqlmap/jiveforum.xml" />
          <sql-map resource="sqlmap/jivethread.xml" />
          <sql-map resource="sqlmap/jivemessage.xml" />

          然后定義MenuDefine類,這個類是一個通用的定義,其主要屬性如下。可以通過它建立一個四張表的樹形關系。

          //sql Map的名稱
          private String sqlMapName;
          //調用的查詢名稱
          private String SqlName;
          //子菜單的名稱
          private String submenuName;

          //對應字段,其中key為主表的字段,value是從表的字段。
          private HashMap keymap;
          //菜單的名稱
          private String MenuName;
          //標題
          private String Title;
          //標題字段
          private String TitleField;
          //需要讀取的字段
          private String listField;
          //是否需要顯示
          private boolean needShow=true;

          然后建立一個XML的文件(此處簡化了它的功能,就是把上面這個類序列化了一下)。把它放在classes目錄下。

            
          <?xml version="1.0" encoding="UTF-8"?> 
          <java version="1.4.2_03" class="java.beans.XMLDecoder">
          <object class="java.util.HashMap">
          <void method="put">
          <string>message</string>
          <object class="com.ewuxi.champion.MenuDefine">
          <void property="keymap">
          <object class="java.util.HashMap">
          <void method="put">
          <string>MESSAGEID</string>
          <string>PARENTMESSAGEID</string>
          </void>
          </object>
          </void>
          <void property="listField">
          <string>MESSAGEID,SUBJECT</string>
          </void>
          <void property="menuName">
          <string>message</string>
          </void>
          <void property="sqlMapName">
          <string>jivemessage</string>
          </void>
          <void property="sqlName">
          <string>findjivemessageDao</string>
          </void>
          <void property="submenuName">
          <string>message</string>
          </void>
          <void property="title">
          <string>文章</string>
          </void>
          <void property="titleField">
          <string>SUBJECT</string>
          </void>
          </object>
          </void>
          <void method="put">
          <string>category</string>
          <object class="com.ewuxi.champion.MenuDefine">
          <void property="keymap">
          <object class="java.util.HashMap">
          <void method="put">
          <string>CATEGORYID</string>
          <string>CATEGORYID</string>
          </void>
          </object>
          </void>
          <void property="listField">
          <string>CATEGORYID,NAME</string>
          </void>
          <void property="menuName">
          <string>category</string>
          </void>
          <void property="sqlMapName">
          <string>jivecategory</string>
          </void>
          <void property="sqlName">
          <string>findjivecategoryDao</string>
          </void>
          <void property="submenuName">
          <string>forum</string>
          </void>
          <void property="title">
          <string>大分類</string>
          </void>
          <void property="titleField">
          <string>NAME</string>
          </void>
          </object>
          </void>
          <void method="put">
          <string>forum</string>
          <object class="com.ewuxi.champion.MenuDefine">
          <void property="keymap">
          <object class="java.util.HashMap">
          <void method="put">
          <string>FORUMID</string>
          <string>FORUMID</string>
          </void>
          </object>
          </void>
          <void property="listField">
          <string>FORUMID,NAME</string>
          </void>
          <void property="menuName">
          <string>forum</string>
          </void>
          <void property="sqlMapName">
          <string>jiveforum</string>
          </void>
          <void property="sqlName">
          <string>findjiveforumDao</string>
          </void>
          <void property="submenuName">
          <string>thread</string>
          </void>
          <void property="title">
          <string>子分類</string>
          </void>
          <void property="titleField">
          <string>NAME</string>
          </void>
          </object>
          </void>
          <void method="put">
          <string>thread</string>
          <object class="com.ewuxi.champion.MenuDefine">
          <void property="keymap">
          <object class="java.util.HashMap">
          <void method="put">
          <string>THREADID</string>
          <string>THREADID</string>
          </void>
          <void method="put">
          <string>FORUMID</string>
          <string>FORUMID</string>
          </void>
          <void method="put">
          <string>ROOTMESSAGEID</string>
          <string>MESSAGEID</string>
          </void>
          </object>
          </void>
          <void property="listField">
          <string>THREADID,ROOTMESSAGEID</string>
          </void>
          <void property="menuName">
          <string>thread</string>
          </void>
          <void property="needShow">
          <boolean>false</boolean>
          </void>
          <void property="sqlMapName">
          <string>jivethread</string>
          </void>
          <void property="sqlName">
          <string>findjivethreadDao</string>
          </void>
          <void property="submenuName">
          <string>message</string>
          </void>
          <void property="title">
          <string>欄目</string>
          </void>
          <void property="titleField">
          <string>ROOTMESSAGEID</string>
          </void>
          </object>
          </void>
          </object>
          </java>

          關聯關系是category表通過CATEGORYIDforum關聯,forum通過FORUMIDthread關聯,thread是一張特殊的表。它將不顯示在樹中,只是一個過渡關聯,用于讀出新建的文章。

          thread通過FORUMIDFORUMIDROOTMESSAGEIDmessage表關聯(FORUMIDFORUMIDMESSAGEID)。而message表是一個自關聯的表。MESSAGEIDPARENTMESSAGEID關聯建立父子關系。

          然后我們建立一個session類作為主要類

          public class TreeDemoSession {
          	//通過名稱和參數來得到樹
          	public MenuComponent getMenu(String name, Map keys) throws Exception {
          		Map menuMap =
          			(Map) (new XmlUtils().read(Service.getPath() + "/menu.xml"));
          		MenuComponent menu = new MenuComponent();
          
          		if (menuMap.get(name) != null) {
          			MenuDefine rootMenudefine = (MenuDefine) menuMap.get(name);
          			menu.setTitle(rootMenudefine.getTitle());
          			menu.setName(rootMenudefine.getMenuName());
          
          			menu = submenuAdd(menu, keys, menuMap, name);
          		}
          		return menu;
          	}
          
          	/**一個典型的遞歸函數。用以組織樹。
          	 * @param menu
          	 * @param map
          	 * @param menuMap
          	 * @param menuName
          	 * @return
          	 * @throws DaoException
          	 * @throws Exception
          	 */
          	private MenuComponent submenuAdd(
          		MenuComponent menu,
          		Map map,
          		final Map menuMap,
          		String menuName)
          		throws DaoException, Exception {
          		try {
          			//得到菜單定義
          			MenuDefine menudefine = (MenuDefine) menuMap.get(menuName);
          			//listfield,表示需要讀取哪幾個字段
          			map.put("listfield", menudefine.getListField());
          			//查詢,返回列表。
          			List list = DaoCommon.findbyName(map, menudefine.getSqlName());
          
          			int namei = 0;
          			for (Iterator iter = list.iterator(); iter.hasNext();) {
          				Map element = (Map) iter.next();
                          //建立當前節點
          				MenuComponent submenu = new MenuComponent();
          				submenu.setName(menu.getName() + String.valueOf(namei++));
          				submenu.setTitle(
          					String.valueOf(element.get(menudefine.getTitleField())));
          					//如果不需要顯示,則使用父節點作為當前節點
          				if (!menudefine.isNeedShow())
          					submenu = menu;
          				//如果有子菜單,則遞歸調用。	
          				if (menudefine.getSubmenuName() != null) {
          
          					submenu =
          						submenuAdd(
          							submenu,
          							getSubMenuInfo(menudefine, element),
          							menuMap,
          							menudefine.getSubmenuName());
          				}
          				//將當前節點放到樹中。(如果不需要顯示就不用放)
          				if (menudefine.isNeedShow())
          					menu.addMenuComponent(submenu);
          			}
          			return menu;
          		} catch (DaoException e) {
          
          			throw e;
          		} catch (Exception e) {
          
          			throw e;
          		}
          	}
          
          	/**將父菜單的關鍵字段的值作為參數給子菜單
          	 * @param menudefine
          	 * @param element
          	 * @return
          	 */
          	private HashMap getSubMenuInfo(MenuDefine menudefine, Map element) {
          		HashMap map = new HashMap();
          		for (Iterator iter = menudefine.getKeymap().keySet().iterator();
          			iter.hasNext();
          			) {
          			String key = (String) iter.next();
          			map.put(menudefine.getKeymap().get(key), element.get(key));
          		}
          		return map;
          	}
          } 

          三個函數,非常簡單,主函數讀取配置文件的內容。一個遞歸函數用來建立樹形結構。這棵樹只有兩個屬性被設置。一個是名字和標題。其中標題采用從數據庫里讀出的字段。名字則采用流水號。讀取數據庫只有一句,其中map是參數的一個列表。后面是sql的名字。

          List list = DaoCommon.findbyName(map, menudefine.getSqlName());

          而真正的實現代碼也非常簡單

          public static List findbyName(Object vo,String name) throws DaoException {
          try {
          SqlMap sqlMap = DaoCommon.getSqlMap(DaoCommon.getDefautDao());
          return (List) sqlMap.executeQueryForList(name, vo);
          } catch (Exception e) {
          throw new DaoException(e);
          }
          }

          下面我們來做Action的工作

          public ActionForward execute(
          ActionMapping mapping,
          ActionForm form,
          HttpServletRequest request,
          HttpServletResponse response)
          throws Exception {

          Service.initSet();
          DaoCommon.startTransaction();

          HashMap parMap = new HashMap();
          Enumeration enumeration = request.getParameterNames();
          while (enumeration.hasMoreElements()) {
          String element = (String) enumeration.nextElement();
          parMap.put(element, request.getParameter(element));
          }
          TreeDemoSession session=new TreeDemoSession();
          request.setAttribute("com.ewuxi.champion.menu",session.getMenu(request.getParameter("menuName"),parMap));

          DaoCommon.rollBack();

          return mapping.findForward(request.getParameter("type"));
          }

          這個函數也非常簡單,就是把從request傳來的內容生成一個Map對象。然后調用session,將返回結果以com.ewuxi.champion.menu為名字保存到request中去。

          最后我們需要生成一個自定義的taglib。實際上很簡單。只是因為struts-menu自身的taglib是寫死了,我們不能利用,不過只要改一個地方就可以了,copy UseMenuDisplayerTag到我們的目錄下。

          MenuRepository repository =
          (MenuRepository) pageContext.getServletContext().getAttribute(MenuRepository.MENU_REPOSITORY_KEY);

          if (repository == null) {
          throw new JspException("Could not obtain the menu repository");
          }

          MenuComponent menu = repository.getMenu(this.name);

          找到上面這一段,改成


          MenuComponent menu =
          (MenuComponent) pageContext.findAttribute(this.name);

          就OK了。然后需要建立一個JSP文件。我們把xtree.jsp借用過來。唯一需要改的就是<cp:displayMenu name="com.ewuxi.champion.menu"/>,當然還有幾個link的路徑。因為此處用treeDemo來所以就是href="/treeDemo/styles/xtree.css"

          <head>
          <title>XTree (with Velocity) Example</title>

          <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

          <link rel="stylesheet" type="text/css" media="screen"
          href="/treeDemo/styles/global.css" />
          <link rel="stylesheet" type="text/css" media="screen"
          href="/treeDemo/styles/xtree.css" />

          <script type="text/javascript" src="/treeDemo/scripts/xtree.js"></script>

          </head>
          <body>

          <div class="container">
          Simple menu with Velocity:<br />
          <script type="text/javascript">
          <menu:useMenuDisplayer name="Velocity" config="/templates/xtree.html"
          bundle="org.apache.struts.action.MESSAGE">
          if (document.getElementById) {
          <cp:displayMenu name="com.ewuxi.champion.menu"/>
          } else {
          var msg = "Your browser does not support document.getElementById().\n";
          msg += "You must use a modern browser for this menu.";
          alert(msg);
          }

          </menu:useMenuDisplayer>
          </script>
          </div>

          下面就可以自由的看效果了。

          <p><a href="demo.do?type=demo&menuName=category" target="_blank">大分類列表</a>
          </p>
          <p><a href="demo.do?type=demo&menuName=forum" target="_blank">子分類列表</a> </p>
          <p><a href="demo.do?type=demo&menuName=forum&FORUMID=1" target="_blank">只看java分類</a> </p>
          <p><a href="demo.do?type=demo&menuName=thread" target="_blank">所有文章</a> </p>

          上面是幾種不同的參數。主要的差別是menuName不同。然后也可以加數據庫需要的參數,比如java分類的forumId=1。就在參數中加FORUMID=1,注意大小寫要跟XML中的動態參數相同,此處全是大寫。

          在線演示看這樣http://demo.ewuxi.com:8000/treejivedemo/,
          源碼下載
          http://champion.ewuxi.com/old/opensource/struts-new/treeDemo.rar

          posted on 2005-10-14 10:16 撲撲 閱讀(1361) 評論(0)  編輯  收藏 所屬分類: hibernate
          主站蜘蛛池模板: 酉阳| 上栗县| 芒康县| 西青区| 东源县| 无为县| 宣汉县| 葵青区| 繁昌县| 石屏县| 麦盖提县| 农安县| 巴林左旗| 曲周县| 井陉县| 玉溪市| 商城县| 封丘县| 泰州市| 曲周县| 浦县| 阳春市| 兰溪市| 汉阴县| 资源县| 滦南县| 津市市| 磐石市| 成都市| 阿巴嘎旗| 南通市| 宁河县| 东兴市| 克什克腾旗| 安图县| 许昌市| 仁化县| 昭平县| 象州县| 成武县| 平阳县|