使用Mybatis生成树形菜单-适用于各种树形场景
开发中我们难免会遇到各种树形结构展示的场景。比如用户登录系统后菜单的展示,某些大型购物网站商品的分类展示等等,反正开发中会遇到各种树形展示的功能,这些功能大概处理的思路都是一样的,所以本文就总结一下树形结构的代码生成,在开发的时候套用这种结构就可以了。
好了正文开始,首先相关的SQL脚本【MYSQL】提供给你(包吃包住包SQL)【如果巅峰留不住那就进厂包吃住】。
DDL语句:
CREATE TABLE `student`.`SYS_menu`( `ID` INT(10) NOT NULL AUTO_INCREMENT, `name` VARCHAR(200) NOT NULL, `permissions` VARCHAR(1000), `url` VARCHAR(200), `description` VARCHAR(2000), `icon_cls` VARCHAR(2000), `pid` INT(10), `status` INT(2), `resource_type` INT(2), `sort` INT(6), `create_time` TIMESTAMP, `update_time` TIMESTAMP, PRIMARY KEY (`ID`) ) ENGINE=INNODB CHARSET=utf8;
初始化语句:
INSERT INTO SYS_MENU(NAME,permissions,url,description,icon_cls,pid,STATUS,resource_type,create_time,update_time,SORT) VALUES('主菜单',NULL,NULL,'数据主菜单',NULL,0,1,1,NOW(),NOW(),NULL) ; INSERT INTO SYS_MENU(NAME,permissions,url,description,icon_cls,pid,STATUS,resource_type,create_time,update_time,SORT) VALUES('菜单1',NULL,NULL,'菜单1',NULL,(SELECT * FROM (SELECT id FROM SYS_MENU WHERE NAME='主菜单') a ),1,1,NOW(),NOW(),1) ; INSERT INTO SYS_MENU(NAME,permissions,url,description,icon_cls,pid,STATUS,resource_type,create_time,update_time,SORT) VALUES('菜单2',NULL,NULL,'菜单2',NULL,(SELECT * FROM (SELECT id FROM SYS_MENU WHERE NAME='主菜单') a ),1,1,NOW(),NOW(),2) ; INSERT INTO SYS_MENU(NAME,permissions,url,description,icon_cls,pid,STATUS,resource_type,create_time,update_time,SORT) VALUES('菜单3',NULL,NULL,'菜单3',NULL,(SELECT * FROM (SELECT id FROM SYS_MENU WHERE NAME='主菜单') a ),1,1,NOW(),NOW(),3) ; INSERT INTO SYS_MENU(NAME,permissions,url,description,icon_cls,pid,STATUS,resource_type,create_time,update_time,SORT) VALUES('菜单1.1',NULL,NULL,'菜单1的子菜单',NULL,(SELECT * FROM (SELECT id FROM SYS_MENU WHERE NAME='菜单1') a ),1,1,NOW(),NOW(),1) ; INSERT INTO SYS_MENU(NAME,permissions,url,description,icon_cls,pid,STATUS,resource_type,create_time,update_time,SORT) VALUES('菜单1.2',NULL,NULL,'菜单1的子菜单',NULL,(SELECT * FROM (SELECT id FROM SYS_MENU WHERE NAME='菜单1') a ),1,1,NOW(),NOW(),2) ; INSERT INTO SYS_MENU(NAME,permissions,url,description,icon_cls,pid,STATUS,resource_type,create_time,update_time,SORT) VALUES('菜单1.3',NULL,NULL,'菜单1的子菜单',NULL,(SELECT * FROM (SELECT id FROM SYS_MENU WHERE NAME='菜单1') a ),1,1,NOW(),NOW(),3) ; INSERT INTO SYS_MENU(NAME,permissions,url,description,icon_cls,pid,STATUS,resource_type,create_time,update_time,SORT) VALUES('菜单2.1',NULL,NULL,'菜单2的子菜单',NULL,(SELECT * FROM (SELECT id FROM SYS_MENU WHERE NAME='菜单2') a ),1,1,NOW(),NOW(),1) ; INSERT INTO SYS_MENU(NAME,permissions,url,description,icon_cls,pid,STATUS,resource_type,create_time,update_time,SORT) VALUES('菜单2.2',NULL,NULL,'菜单2的子菜单',NULL,(SELECT * FROM (SELECT id FROM SYS_MENU WHERE NAME='菜单2') a ),1,1,NOW(),NOW(),2) ; INSERT INTO SYS_MENU(NAME,permissions,url,description,icon_cls,pid,STATUS,resource_type,create_time,update_time,SORT) VALUES('菜单2.3',NULL,NULL,'菜单2的子菜单',NULL,(SELECT * FROM (SELECT id FROM SYS_MENU WHERE NAME='菜单2') a ),1,1,NOW(),NOW(),3) ; INSERT INTO SYS_MENU(NAME,permissions,url,description,icon_cls,pid,STATUS,resource_type,create_time,update_time,SORT) VALUES('菜单3.1',NULL,NULL,'菜单3的子菜单',NULL,(SELECT * FROM (SELECT id FROM SYS_MENU WHERE NAME='菜单3') a ),1,1,NOW(),NOW(),1) ; INSERT INTO SYS_MENU(NAME,permissions,url,description,icon_cls,pid,STATUS,resource_type,create_time,update_time,SORT) VALUES('菜单3.2',NULL,NULL,'菜单3的子菜单',NULL,(SELECT * FROM (SELECT id FROM SYS_MENU WHERE NAME='菜单3') a ),1,1,NOW(),NOW(),2) ; INSERT INTO SYS_MENU(NAME,permissions,url,description,icon_cls,pid,STATUS,resource_type,create_time,update_time,SORT) VALUES('菜单3.3',NULL,NULL,'菜单3的子菜单',NULL,(SELECT * FROM (SELECT id FROM SYS_MENU WHERE NAME='菜单3') a ),1,1,NOW(),NOW(),3)
数据结构一般就是上面得那样,只是初始化得数据按照你开发得需求初始化。
然后就是创建简单得项目,好了那我就贴出相关得代码,这些大家开发得时候可以根据需求进行嵌套使用。
// entity @Data @TableName("sys_menu") public class SysMenu implements Serializable { private static final long serialVersionUID = 1L; @TableId private Integer id; private String name; private String permissions; private String url; private String description; private String iconCls; private Integer pid; private Integer status; private Integer resourceType; private Integer sort; private Date createTime; private Date updateTime; /** * 此处为了简单我就不新建DTO对象了, * 加一个children属性,注意如果不是数据库的字段一定要 * 加下面d额那个注解 */ @TableField(exist=false) private List<SysMenu> children; }
// mapper接口,很简单没有多余代码 public interface SysMenuDao extends BaseMapper<SysMenu> { }
// mapper 文件也是没有多余的代码,使用Mybatis-Plus特有的就行 // 其中下面的resultMap也可以去掉。 <mapper namespace="io.renren.mapper.SysMenuDao"> <!-- 可根据自己的需求,是否要使用 --> <resultMap type="io.renren.domain.SysMenu" id="sysMenuMap"> <result property="id" column="ID"/> <result property="name" column="name"/> <result property="permissions" column="permissions"/> <result property="url" column="url"/> <result property="description" column="description"/> <result property="iconCls" column="icon_cls"/> <result property="pid" column="pid"/> <result property="status" column="status"/> <result property="resourceType" column="resource_type"/> <result property="sort" column="sort"/> <result property="createTime" column="create_time"/> <result property="updateTime" column="update_time"/> </resultMap> </mapper>
// service 主要的逻辑代码就在这里。 public interface SysMenuService extends IService<SysMenu> { List<SysMenu> getMenuTree(); } @Service("sysMenuService") public class SysMenuServiceImpl extends ServiceImpl<SysMenuDao, SysMenu> implements SysMenuService { @Autowired private SysMenuDao sysMenuDao ; @Override public List<SysMenu> getMenuTree() { //查询出所有菜单 List<SysMenu> sysMenus = sysMenuDao.selectList(null); //2、组装成树形结构 //2.1)、找到所有的一级菜单 List<SysMenu> level1Menus = sysMenus.stream().filter(sysMenu -> sysMenu.getPid() == 0 ).map((menu) -> { menu.setChildren(getChildrens(menu, sysMenus)); return menu; // 排序 }).sorted((menu1, menu2) -> { return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort()); }).collect(Collectors.toList()); return level1Menus; } //递归查找所有菜单的子菜单,主要就是用了这个递归查询。 // 我也都写了相关的注释。 private List<SysMenu> getChildrens(SysMenu root, List<SysMenu> all) { List<SysMenu> children = all.stream().filter(sysMenu -> { return sysMenu.getPid() == root.getId(); }).map(sysMenu -> { //1、找到子菜单 sysMenu.setChildren(getChildrens(sysMenu, all)); return sysMenu; }).sorted((menu1, menu2) -> { //2、菜单的排序 return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort()); }).collect(Collectors.toList()); return children; }
有的小伙伴可能会说,博主我们项目使用的jdk7 。上面的代码使用不了啊,这篇文章对我来说不合适啊。好了安排上,既然写文章我就要写的明明白白,下面是不使用Stream流完成的功能。
List<SysMenu> getMenuTreeVerLowJava8() ; @Override public List<SysMenu> getMenuTreeVerLowJava8() { List<SysMenu> sysMenus = sysMenuDao.selectList(null); List<SysMenu> tree = new ArrayList<>(); for (SysMenu sysmenu: sysMenus) { if (sysmenu.getPid()==0){ tree.add(getChildrens02(sysmenu,sysMenus)) ; } } return tree; } /** * 不使用stream的递归调用 * @param list * @return */ private SysMenu getChildrens02(SysMenu sysMenu, List<SysMenu> list) { List<SysMenu> children = new ArrayList<SysMenu>(); for (SysMenu sysMenu2 : list) { if (sysMenu2.getPid() == sysMenu.getId()) { // 递归调用 SysMenu result = getChildrens02(sysMenu2, list); children.add(result); } } sysMenu.setChildren(children); return sysMenu; }
使用Java7的菜单树我没有进行排序,Java7的排序使用起来也很简单,相信大家开发的时候都使用过,大家可以自行完成排序。生成的树形结构太多我就不贴出来了肯定是正确的。
开发中就是需要这种记录,为什么呢,当你没看到这篇文章你写一个树形结构的代码可能需要一天,而你点一下关注,后面开发中你遇到这种功能的开发一个小时应该就能搞定并且还没有问题,极大的提高了开发效率,领导看到你效率那么高应该也会很高兴,说不定升职加薪就在眼前。点点关注何乐而不为呢?生活中也一样,你同样需要记录总结,这样你应该也越走越顺,比如你今天上班路上遇到一个坑,你记住了。下次走过这里你就会避开这个坑,路也越走越顺了。