EasyExcel合併行處理並優化

業務場景

由於業務需要導出如下圖中訂單數據和訂單項信息,而一個訂單對應多個訂單項,所以會涉及到自定義合併行

 


1.簡單處理
項目使用的EasyExcel,經查找發現Excel種有個AbstractMergeStrategy抽象類,可以用於合併單元格。
於是先簡單的寫一個工具類實現根據數據自定義合併單元行,基本思路是獲取當前單元格內容和上一行的單元格內容比對,如果相同則添加合併區域,如果不同則不處理,如果上一個單元格在合併區域中,則先移除合併區域再將當前單元格添加到合併區域中。

 

 

2.優化數據顯示
第一個版本上線後財務反饋數據有問題。如圖,求和數值實際應該是45,但是顯示爲90,導致財務不好對賬

z

經排查發現圖中C3、C5單元格雖然已經合併了,但是數據仍然存在,導致下拉選中的時候將他們的值也計算進去了。於是在1.0的基礎上調整,將合併單元格僅保留首行數據,其他行內容直接清空

 

 


3.優化合並策略
第二版上線後數據問題已經解決,但是財務反饋導出大量數據時太慢。本來打算調整爲異步導出解決此問題,但是經過測試發現10000條數據如果不合並直接生成excel只要幾秒,但是使用合併自定義合併策略就非常慢,需要一二十分鐘,這個時間差大的太離譜了,於是查看合併策略代碼有哪些地方可以優化的。
經過查看代碼可以發現在上述2.1步驟中一直查找合併單元格數據,然後一直刪除再新增。假設10000條數據,有3列需要自動合併,每三行合併,執行以上代碼會執行6666✖️3次新增和3333✖️3次刪除,大大的影響了效率。於是調整代碼,處理數據時只保存需要合併的單元格信息,導出完成再統一添加合併信息到sheet

 

 

 

經過測試,原本生成文件需要20分鐘左右,現優化到只需要20多秒了。

工具類

 


/**
* excel線程上下文.
*
*/
public class ExcelThreadContext {

private static final ThreadLocal<Map<String, Object>> THREAD_LOCAL = ThreadLocal.withInitial(HashMap::new);

public static void clear() {
THREAD_LOCAL.remove();
}

public static void setData(String key, Object value) {
Map<String, Object> map = get();
map.put(key, value);
}

public static Integer getInteger(String key) {
return getInteger(key, 1);
}

public static Integer getInteger(String key, Integer defaultValue) {
Map<String, Object> map = get();
return Convert.toInt(map.get(key), defaultValue);
}

public static <K, V> Map<K, V> getMap(String key) {
return getMap(key, new HashMap<>());
}

public static <K, V> Map<K, V> getMap(String key, Map<K, V> defaultValue) {
Map<String, Object> map = get();
try {
return (Map<K, V>) map.getOrDefault(key, defaultValue);
} catch (Exception e) {
return defaultValue;
}
}

public static <T> T getObject(String key) {
return getObject(key, null);
}

public static <T> T getObject(String key, T defaultValue) {
Map<String, Object> map = get();
try {
return (T) map.getOrDefault(key, defaultValue);
} catch (Exception e) {
return defaultValue;
}
}

private static void set(Map<String, Object> map) {
THREAD_LOCAL.set(map);
}

public static Map<String, Object> get() {
return THREAD_LOCAL.get();
}
}

 

因爲數據是存儲在線程中的,需要每次使用後清理線程數據

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章