1.首先了解一下需求
目标是写一个接口,活动码表查询接口。
先看一下表结构和表数据。
要执行的操作是层级结构查询,通过活动分类(activity_type)查询所有活动名称(activity_name),再通过所有活动名称(activity_name)查询所有活动一级名称(activity_level1_name)及(activity_level1_code)。
至此结束。
2.思路(先说一下我第一次的思路)
首先,查出所有的活动分类并存储在一个List<String>数组中。
然后通过循环遍历这个数组,查出所有活动名称并存储在一个List<String>数组中。
再通过遍历这个数组,查出所有活动一级名称并存储在一个List<String>数组中。
然后在每个循环内进行数据的组装。
比如:map.put("activityName", typeList.get(i));
map.put("child", firstNameList);
等等。。。。。。最后经过我不懈的努力,终于把数据格式组装好了,开心了半天把接口提上去了,本以为前端同学能够正常使用。。。结果。。。
3.问题出现
前端同学反馈,代码响应时间过长,无法使用,需要后端进行接口的优化。
当时使用PostMan测接口的时候确实发现了这个问题,但是当时对这个问题并不敏感,下次一定要注意。
我们再次使用PostMan进行测试,查看请求时间。
真不错,12.87秒,估计客户可以抓一把瓜子边磕边等了。。。。。。
为什么会慢?(小白,努力ing)
看一下代码。
@Override public List<Map> queryListNew() { List<String> list = jfMarketingActivityMapper.queryActivityTypeList(); List<Map<String, Object>> activityNameList = new ArrayList<>(); List title = new ArrayList(); Map<String, Object> mapType; Map<String, Object> mapName; Map<String, Object> nameCode; List codeList; for (int i = 0 ; i < list.size() ; i++) { List<String> typeList = jfMarketingActivityMapper.typeList(list.get(i)); for (int j = 0 ; j < typeList.size() ; j++) { List<String> firstNameList = jfMarketingActivityMapper.firstNameList(typeList.get(j)); mapName = new HashMap<>(); mapName.put("activityName", typeList.get(j)); codeList = new ArrayList(); for (int k = 0 ; k < firstNameList.size() ; k++) { String code = jfMarketingActivityMapper.firstName(firstNameList.get(k)); nameCode = new HashMap<>(); nameCode.put("activityLevel1Code", code); nameCode.put("activityName", code + "_" + firstNameList.get(k)); codeList.add(k, nameCode); } mapName.put("child", codeList); activityNameList.add(j, mapName); } mapType = new HashMap<>(); mapType.put("activityName", list.get(i)); mapType.put("child", activityNameList); activityNameList = new ArrayList<>(); title.add(i, mapType); } return title; }
可以看到,在三层循环中,我们不断的调用Mapper层进行sql的查询操作。每次查询都需要时间,三层循环之下导致时间过长。
所以我们要尽量减少sql的查询操作。
4.collection标签
之前做过的一个模块中,使用到了MyBatis的collection标签。当时是需要给前端返回一个树级的结构,一个层级关系。
于是我就想到应该可以直接在sql中把层级结构搂出来,这样就大大减少了接口的响应时间。
不太一样的地方就是这个层级是三级,可能需要嵌套使用collection标签。
ServiceImpl层:
List<String> typeList = jfMarketingActivityMapper.queryActivityTypeList();
List<Map> allData = jfMarketingActivityMapper.allList(typeList);
for (int i = 0 ; i < allData.size() ; i++) {
List list = (List) allData.get(i).get("child");
for (int j = 0 ; j < list.size() ; j++) {
Map<String, Object> map = (Map<String, Object>) list.get(j);
List list1 = (List) map.get("child");
for (int k = 0 ; k < list1.size() ; k++) {
Map<String, Object> map1 = (Map<String, Object>) list1.get(k);
map1.put("activityName", map1.get("activityLevel1Code") + "_" + map1.get("activityName"));
}
}
}
return allData;
}
Mapper层:
List<Map> allList(@Param("typeList") List<String> typeList);
MyBatis层:
<resultMap id="child" type="java.util.Map"> <result property="activityName" column="activity_name"/> <collection property="child" ofType="java.util.Map" javaType="ArrayList" resultMap="child2"> </collection> </resultMap> <resultMap id="child2" type="java.util.Map"> <result property="activityName" column="activity_level1_name"/> <result property="activityLevel1Code" column="activity_level1_code"/> </resultMap> <resultMap id="list" type="java.util.Map"> <result property="activityName" column="activity_type"/> <collection property="child" ofType="java.util.Map" javaType="ArrayList" resultMap="child"> </collection> </resultMap> <select id="allList" resultMap="list"> SELECT activity_type, activity_name, activity_level1_name, activity_level1_code FROM jf_marketing_activity WHERE 1=1 AND activity_type IN <foreach collection="typeList" item="list" index="index" open="(" separator="," close=")"> #{list} </foreach> </select>
代码实现,通过MyBatis层使用collection标签进行多层级嵌套查询,实现类中的循环是前端要求拼接code所以进行循环。
可以看到接口响应时间变成96ms,12.87s和96ms,一百三十多倍。。。。。。
5.总结
下次写代码的时候不光要只考虑能实现,还要考虑一些性能方面的问题。