Mybatis collection 遞歸查詢並自動裝填所有子節點(多參數查詢傳入參數方法)

需求:項目中想實現無限下拉子菜單功能,後臺就需要返回包括子節點的所有數據 

 

數據庫表結構如下:

上次是通過在java程序中遞歸不斷通過父級id查詢子節點實現的:

https://blog.csdn.net/lianzhang861/article/details/83783796

但這樣需要不斷連接和斷開數據庫,比較費時

mybatis可以在數據庫內部實現遞歸查詢被自動裝填,由於省去了數據庫連接步驟,訪問速度會更快,但會增加數據庫服務器壓力,使用時根據實際情況定使用程序遞歸還是數據庫遞歸

方式是使用 resultMap中的collection,此標籤可以一對多級聯


  <resultMap>
        <constructor> //適用於不存在沒有參數的構造方法
            <idArg></idArg>
            <arg></arg>
        </constructor>
        <id/>//這個對象的主鍵
        <association/>//一對一級聯
        <collection/>//一對多級聯
        <discriminator>//鑑別器
            <case/>
        </discriminator>
    </resultMap>

實現方法:

1.首先添加該表的實體類ArticleCategory

public class ArticleCategory {

    private String categoryId;

    private String categoryName;

    private String categoryParentId;
    //存放子節點
    private List<ArticleCategory> subList;

    public String getCategoryId() {
        return categoryId;
    }

    public void setCategoryId(String categoryId) {
        this.categoryId = categoryId;
    }

    public String getCategoryName() {
        return categoryName;
    }

    public void setCategoryName(String categoryName) {
        this.categoryName = categoryName;
    }

    public List<ArticleCategory> getSubList() {
        return subList;
    }

    public void setSubList(List<ArticleCategory> subList) {
        this.subList = subList;
    }

    public String getCategoryParentId() {
        return categoryParentId;
    }

    public void setCategoryParentId(String categoryParentId) {
        this.categoryParentId = categoryParentId;
    }
}

2.controller調用

@ResponseBody
@RequestMapping(value = "getArticleCategoryTree2", produces = "text/plain;charset=UTF-8")
public String getArticleCategory2(@RequestParam Map<String,String> params) {
    RetBase ret=new RetBase();
    params.put("categoryId","0");
    //List<Map<String,Object>> list=this.getSubCategory(params);
    List<ArticleCategory> list=articleService.getArticleCategory1(params);
    ret.setData(list);
    ret.setSuccess(true);
    return JSON.toJSONString(ret);
}

3.dao

public List<Map<String,Object>> getArticleCategory(Map<String,String> params);

 4.xml

<resultMap id="Category" type="com.zhuhuixin.common.entity.ArticleCategory">
    <id column="category_id" property="categoryId"/>
    <result column="category_name" property="categoryName"></result>
    <result column="category_parent_id" property="categoryParentId"></result>
    <collection column="category_id" property="subList"
                ofType="com.zhuhuixin.common.entity.ArticleCategory"
                select="getArticleCategory"></collection>
</resultMap>

<select id="getArticleCategory" resultMap="Category">
    select
    *
    from article_category t
    where  t.category_status='YES'
    and t.category_parent_id = #{categoryId}
    order by t.category_name
</select>

5.查出的數據格式

注意:

1.collection 的column與id的column相同,property爲實體類中子集合的名字,select與查詢方法名字相同

2.查詢時一定要將id和parentId都查出來,否則mybaits無法完成遞歸,我用*查就更沒問題了。實體類中也要有父id那個屬性

3.我從controller調用的時候已經傳入了一個父id參數,然後mybatis遞歸時又自動將父id傳給查詢。但這個參數的名字可以和resultMap中的id名字不相同,也就是說如果我controller裏面傳的父id名字爲 cc,select中改爲

and t.category_parent_id = #{cc}

mybatis照樣能查出子集合,所以這個參數名可以隨便寫

不過前臺js解讀數據的時候也得用遞歸方法解析數據,有點麻煩~~~

 

========== 華麗分割線 ==============

時間來到了2019.12.19

最近搞springboot項目中的菜單管理中又用到了這個,不過菜單由於有權限限制啥的,傳入的參數比較多,查詢就會出現問題,要不查不出來,要不就是子查詢參數不生效,因爲先前只用到一個父id參數,而且網上能找到的大部分例子都是一個參數例子,這次經過試驗總結到collection多參數傳入的方法

<resultMap>
        <id/>//這個對象的主鍵
        <collection column=" 
        {menuParent=MENU_ID,menuType=MENU_TYPE,menuStatus=MENU_STATUS,userId=USER_ID}" 
         property="subList"
                ofType="com.bomc.enterprise.entry.SysMenu"
                select="getMenuList">
    </collection>
    </resultMap>

首先collection是用來遞歸查詢一對多的,其次是參數傳入的問題,column是用來傳參數的,如果只有一個參數,直接寫一個用來關聯的字段名就行了,不管你傳的參數叫啥,它都會把上次查出的結果的這個字段自動放到下一次查詢 這個參數的位置,所以就會發現如果只有一個參數,你參數傳啥名字都沒事。

<resultMap id="Category" type="com.zhuhuixin.common.entity.ArticleCategory">
    <id column="category_id" property="categoryId"/>
    <result column="category_name" property="categoryName"></result>
    <result column="category_parent_id" property="categoryParentId"></result>
    <collection column="category_id" property="subList"
                ofType="com.zhuhuixin.common.entity.ArticleCategory"
                select="getArticleCategory"></collection>
