Mybatis一級緩存的鍋


問題背景

項目開發中有一個樹形數據結構,不像經典組織結構樹、菜單級別樹,我們這個樹形結構是用戶后期手動建立起來的關系。因此數據庫表結構為兩張表:數據記錄表、記錄關系表,通過業務規則限制,形成的樹形結構像下面這樣:

特殊之處就是樹結構節點是有重復的

  • 不復制重復節點
    5NVrIU.png

  • 復制重復節點

5NVDaT.png

項目要求前端展示、導出時使用復制重復節點的方式。開搞吧

Mybatis樹結構查詢

樹結構查詢,在mysql下當然是使用Mybatis框架提供的遞歸查詢了。

  1. 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>
  1. mapper文件
public interface OKRAlignExportMapper {
    TreeNode getTree(Long objectiveId);
}
  1. 查詢結果

5NVyiF.png

樹結構導出到Excel

關於樹形結構數據導出,我參考這篇博客,並針對OKR的特點做了修改。

Java 樹形結構數據生成導出excel文件

OKR對齊視圖數據結構的特點是:

  • 1.以本人的目標為中心,向左右兩側發散。
  • 2.左側是自己對齊的目標,以及對齊目標再次對齊的目標,遞歸到頂。
  • 3.右側是向自己對齊的目標,遞歸到底。

關於OKR對齊視圖這種數據結構的導出,我們下篇博客會把完整的代碼放上來,並分析一下。這里說一下導出這種樹形結構數據的主要步驟:

  • 1.計算每條數據的行列坐標,這里采用遞歸的算法,最終可以計算出父級節點需要合並的行數,以及Excel文件的最大列數。
  • 2.根據行列坐標遞歸輸出每條數據的值到Excel單元格。

Mybatis一級緩存導致的問題

首先我們來了解一下Mybatis一級緩存:

Mybatis對緩存提供支持,但是在沒有配置的默認情況下,它只開啟一級緩存,一級緩存只是相對於同一個SqlSession而言。所以在參數和SQL完全一樣的情況下,我們使用同一個SqlSession對象調用一個Mapper方法,往往只執行一次SQL,因為使用SelSession第一次查詢后,MyBatis會將其放在緩存中,以后再查詢的時候,如果沒有聲明需要刷新,並且緩存沒有超時的情況下,SqlSession都會取出當前緩存的數據,而不會再次發送SQL到數據庫。

mybatis一級緩存二級緩存

由於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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM