巧用 MyBatis 構建樹形結構

  • 2019 年 12 月 29 日
  • 筆記

在項目中我們經常會碰到這種格式的數據, 需要將其轉化為樹形結構:

menu_id

parent_id

menu_name

url

1

0

權限管理

#

2

1

用戶管理

/user/index

3

1

角色管理

/role/index

4

1

菜單權限

/menu/index

11

0

系統監控

#

12

11

登錄日誌

/log/login/index

19

11

操作日誌

/log/sys/index

20

11

在線用戶

/online/index

27

1

操作權限

/operator/index

28

1

部門管理

/dept/index

29

11

系統管理

/system/index

30

0

賬號關聯

/oauth2/index

一般的做法是查詢出所有, 然後遞歸構建樹形結構, 但其實可以巧用 MyBatis 在查詢時就進行轉換, 這用到了 MyBatis 的 resultMap 功能.

首先由以下表結構定義:

create table menu  (      menu_id   int primary key auto_increment comment '菜單 ID',      parent_id int          not null,      menu_name varchar(20)  null comment '菜單名稱',      url       varchar(100) null comment '菜單 URL'  );

實體類定義:

public class Menu {        private Integer menuId;        private Integer parentId;        private String menuName;        private String url;        private List<Menu> children;  }

resultMap 定義:

<resultMap id="BaseResultTreeMap" type="im.zhaojun.model.Menu">      <id column="menu_id" jdbcType="INTEGER" property="menuId"/>      <result column="parent_id" jdbcType="INTEGER" property="parentId"/>      <result column="menu_name" jdbcType="VARCHAR" property="menuName"/>      <result column="url" jdbcType="VARCHAR" property="url"/>      <collection property="children" ofType="Menu" select="selectTree" column="{parent_id = menu_id}"/>  </resultMap>

查詢定義:

<select id="selectMenuTree" resultMap="BaseResultTreeMap">      select *      from menu      <where>          <choose>              <when test="parent_id!=null">                  and parent_id = #{parent_id}              </when>              <otherwise>                  and parent_id = 0              </otherwise>          </choose>      </where>  </select>

查詢出的結果集:

[      {          "menuId": 1,          "parentId": 0,          "menuName": "權限管理",          "url": "#",          "children": [              {                  "menuId": 2,                  "parentId": 1,                  "menuName": "用戶管理",                  "url": "/user/index",                  "children": []              },              {                  "menuId": 3,                  "parentId": 1,                  "menuName": "角色管理",                  "url": "/role/index",                  "children": []              },              {                  "menuId": 4,                  "parentId": 1,                  "menuName": "菜單權限",                  "url": "/menu/index",                  "children": []              },              {                  "menuId": 27,                  "parentId": 1,                  "menuName": "操作權限",                  "url": "/operator/index",                  "children": []              },              {                  "menuId": 28,                  "parentId": 1,                  "menuName": "部門管理",                  "url": "/dept/index",                  "children": []              }          ]      },      {          "menuId": 11,          "parentId": 0,          "menuName": "系統監控",          "url": "#",          "children": [              {                  "menuId": 12,                  "parentId": 11,                  "menuName": "登錄日誌",                  "url": "/log/login/index",                  "children": []              },              {                  "menuId": 19,                  "parentId": 11,                  "menuName": "操作日誌",                  "url": "/log/sys/index",                  "children": []              },              {                  "menuId": 20,                  "parentId": 11,                  "menuName": "在線用戶",                  "url": "/online/index",                  "children": []              },              {                  "menuId": 29,                  "parentId": 11,                  "menuName": "系統管理",                  "url": "/system/index",                  "children": []              }          ]      },      {          "menuId": 30,          "parentId": 0,          "menuName": "賬號關聯",          "url": "/oauth2/index",          "children": []      }  ]

看完了效果, 我們來講解下 resultMap 中的定義, 主要是 collection 語句中的內容:

<collection property="children" ofType="Menu" select="selectTree" column="{parent_id = menu_id}"/>
  • property="children" 對應的是實體類中的 children 字段.
  • ofType="Menu" 對應 children 中泛型的的類型.
  • select="selectTree" 指定了 SELECT 語句的 id.
  • column="{parent_id = menu_id}" 參數的表達式, 向子語句中傳遞參數.

這個collection整體的含義可以這樣理解:

通過 selectTree 這個 SELECT 語句來獲取當前菜單中的 children 屬性結果, 在查詢子菜單的 SELECT 語句中, 需要傳遞一個 parent_id 參數, 這個參數的值就是當前菜單中的 id.

本章實例代碼: https://github.com/zhaojun1998/mybatis-recursive-demo