实习总结四:周报,解决导出功能效率过慢问题

哎,很惭愧,一个多月没写博客了。一直在等这个项目上线后更新博客,今天终于上线了(当然我希望需求不会再更改)。而且最近学习salseforce,学习路漫漫!加油!!

周报

这个周报的项目其实是给公司内部使用的。使用Mysql和Mongo两个数据库。

  • Mysql是用来存储人员信息。
  • Mongo是用保存周报的信息、以及一些相关的人员配置,周报配置。

增删改查就不说了,没意思。说说下面几个

  1. 设置管理员:用户不必每次新建周报都要选择分享人以及领导人。
  2. 导出周报:说起这个我就来气。就这个简单的东西非要搞得很奇怪。不导出周报信息,要导出人员信息= =,一会再具体说吧。
  3. 设置管理员:管理员有权限进行查询导出。

controller

/**
 * 周报导出(以及导出前分页查询)
 * 
 * @param queryBean
 *            封装分页查询
 * @param operation
 *            判断是查询还是导出.值为'export'时是导出;为空或者任意字符串时,是分页查询。
 * @return
 */
@RequestMapping(value = "/apply-export", method = RequestMethod.GET)
@ApiOperation(value = "周报导出及分页查询", notes = "分页查询时'operation'可以为空,或为任意字符串;当需要导出时,'operation'必须为'export'", responseReference = "Page", produces = "application/json")
public Rest getReports(QueryBean queryBean , @RequestParam String operation) {
    QContext context = getContext();
    // 判断是否有导出权限
    if(!workReportService.isAdmin(context)) {
        return Rest.info(Sys.StateCode.STATUS_CODE_FORBIDDEN, "对不起,没有导出权限");
    }
    WebParamInfo webParamInfo = getWebParamInfo(queryBean);
    if(!StrUtils.isEmpty(operation) && "export".equals(operation)) {
        HttpServletResponse response = getResponse();
        HttpServletRequest request = getRequest();
        workReportService.exportReport(context, webParamInfo, response, request);
        return null;
    }else {
        Page list = workReportService.findExportReportBypage(context, webParamInfo);
        return Rest.page(Sys.StateCode.STATUS_CODE_SUCCESS, list);
    }
}

其实整个项目没有什么难度,之所以做的慢,是因为数据错误(这个我不是负责,只是简单的看了看),以及导出速度太慢。一直在优化,影响最大的地方就是查询导出,因为要导出的是人员基本信息和职位信息,这是两个表,我需要去查当前周报创建人信息。在客户端查的时候,才几十毫秒,很快。但是一旦通过代码进行实现,就变成分钟级别,很恐怖。而且人真不多,9000而已。
我先说说几次优化吧。
最下面的代码是最终的解决方案。用了最笨的办法但是也是最有效的,时间换空间。将所有人员信息在创建周报的时候就存放在一个javabean中,导出的时候直接取周报就行。这样确实快了很多。用脚本测的时候查询周报的时候略慢,但是完全能接受了。

第一次:也就是刚写出来的时候。想的很简单,因为是多个周报么,每个周报都需要设置数据,如果每次都查询的话,那1000个周报,1000个人创建人,要我查1000次?太恐怖了,还不如一次性全部查出来。但是这个时候其中一个问题就出来了,几条记录怎么办?先试试查所有要多久吧,OK。Person person = personService.findall();这个查询速度真的太慢了。肯定是不行的。然后我试了试添加查询条件,查询几个人,还是很快的,稍微多一点就不行了。所以这个方法pass掉。
第二次:其实也是第一次的遗留问题,就算我导出1w条记,录,但是其实就是100个人创建的。就是一个创建人存在多条周报。OK,那我就设置一个集合,存放已经查询过得人的信息。每次查询之前先去找是否存在。

// creaters集合,存放创建人
Map creaters = new HashMap();
Person person =null;
String id = workReport.getCreateBy().getId();
// 判断当前周报创建人是否存在。如果不存在,再去查询,同时放进map集合中
if(creaters.get(id) == null) {
    person = personService.findByItcode(context, workReport.getCreateBy().getItcode());
    creaters.put(id, person);
}else {
    person = creaters.get(id);
}

其实这么写,还是老问题,创建人多了,查询就变慢了。哎。。。因为这个我还恶补了一下java代码优化。把整个导出部分的代码都优化了一遍。效果甚微啊。代码很长,但是都是需要的字段,逻辑部分其实很少。
代码既然优化到此结束,查询的慢,那就是数据的问题了。抛出该person表中一大堆无用字段不提。好多的约束条件我也是第一次看到。这么多的约束条件,对查询速度肯定会有影响,而且当表大了之后,影响会更大。约束,更多是为了保证数据的正确性,一定程度上保证了安全性。而正确性、安全性和性能是天平的两个方面,就像鱼和熊掌,不可兼得。

Impl-最终的解决方案

/**
  *代码中的exportPersonInformation就是放在周报中的字段,用来存放人员信息。
  */
@Override
public void exportReport(QContext context, WebParamInfo webParamInfo, HttpServletResponse response,
        HttpServletRequest request) {
    Criteria criteria = Criteria.where("org").is(context.getOrg()).and("status").is(SUBMIT);
    Map param = webParamInfo.getParam();
    Object filterObject = param.get("filterUsers");
    List userIds = getUserIds(filterObject);
    if (userIds != null && userIds.size() > 0) {
        criteria.and("createBy._id").in(userIds);
    }
    // 根据日期范围进行查询
    Criteria dateCrita = getDateCrita(param);
    criteria.andOperator(dateCrita);
    Query query = new Query();
    query.addCriteria(criteria);
    querySetSort(query, webParamInfo);
    List workReports = findAll(context, query);
    if (workReports != null && workReports.size() > 0) {
        try {
            Map heads = new TreeMap();
            heads.put("A", "周报名称");
                    .
                    .
                    .
            heads.put("Y", exportPersonInformation.getName());
            List> datas = new ArrayList>();
            datas.add(heads);// 添加表头
            for (WorkReport workReport : workReports) {
                datas.add(setData(workReport));// 添加数据
            }
            String date = DateUtils.convertDateToString(new Date());
            String fileName = "周报导出" + date + ".xls";
            response.setHeader("Content-disposition",
                    "attachment; filename=" + new String(fileName.getBytes("GB2312"), "ISO-8859-1"));
            OutputStream outStream = response.getOutputStream();
            ExcelExport export = new ExcelExport();
            export.export(outStream, datas);// 导出excel
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

private Map setData(WorkReport workReport) {
    Map map = new HashMap();
    ExportPersonInformation exportPersonInformation  = workReport.getExportPersonInformation();
    // 设置数据
    map.put("A", DateUtils.getDateStr(workReport.getStartDate()) + "~" + DateUtils.getDateStr(workReport.getEndDate()) + "的周报");// 周报名称
            .
            .
            .
    map.put("Y", *****);
    return map;
}

你可能感兴趣的:(java,实习笔记)