MyBatis使用collection標籤進行多層級巢狀查詢
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 = newArrayList<>(); 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.總結
下次寫程式碼的時候不光要只考慮能實現,還要考慮一些效能方面的問題。