springboot+mysql+mybatis实现菜单树(超详细)
在项目中常常能用到菜单树,今天就带大家自己实现一个
这个是我的目录结构
一、mysql 表
首先我们先建立一张表, 这个是菜单表,主要 两个字段就是menu_id 和 parent_id这两个字段
DROP TABLE IF EXISTS `sys_menu`;
CREATE TABLE `sys_menu` (
`menu_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '菜单ID',
`menu_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '菜单名称',
`parent_id` bigint(20) NULL DEFAULT 0 COMMENT '父菜单ID',
`order_num` int(4) NULL DEFAULT 0 COMMENT '显示顺序',
`url` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '#' COMMENT '请求地址',
`target` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '打开方式(menuItem页签 menuBlank新窗口)',
`menu_type` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '菜单类型(M目录 C菜单 F按钮)',
`visible` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '0' COMMENT '菜单状态(0显示 1隐藏)',
`is_refresh` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '1' COMMENT '是否刷新(0刷新 1不刷新)',
`perms` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限标识',
`icon` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '#' COMMENT '菜单图标',
`create_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
`update_by` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '更新者',
`update_time` datetime(0) NULL DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '备注',
PRIMARY KEY (`menu_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1062 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '菜单权限表' ROW_FORMAT = Dynamic;
INSERT INTO `sys_menu` VALUES (1, '系统管理', 0, 1, '#', '', 'M', '0', '1', '', 'fa fa-gear', 'admin', '2022-08-09 13:41:06', '', NULL, '系统管理目录');
INSERT INTO `sys_menu` VALUES (2, '系统监控', 0, 2, '#', '', 'M', '0', '1', '', 'fa fa-video-camera', 'admin', '2022-08-09 13:41:06', '', NULL, '系统监控目录');
INSERT INTO `sys_menu` VALUES (100, '用户管理', 1, 1, '/system/user', '', 'C', '0', '1', 'system:user:view', 'fa fa-user-o', 'admin', '2022-08-09 13:41:06', '', NULL, '用户管理菜单');
INSERT INTO `sys_menu` VALUES (113, '缓存监控', 2, 5, '/monitor/cache', '', 'C', '0', '1', 'monitor:cache:view', 'fa fa-cube', 'admin', '2022-08-09 13:41:06', '', NULL, '缓存监控菜单');
INSERT INTO `sys_menu` VALUES (114, '表单构建', 3, 1, '/tool/build', '', 'C', '0', '1', 'tool:build:view', 'fa fa-wpforms', 'admin', '2022-08-09 13:41:06', '', NULL, '表单构建菜单');
INSERT INTO `sys_menu` VALUES (501, '登录日志', 108, 2, '/monitor/logininfor', '', 'C', '0', '1', 'monitor:logininfor:view', 'fa fa-file-image-o', 'admin', '2022-08-09 13:41:06', '', NULL, '登录日志菜单');
INSERT INTO `sys_menu` VALUES (1000, '用户查询', 100, 1, '#', '', 'F', '0', '1', 'system:user:list', '#', 'admin', '2022-08-09 13:41:06', '', NULL, '');
INSERT INTO `sys_menu` VALUES (1012, '菜单查询', 102, 1, '#', '', 'F', '0', '1', 'system:menu:list', '#', 'admin', '2022-08-09 13:41:06', '', NULL, '');
INSERT INTO `sys_menu` VALUES (1016, '部门查询', 103, 1, '#', '', 'F', '0', '1', 'system:dept:list', '#', 'admin', '2022-08-09 13:41:06', '', NULL, '');
INSERT INTO `sys_menu` VALUES (1021, '岗位新增', 104, 2, '#', '', 'F', '0', '1', 'system:post:add', '#', 'admin', '2022-08-09 13:41:06', '', NULL, '');
INSERT INTO `sys_menu` VALUES (1022, '岗位修改', 104, 3, '#', '', 'F', '0', '1', 'system:post:edit', '#', 'admin', '2022-08-09 13:41:06', '', NULL, '');
INSERT INTO `sys_menu` VALUES (1026, '字典新增', 105, 2, '#', '', 'F', '0', '1', 'system:dict:add', '#', 'admin', '2022-08-09 13:41:06', '', NULL, '');
INSERT INTO `sys_menu` VALUES (1027, '字典修改', 105, 3, '#', '', 'F', '0', '1', 'system:dict:edit', '#', 'admin', '2022-08-09 13:41:06', '', NULL, '');
INSERT INTO `sys_menu` VALUES (1057, '生成查询', 115, 1, '#', '', 'F', '0', '1', 'tool:gen:list', '#', 'admin', '2022-08-09 13:41:06', '', NULL, '');
SET FOREIGN_KEY_CHECKS = 1;
二、建立controller、service、entity、mapper等
1.controller层
代码如下(示例):
@AllArgsConstructor
public class SysMenuRestController {
private final SysMenuService sysMenuService;
@GetMapping("/queryTreeMenu")
public AjaxResult queryTreeMenu() {
List<SysMenu> sysMenus = sysMenuService.queryAll();
return AjaxResult.success(sysMenus);
}
}
2.service层
代码如下(示例):
public interface SysMenuService extends IService<SysMenu> {
List<SysMenu> queryAll();
3.impl层
代码如下(示例): 这个注释我写的很明白,一看就懂
@Service
@AllArgsConstructor
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {
private final SysMenuMapper sysMenuMapper;
@Override
public List<SysMenu> queryAll() {
List<SysMenu> menusBase = sysMenuMapper.queryById(0);
List<SysMenu> menuNotBase = sysMenuMapper.queryAllNotBase();
for (SysMenu sysMenu : menusBase) {
List<SysMenu> menus = iterateMenus(menuNotBase, sysMenu.getMenuId());
sysMenu.setMenuChildren(menus);
}
return menusBase;
}
public List<SysMenu> iterateMenus(List<SysMenu> sysMenuList, Long menuId) {
List<SysMenu> result = new ArrayList<>();
for (SysMenu sysMenu : sysMenuList) {
Long menuIds = sysMenu.getMenuId();
String parentId = String.valueOf(sysMenu.getParentId());
if (StringUtils.isNotBlank(parentId)) {
if (parentId.equals(String.valueOf(menuId))) {
List<SysMenu> iterateMenu = iterateMenus(sysMenuList, menuIds);
sysMenu.setMenuChildren(iterateMenu);
result.add(sysMenu);
}
}
}
return result;
}
}
4.mapper层
代码如下(示例):
public interface SysMenuMapper extends BaseMapper<SysMenu> {
/**
* 根据父类id查询子类菜单
* @param pid 父类id
* @return 数据
*/
List<SysMenu> queryById(Integer pid);
/**
* @return 除一级菜单数据
*/
List<SysMenu> queryAllNotBase();
}
5.xml
代码如下(示例):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.study.pc.mapper.SysMenuMapper">
<resultMap id="BaseResultMap" type="com.study.pc.entity.SysMenu">
<id column="menu_id" property="menuId" />
<result column="menu_name" property="menuName" />
<result column="parent_id" property="parentId" />
<result column="order_num" property="orderNum" />
<result column="path" property="path" />
<result column="component" property="component" />
<result column="query" property="query" />
<result column="is_frame" property="isFrame" />
<result column="is_cache" property="isCache" />
<result column="menu_type" property="menuType" />
<result column="visible" property="visible" />
<result column="status" property="status" />
<result column="perms" property="perms" />
<result column="icon" property="icon" />
<result column="create_by" property="createBy" />
<result column="create_time" property="createTime" />
<result column="update_by" property="updateBy" />
<result column="update_time" property="updateTime" />
<result column="remark" property="remark" />
</resultMap>
<sql id="Base_Column_List">
menu_id, menu_name, parent_id, order_num, path, component, query, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark
</sql>
<select id="queryById" resultType="com.study.pc.entity.SysMenu">
SELECT * FROM sys_menu WHERE menu_id is not null
<if test="pid != null">
AND parent_id = #{pid}
</if>
</select>
<select id="queryAll" resultType="com.study.pc.entity.SysMenu">
SELECT * FROM sys_menu
</select>
<select id="queryAllNotBase" resultType="com.study.pc.entity.SysMenu">
SELECT * FROM sys_menu WHERE parent_id != 0
</select>
</mapper>
entity 实体层
代码如下(示例):
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("sys_menu")
public class SysMenu extends Model<SysMenu> {
private static final long serialVersionUID = 1L;
@TableId(value = "menu_id", type = IdType.AUTO)
private Long menuId;
private String menuName;
private Long parentId;
private Integer orderNum;
private String path;
private String component;
private String query;
private Integer isFrame;
private Integer isCache;
private String menuType;
private String visible;
private String status;
private String perms;
private String icon;
private String createBy;
private Date createTime;
private String updateBy;
private Date updateTime;
private String remark;
private List<SysMenu> menuChildren;
@Override
protected Serializable pkVal() {
return this.menuId;
}
}
测试
测试结果正确
总结
其实这个很简单,没有什么逻辑。 主要的逻辑就是在 service层 xml中其实只做了查询 一个是查询一级菜单,另一个是查询子菜单
|