海鷗航際

          JAVA站
          posts - 11, comments - 53, trackbacks - 1, articles - 102

          導航

          <2025年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          常用鏈接

          留言簿(4)

          隨筆檔案

          文章分類

          文章檔案

          相冊

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          struts-menu+ibatis+少量的代碼=通用的自定義菜單和動態加載的樹

          前言:
          知識準備:首先你需要懂一些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"
          "<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表通過CATEGORYID與forum關聯,forum通過FORUMID與thread關聯,thread是一張特殊的表。它將不顯示在樹中,只是一個過渡關聯,用于讀出新建的文章。

          thread通過FORUMID、FORUMID、ROOTMESSAGEID與message表關聯(FORUMID、FORUMID、MESSAGEID)。而message表是一個自關聯的表。MESSAGEID與PARENTMESSAGEID關聯建立父子關系。

          然后我們建立一個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/,源碼下載

           

          主站蜘蛛池模板: 呈贡县| 静海县| 军事| 华安县| 瓦房店市| 治多县| 汽车| 临高县| 滕州市| 阿鲁科尔沁旗| 佛坪县| 公安县| 泾源县| 彰化县| 仪征市| 广州市| 县级市| 寻甸| 巍山| 河东区| 黄龙县| 简阳市| 遂平县| 南召县| 响水县| 塔城市| 深圳市| 阿鲁科尔沁旗| 青河县| 奉贤区| 凌海市| 会宁县| 东兰县| 溧水县| 乡城县| 阿瓦提县| 娄底市| 通渭县| 长子县| 苍梧县| 大厂|