</resultMap>

<select id="getArticleCategory" resultMap="Category">
    select
    *
    from article_category t
    where  t.category_status='YES'
    and t.category_parent_id = #{categoryId}
    order by t.category_name
</select>

但如果你的查詢有多個參數就不一樣了,需要用這種寫法了,前面是傳入參數名,等號 後面是字段名字

<collection column=" 
        {menuParent=MENU_ID,menuType=MENU_TYPE,menuStatus=MENU_STATUS,userId=USER_ID}" 

例子:

1.傳入的參數

params.put("menuParent", "0");
params.put("menuType", "menu");
params.put("userId", USER.getUserId());
List<SysMenu> menuList = menuService.getMenuList(params);

2.xml

<resultMap id="SysMenu" type="com.bomc.enterprise.entry.SysMenu" >
    <id column="MENU_ID" property="menuId"/>
    <!--<result column="MENU_ID" property="menuId" jdbcType="VARCHAR" />-->
    <result column="MENU_PARENT" property="menuParent" jdbcType="VARCHAR" />
    <result column="MENU_CODE" property="menuCode" jdbcType="VARCHAR" />
    <result column="MENU_NAME" property="menuName" jdbcType="VARCHAR" />
    <result column="MENU_TYPE" property="menuType" jdbcType="VARCHAR" />
    <result column="MENU_LEAF" property="menuLeaf" jdbcType="VARCHAR" />
    <result column="MENU_METHOD" property="menuMethod" jdbcType="VARCHAR" />
    <result column="MENU_ICON" property="menuIcon" jdbcType="VARCHAR" />
    <result column="MENU_URL" property="menuUrl" jdbcType="VARCHAR" />
    <result column="MENU_DESC" property="menuDesc" jdbcType="VARCHAR" />
    <result column="MENU_STATUS" property="menuStatus" jdbcType="VARCHAR" />
    <result column="MENU_SORT" property="menuSort" jdbcType="DECIMAL" />
    <result column="CREATE_TIME" property="createTime" jdbcType="DATE" />
    <result column="MODIFY_TIME" property="modifyTime" jdbcType="DATE" />
    <result column="AVAILABLE_START_TIME" property="availableStartTime" jdbcType="DATE" />
    <result column="AVAILABLE_END_TIME" property="availableEndTime" jdbcType="DATE" />

    <collection column="{menuParent=MENU_ID,menuType=MENU_TYPE,userId=USER_ID}" property="subList"
                ofType="com.bomc.enterprise.entry.SysMenu"
                select="getMenuList">
    </collection>
</resultMap>


<select id="getMenuList" resultMap="SysMenu">
    select
    t.*,case t.menu_type when 'menu' then '按鈕' when 'href' then '鏈接' else '菜單' end as menu_type1,
    case t.menu_status when 'YES' then '是' else '否' end as menu_status1,
    #{userId,jdbcType=VARCHAR} USER_ID
    from sys_menu t
    where
    1=1
    <if test="menuParent!=null and menuParent!=''">
        and t.menu_parent =#{menuParent,jdbcType=VARCHAR}
    </if>
    <choose>
        <when test="menuStatus!=null and menuStatus!=''">
            and t.menu_status=#{menuStatus,jdbcType=VARCHAR}
        </when>
        <otherwise>
            and t.menu_status='1'
        </otherwise>
    </choose>
    <if test="menuType!=null and menuType!=''">
        and t.menu_type = #{menuType,jdbcType=VARCHAR}

    </if>
    <if test="menuName!=null and menuName!=''">
        and t.menu_name like '%'||#{menuName,jdbcType=VARCHAR}||'%'

    </if>
    <if test="roleId!=null and roleId!=''">
        and t.menu_Id in (
          select t1.menu_Id from sys_role_menu t1
          where t1.role_id = #{roleId,jdbcType=VARCHAR}
        )
    </if>
    <if test="userId!=null and userId!=''">
        and t.menu_Id in (
            select t1.menu_Id from sys_role_menu t1
            where t1.role_id in(
              select t2.role_id from sys_user_role t2
              where t2.user_id = #{userId,jdbcType=VARCHAR}
            )
        )
    </if>
    order by t.menu_sort

</select>

上面例子我傳入了三個參數,想要正常查詢,就得在column中配置三個參數,雖然我的sql中還判斷其他的參數,但實際沒有用到的話就不必在column中配

每次查詢到父級後就會把查到結果中對應的字段值 匹配 配置的 參數名,一一對應的設置爲下一次查詢的參數值。

這也就意味着你的參數必須存在於上一次的查詢結果集中,但是對於菜單管理,需要傳入userId 去查詢菜單權限這種情況,菜單表中肯定沒有userId這個字段,這就需要你在查詢第一次的時候就手動加上這個字段:

按理說應該在實體類和resultMap映射也添加這個字段纔對,但我測試的是即使 實體類和resultMap映射中沒有這個字段也可以正常使用。

如果參數是常量似乎可以直接寫,但是不能直接把#{參數}放到column中:

column="{menuParent=MENU_ID,menuType=MENU_TYPE,userId='111'}" 

總之多參數傳入這麼可以解決,暫時還沒發現有啥正統的解決方法,其次關於是否分頁有問題還沒有測試,暫時先這樣

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