Mybatis一級快取的鍋
- 2021 年 10 月 18 日
- 筆記
- Mybatis一級快取, OKR, 樹結構數據導出Excel, 項目筆記
問題背景
項目開發中有一個樹形數據結構,不像經典組織結構樹、菜單級別樹,我們這個樹形結構是用戶後期手動建立起來的關係。因此資料庫表結構為兩張表:數據記錄表、記錄關係表,通過業務規則限制,形成的樹形結構像下面這樣:
特殊之處就是樹結構節點是有重複的
項目要求前端展示、導出時使用複製重複節點的方式。開搞吧
Mybatis樹結構查詢
樹結構查詢,在mysql下當然是使用Mybatis框架提供的遞歸查詢了。
- xml配置文件
<resultMap type="(...).OKRAlignTreeNode" id="TreeNodeResult">
<result property="id" column="objective_id" />
<result property="content" column="content" />
<result property="theOrder" column="the_order" />
<collection property="children" select="getChildren" column="objective_id" ofType="(...).OKRAlignTreeNode"/>
</resultMap>
<select id="getTree" parameterType="Map" resultMap="TreeNodeResult">
select
objective_id,content,the_order
from okr_objective oo
where oo.objective_id = #{id}
order by the_order
</select>
<select id="getChildren" resultMap="TreeNodeResult">
select objective_id,content,the_order
from
(select objective_id from okr_aline where parent_ids = #{objective_id} ) a
left join okr_objective oo on a.objective_id = oo.objective_id
order by b.the_order
</select>
- mapper文件
public interface OKRAlignExportMapper {
TreeNode getTree(Long objectiveId);
}
- 查詢結果
樹結構導出到Excel
關於樹形結構數據導出,我參考這篇部落格,並針對OKR的特點做了修改。
OKR對齊視圖數據結構的特點是:
- 1.以本人的目標為中心,向左右兩側發散。
- 2.左側是自己對齊的目標,以及對齊目標再次對齊的目標,遞歸到頂。
- 3.右側是向自己對齊的目標,遞歸到底。
關於OKR對齊視圖這種數據結構的導出,我們下篇部落格會把完整的程式碼放上來,並分析一下。這裡說一下導出這種樹形結構數據的主要步驟:
- 1.計算每條數據的行列坐標,這裡採用遞歸的演算法,最終可以計算出父級節點需要合併的行數,以及Excel文件的最大列數。
- 2.根據行列坐標遞歸輸出每條數據的值到Excel單元格。
Mybatis一級快取導致的問題
首先我們來了解一下Mybatis一級快取:
Mybatis對快取提供支援,但是在沒有配置的默認情況下,它只開啟一級快取,一級快取只是相對於同一個SqlSession而言。所以在參數和SQL完全一樣的情況下,我們使用同一個SqlSession對象調用一個Mapper方法,往往只執行一次SQL,因為使用SelSession第一次查詢後,MyBatis會將其放在快取中,以後再查詢的時候,如果沒有聲明需要刷新,並且快取沒有超時的情況下,SqlSession都會取出當前快取的數據,而不會再次發送SQL到資料庫。
由於Mybatis的快取機制,導致在出現重複的葉子節點時,雖然樹結構正常構建,但是指向的是同一個java對象。因為是使用的Mybatis的遞歸查詢,因此確認整個查詢在一個SqlSession中執行完成,肯定是一級快取導致的。這樣會造成的後果,就是無法設置重複葉子節點的正確位置,因為指向同一個java對象,後遍歷到的節點設置會覆蓋前面的節點設置。
解決方案
既然確定是一級快取導致的,那關閉或者清除一級快取就行了吧。因為是框架的遞歸查詢,因此無法
調用SqlSession的修改、添加、刪除、commit(),close等
清空一級快取。那怎麼辦呢,笨辦法了:
既然樹的結構關係時正確的,只是重複節點指向了同一個java對象,那就遍歷重建對象吧
/**
* 深度拷貝樹結構
* @param node
* @return
*/
private static OKRAlignTreeNode deepCopyTree(OKRAlignTreeNode node){
OKRAlignTreeNode newNode = node.clone();
List<OKRAlignTreeNode> children = node.getChildren();
if(children!=null&&children.size()>0){
List<OKRAlignTreeNode> newChildren = new LinkedList<>();
for (OKRAlignTreeNode child:children){
if(child!=null){
OKRAlignTreeNode newChild = deepCopyTree(child);
newChildren.add(newChild);
}
}
newNode.setChildren(newChildren);
}
return newNode;
}