巧用 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