• java树型结构的数据展现设计


    在做一个需求管理的页面时,需求的展现是不限层级树型结构,需求下还可以分拆任务,页面要展现的字段有20多个,而且需求采用通用表单设计,db采用大宽表存储,有一百多个字段。目前数据量不大,第一版采用普通的同步加载数据方式,页面加载速度慢,慢的原因主要是代码逻辑没有做到最优,不如存在重复查询的问题,组织树型结构的时候也存在冗余的查询。加上页面渲染出全部数据,使得整个页面打开速度很慢。

    针对以上问题,同事做了第二版优化,主要采用ajax加载页面,滚动条采用代码控制,拖动滚动条的位置计算要显示的数据,然后从数据库取出相应的数据。第二版主要存在问题是每次滚动鼠标都会发起请求加载数据,并且每次请求的数据都会覆盖已经加载的数据,在加载过程中使用遮罩禁止重复滚动加载数据。以上问题使得用户体验很差,所以我临时接手进行了第二次优化。

    思路:

    1.针对每个项目的需求和任务,在第一次访问的时候,将当前访问的项目的全部需求和任务树型结构组织好并缓存起来,缓存过期时间控制在2-4小时(依数据量和内存大小灵活设置),注意采用indexMap.put(i++, row);的形式,这样方便后期分屏加载。row的定义为Map<String, Object> row ;//Object可存需求和任务,通过key区分。

    2.获取查询条件,如果为空,则从数据库取最后一次保存的查询条件,否则保存最新查询条件。

    3.通过遍历indexMap,统计满足条件的需求和任务数量。

    4.返回框架页面,并发送ajax请求真实数据。

    5.对请求分页处理,分页大小控制在一次请求50条,太大页面渲染时间慢,太小又需较多次加载。遍历indexMap,取出满足分页条件和查询条件的数据,返回给客户端。

    6.页面滚动到末尾,自动发起ajax加载后面的数据。

    通过瀑布流的形式,解决了树型结构的分页问题,不用一次全部取出数据,使用缓存,提高加载速度。通过第三次优化。用户体验得到了较大提升。

     注意点:使用原生ajax返回50条数据拼接到原有数据末尾的时候,ie容易内存溢出。

     部分关键代码:

     /**

    判断最后一行是否进入可视区域

    obj:有滚动条的容器box

    lastRow:最后一行

    **/

    function see(obj, lastRow){

      if(obj && lastRow){

        var seeHeight = document.documentElement.clientHeight || document.body.clientHeight;//可视高度

        var winScroll = obj.scrollTop;//获取滚动条当前位置

        var lastSee = obj.scrollHeight;//最后一行

        return lastSww< (seeHeight + winScroll)?true:false;

      }else{

        return true;

      }

    }

    /**

      将ajax数据拼接到页面末尾

    **/

     function append(parent, text){

      if(typeof text ==='string'){

        var temp = document.createElement('div');

        temp.innerHTML = text;

        var frag = document.createDocumentFragment();

        while(temp.firstChild){

          frag.appendChild(temp.firstChild);

        }

        parent.appendChild(frag);

        delete temp;

        delete frag;

        CollectGarbage();

      }

    }

    /**

      组织treeIndex

    **/

    ... ...

    int i = 0;

    for(ProjectDemand mode: items){

      Map<String, Object> row = new HashMap<String, Object>();

      if(attachMap.get(mode.getId())!=null){

        mode.setArrachmetCount(attachMap.get(mode.getId()));

      }

      row.put.("demand", mode);

      indexMap.put(i++, row);

      if(ScrumTaskMap.get(mode.getId())!=null){

        List<Task> tasklist = scrumTaskMap.get(mode.getId());

        for(Task task : tasklist){

          Map<String, object> rowTask  = new HashMap<String, Object>();

          rowTask.put("task", task);

          indexMap.put(i++, rowTask);

        }

      }

    }

    /**

    统计需求和任务数量

    **/

    private String projectDemandStatistics(Map indexMap, Map<String,String> filter, Map<integer, FormBase> showDemandAll, Map<Integer, Task> showTaskAll ){

      int demandAllCount=0;

      int leafCount=0;

      int taskCount = 0;

      int allCount = indexMap.size();

      FormBase demand = null;

      FormBase tempDemand = null;

      Task task = null;

      Map<String, String>SignMap = SZSEUtils.getItemsSign(indexMap);

      String showDemandFlag = TypeUtils.nullToString(filter.get("showDemandFlag")); 

      Map<Integer, FormBase> iteredDemand = new HashMap();//已遍历需求

      

      for(int i=0; i<allCount;i++){

        Map<String, Object> row = (Map<String,  Object>) indexMap.get(i);

        if(row==null) break;

        if(row.get("demand")!=null){
          demand = (FormBase) row.get("demand");

          iteredDemand.put(demand.getId(), demand);

          if(isShowDemand(filter, demand)){

            if(showDemandAll!=null) showDemandAll.p;ut(demand.getId(), demand);//在这里记录要显示的需求map,供任务显示与否判断时用

            demandAllCount++;

            if(!"+".equals(signMap.get(demand.getId()+""))){

              leafCount++;

            }

            //拾遗父需求

            tempDemand = demand;

            while(showDemandAll !=null

              && SZSEUtils.isNotEmpty(tempDemand.getNum01())

              && !"0".equals(tempDemand.getNum01())

              &&showDemandAll.get(TypeUtils.nullToInt(tempDemand.getNum01()))==null){

              tempDemand = itereDemand.get(Typeutils.nullToInt(tempDemand.getNum01()));

              showDemandAll.put(tempDemand.getId(), tempDemand);

              demandAllCount++;

              if(!"+".equals(signMap.get(tempDemand.getId()+""))){

                leafCount++;

              }

            }

          }

        }else if("true".equals(showDemandFlag)){//显示任务

          task = (Task) row.get("taskId");

          if(isShowTask(filter, task)){

            int parentID = TypeUtils.nullToInt(task.getStr07());

            if(showDemandAll!=null && showDemandAll.get(parentID)!=null){//如果需求不显示,则任务也不显示

              showTaskAll.put(task.getId9), task);

              taskCount++;

            }

          }

        }

      }

      String countMessage = "共"+ demandAllCount +"个需求,共"+leafCount+"个叶子需求";

      if("true".equals(showDemandFlag)) countMessage +="; 共"+taskCount+"个任务";

      countMessage+=".";

      return countMessage;

    }

  • 相关阅读:
    Extjs系列篇(3)—-model数据模型
    js中parseInt()会导致的一些问题
    Extjs系列篇(2)—-初步了解
    一步一步学python(七)
    一步一步学python(六)
    一步一步学python(五) -条件 循环和其他语句
    一步一步学python(四)
    一步一步学python(三)
    MFC socket网络通讯核心代码
    MFC 遍历FTP服务器目录中文乱码问题
  • 原文地址:https://www.cnblogs.com/firstdream/p/5728339.html
Copyright © 2020-2023  润新知