一个节点超多的树的实现方式

    近日作项目,项目中有一个树,它的节点竟然有1700多个,为了能快速的显示它,或者说让用户感觉挺快的显示。我和同事们尝试了几种不同的方法。
    原来的树,是基于老外写的一个js做的,将节点信息,转为js数组,在通过页面内定义的js树节点对象一一加入。以前最动时,曾用过800个节点,勉强可以接受,本以为在这个项目中,也有可能使用,所以试了一下,结果,惨不忍睹,足足花了3-4秒,树才出来@^@#^@#^@^#。
   接着,采用同事的方法,使用了一个自己写的js代码,将树分散为几个个数组片断,分段加载,总的来说,速度还行。可是,分段过程需要人工干预...而且分段加载会出现某些节点后台加载偏慢地问题,另外,如果节点都被加载后,页面操作也有点慢。当需要实时更新,如树节点维护模块,这个方法就不是很可行。
  又尝试另一个同事的JSP TreeTag,发现也不算很快(也许是没正确配置的原因罢)。
  最后,实在没办法,只好自己编写了一段程序,我把它称为不完全展开树方式。
  一般使用树的模块,往往操作集中在几个地方。根节点、兄弟节点、子节点(也许还有父节点),那么从这个角度考虑的话,可以有选择性的显示这些节点,而其他节点不需要输出到Html中。
  因此,我制作了一个树的实现页面,以树的当前节点ID为参数,输出实际的树,从服务器端读取(当然结合AJAX技术,也可以用js展开),点击节点前面的图标,则当前节点就切换过去。
 根据这个办法,如果,只显示根节点、节点所在路径的所有节点,节点的子节点,节点的兄弟节点,一般查询的次数会比较少,而且大多是单节点查找。
 在目前的使用中情况较好.截图如下:
 树效果
另外配合,这个树,通过ajax技术,可以支持快速节点定位(通过ID)和一次返回所有子节点ID。
目前运行状况良好,我已经改写了所有相关的树,采用此模式处理。

附核心逻辑代码:(工具函数Common.jsp主要定义数据源和一些处理数据的基本方法)
<%@ page contentType="text/html; charset=gb2312"%>
<%@ include file="Common.jsp" %>
<%!
    public static String rootParent="0"; //根节点的查找方法
    public static int nodeType=Text_TYPE; //节点主键的类型

    //国标分类树
    public static String treeSQL="select GBID,GParentID,GBName from pclassandgb where 1=1 ";
    public static String nodeCountSQL="select Count(GBID) from pclassandgb where 1=1 ";
    public static String keyField="GBID";
    public static String nameField="GBName";
    public static String parentField="GParentID";

    public static String spaceStr="&nbsp;&nbsp;&nbsp;";

    //递归获得节点的完整路径
    String getFullPathEx(String nodeKey) throws Exception {
        String fullpath="";
        //获取当前的节点信息
        String nodeParent; //当前节点父ID
        javax.sql.RowSet nodeRs=null;       
        nodeRs = openRowSet(treeSQL+" and "+keyField+"="+toSQL(nodeKey,nodeType));
        if (nodeRs.next()){
            nodeParent=checkNull(nodeRs.getString(2));
            if (nodeParent.equals(rootParent)){
                fullpath=nodeKey;
            }else{
                fullpath=getFullPathEx(nodeParent)+"-"+nodeKey;
            }
        }else{
            fullpath="";
        }       
        return fullpath;
    }

     //子节点个数
     int getChildren(String nodeKey) throws Exception {
        int children=0;
if(bDebug) System.err.println(nodeCountSQL+" and "+ parentField + "=" + toSQL(nodeKey,nodeType));
        children=Integer.parseInt(checkNull(getString(nodeCountSQL+" and "+ parentField + "=" + toSQL(nodeKey,nodeType))));
        return children;
    }
   
    //节点深度
    int getNodeDepth(String fullPath) throws Exception {
        int nodeDepth=0;
        String[] nodeKey = ("-"+fullPath+"-").split("-");
        for (int j = 1; j <= nodeKey.length; j++) {
          if (nodeKey[j - 1].equalsIgnoreCase("")) {
          }
          else {
              nodeDepth++;
if(bDebug) System.err.println(nodeKey[j - 1]);
          }
        }
        return nodeDepth;   
    }

   //获得节点所在路径上的所有节点ID列表
    String[] getParentNodes(String fullPath) throws Exception {
//获得完整父节点序列
        int nodeNumber=getNodeDepth(fullPath);
//定义父节点数组
        String[] parentNodes=new String[nodeNumber];
        int nodeDepth=0;
        String[] nodeKey = ("-"+fullPath+"-").split("-");
//给父节点数组赋值
        for (int j = 1; j <= nodeKey.length; j++) {
      if (nodeKey[j - 1].equalsIgnoreCase("")) {
      }
      else {
              nodeDepth++;
              parentNodes[nodeDepth-1]=nodeKey[j - 1];
          }
        }
        return parentNodes;           
    }

  //输出节点信息到Html中
  String WriteNode(String nodeKey) throws Exception{
    //计算深度 ,转换为空格
    //查询名称出来,并输出
        String nodeMsg="";
        String nodeParent; //当前节点父ID
        String nodeName;
        int    nodeDepth=getNodeDepth(getFullPathEx(nodeKey));
        javax.sql.RowSet nodeRs=null;       
        nodeRs = openRowSet(treeSQL+" and "+keyField+"="+toSQL(nodeKey,nodeType));
        if (nodeRs.next()){
            String spaceMsg="";
            for (int i=0;i<nodeDepth-1;i++){
                spaceMsg=spaceMsg+spaceStr;
            }
            //计算,如果有子节点则 显示一种图片,否则另一种图片
            String imgMsg="";
            if (getChildren(nodeKey)>0){
                imgMsg="<img border=0 src=/"images/left/closed.gif/"></img>";
            }else{
                imgMsg="<img border=0 src=/"images/left/document.gif/"></img>";
            }
            nodeMsg=nodeMsg+
                "<Div class='NDS0'>"+
                "<Div class='NDS'>"+spaceMsg+
                "<a target='_self' href='SelectGBMakeTree.jsp?no="+
                checkNull(nodeRs.getString(1))+
                "'>"+
                imgMsg+
                "</a>"+
                "<span href='SelectGBMakeTree.jsp?no="+
                checkNull(nodeRs.getString(1))+
                "' onclick='javascript:eval(retChild(/""+checkNull(nodeRs.getString(1)) +"/"));' >"+
                checkNull(nodeRs.getString(3))+
                "</span></div></div>";
        }else{       
            nodeMsg="";
        }
        return     nodeMsg;
    }

    %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gbk">
<title>搜索条件录入</title>
<link type="text/css" rel="stylesheet" href="/query/csslib/cecp.css">
<script src="/query/jslib/urlencode.js"></script>
<script src="/query/jslib/xmlrpc.js"></script>
<script type="text/javascript">
    var done=true;
//注册服务
    XMLRPC.onerror = function(e){
        alert("执行远程调用时,发生错误:"+e.name+"/r/n详细信息:"+e.message);
        return true;
    }
    try{
        RPCHOST='<%=getHost(request)%>';
        AllFunc = XMLRPC.getService(RPCHOST+"rpc/rpc4pclassandgb.jsp");
    }catch(e){
        alert("连接远程服务提供方时错误,发生错误:"+e.name+"/r/n详细信息:"+e.message);   
    }

    AllFunc.add("AllFunc.retChild", "retChild");       

    function retChild(nodeKey){
        done=true;
        result="";
        result = AllFunc.retChild(escape(nodeKey));
        if (done) {
            return unescape(result);
        }else{               
            return "";
        }
    }

</script>
</head>
<body>
<div id="treeBox" class="TR"  >
<%
    String[] parentNodes=null; //父节点列表
    String currentNodeKey=getParam(request,"no"); //当前节点

    if    (currentNodeKey.equals("")){
    }else{       
        parentNodes=getParentNodes(getFullPathEx(currentNodeKey)); //获得父节点列表
    }
    //获得根节点信息
    javax.sql.RowSet nodeListRs=null;
    nodeListRs = openRowSet(treeSQL+" and "+ parentField + "=" + toSQL(rootParent,nodeType));
    boolean EOF=!nodeListRs.next();
    while (!EOF){
        //绘制所有根节点
        out.println(WriteNode(nodeListRs.getString(1))+"");
        if    (currentNodeKey.equals("")){
            //不绘制子节点了
        }else{
            //从根节点开始
            if(checkNull(nodeListRs.getString(1)).equals(parentNodes[0])){
                //绘制父节点序列,
                //System.err.println(parentNodes.length==1);
                for (int j=0 ;j<parentNodes.length;j++){
                    if (parentNodes.length==1){
                        //System.err.println("测试"+(parentNodes.length==1));                   
                        if (!(j==0)) out.println(WriteNode(parentNodes[j])+"");
                        //只绘制子节点序列
                        javax.sql.RowSet childListRs=null;
                        childListRs = openRowSet(treeSQL+" and "+ parentField + "=" + toSQL(currentNodeKey,nodeType));
                        boolean CEOF=!childListRs.next();
                        while (!CEOF){
                            out.println(WriteNode(childListRs.getString(1))+"");
                            CEOF=!childListRs.next();
                        }
                    }else{
                        //System.err.println(parentNodes.length==1);
                        //不是根节点时,才绘制兄弟节点
                        //当绘制到当前节点时
                        if(j==(parentNodes.length-1)){
                            javax.sql.RowSet brotherListRs=null;
                            brotherListRs = openRowSet(treeSQL+" and "+ parentField + "=" + toSQL(parentNodes[parentNodes.length-2],nodeType));
                            boolean BEOF=!brotherListRs.next();
                            while (!BEOF){
                                out.println(WriteNode(checkNull(brotherListRs.getString(1)))+"");
                                if ((brotherListRs.getString(1)).equals(currentNodeKey)){
                                    //绘制子节点序列
                                    javax.sql.RowSet childListRs=null;
                                    childListRs = openRowSet(treeSQL+" and "+ parentField + "=" + toSQL(currentNodeKey,nodeType));
                                    boolean CEOF=!childListRs.next();
                                    while (!CEOF){
                                        out.println(WriteNode(childListRs.getString(1))+"");
                                        CEOF=!childListRs.next();
                                    }
                                }
                                BEOF=!brotherListRs.next();
                            }
                        }else{
                            if (!(j==0)) out.println(WriteNode(parentNodes[j])+"");
                        }

                    }
                }           
            }else{
                //不在其他节点展开
            }
        }       
        EOF=!nodeListRs.next();
    }
%>
</div>
</body>
</html>

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值