SpringBoot +Mybatis实现三层树形结构 - Go语言中文社区

SpringBoot +Mybatis实现三层树形结构


最近需要将一个菜单存入数据库,然后在用户登录时根据用户等级或者用户权限显示相应的菜单。这里我们只做后端的数据处理。

这里我会介绍两种方法:

第一种方法是将树结构封装进一个bean对象,查出的数据进行for循环层层封装处理。

第二种方法是通过一种递归的方式。后面会具体介绍到。

 

先来第一种。

进入正题:

先看看数据库表设计

这里menu_id是主键,其余的是我需要的字段,然后自关联字段是下面的menu_level_id, 
menu_level_id 就是父id,假如父id为0的话,就代表这条数据是顶级菜单。

我们来看看Java代码的结构

实体类采用的是自嵌套的方式

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import java.util.Date;

@ToString@Getter@Setter
public class Menu {
    private Long menuId;

    private String menuName;

    private String menuCode;

    private String menuUrl;

    private Long menuLevelId;

    private String menuIcon;

    private Integer menuPower;

    private Byte enable;

    private Date createTime;
}

这里引入了Lombok,用注解进行get,set和tostring方法的编写

下面是dao层

import com.mass.biz.level.model.Menutree;
import com.mass.framework.dao.CRUDMapper;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;
import java.util.Map;

@Mapper
public interface MenuMapper extends CRUDMapper<Menutree> {
    List<Menutree> getallmenu(Integer roleid);
    List<Map<String,Object>> getalltree(Integer roleid);
}

第一个dao是我要介绍的第一个方法,第二个是第二个方法

import com.mass.biz.level.model.Menutree;
import java.util.List;
import java.util.Map;

public interface LevelService {
    List<Menutree> getallmenu(Integer roleid);
    List<Map<String,Object>> getalltree(Integer roleid);
}

这里是service接口,第一种方法的返回的是上面实体对象类型的list集合。

<?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.mass.biz.level.dao.MenuMapper" >
  <resultMap id="BaseResultMap" type="com.mass.biz.level.model.Menutree" >
    <id column="menu_id" property="menuId" jdbcType="BIGINT" />
    <result column="menu_name" property="menuName" jdbcType="VARCHAR" />
    <result column="menu_code" property="menuCode" jdbcType="VARCHAR" />
    <result column="menu_url" property="menuUrl" jdbcType="VARCHAR" />
    <result column="menu_level_id" property="menuLevelId" jdbcType="BIGINT" />
    <result column="menu_icon" property="menuIcon" jdbcType="VARCHAR" />
    <result column="menu_power" property="menuPower" jdbcType="INTEGER" />
    <result column="enable" property="enable" jdbcType="TINYINT" />
    <result column="create_time" property="createTime" jdbcType="TIMESTAMP" />
  </resultMap>
  <sql id="Base_Column_List" >
    menu_id, menu_name, menu_code, menu_url, menu_level_id, menu_icon, menu_power, enable, 
    create_time
  </sql>

    <!--上面是关系对象映射,不用看的太细致,这里关系对象映射只映射了表中有的字段,后面的集合类型不进行映射,我们会在后面的代码中进行数据封装-->
    <select id="getallmenu" resultMap="BaseResultMap" parameterType="java.lang.Integer" >
        select
        <include refid="Base_Column_List" />
        from sys_menu
        WHERE
        `enable` = 0
        AND menu_power >= #{roleid}
        ORDER BY
        menu_id
    </select>
    <select id="getalltree" resultType="java.util.HashMap">
        SELECT
        menu_id AS id,
        menu_name,
        menu_code,
        menu_url,
        menu_level_id AS pid,
        menu_icon,
        menu_power,
        `enable`,
        create_time
        FROM
            sys_menu
        WHERE
            `enable` = 0
        AND menu_power >= #{roleid}
        ORDER BY
            menu_id
    </select>

</mapper>

第一个sql也就是第一个要讲的方法,sql没有做特殊处理只是进行查询全部的操作

重点来了,在controller

@Autowired
private LevelService levelService;//此处引入service
private final static Logger LOGGER = LoggerFactory.getLogger(LevelController.class);//这里是日志不用管
@LogAop(menuName = "树结构查询", operationDesc = "查询", operationType = "3")//这边是自定义类型的注解也可以不用看
@RequestMapping(value = "/list", method = RequestMethod.GET)
public AjaxResponse getallmenu(HttpSession session){
    long startTime=System.nanoTime(); //获取开始时间
    UserInfo userInfo = WebHelper.getSessionAttribute(session, LOGIN_USER_KEY, UserInfo.class);//此处是从session中获取用户
    Integer roleid = userInfo.getRoleid();//获取用户角色权限
    List<Menutree> menus = levelService.getallmenu(roleid);//因为我们这里只介绍三层树结构的封装,在参数方面大家可以不用太关心

    HashMap<Long, List<Menutree>> map = new HashMap<>();
    for (int i=0;i<menus.size();i++){
        Menutree menu = menus.get(i);
        if (!map.containsKey(menu.getMenuLevelId())){
            map.put(menu.getMenuLevelId(), new ArrayList<Menutree>());
            map.get(menu.getMenuLevelId()).add(menu);
        }else {
            map.get(menu.getMenuLevelId()).add(menu);
        }
    }

    ArrayList<Menutree> menu1s = new ArrayList<>();
    List<Menutree> menutrees = map.get(0L);
    for (Menutree menutree : menutrees) {
        menu1s.add(menutree);
    }
    for (Menutree menu1 : menu1s) {
        ArrayList<Menutree> menu2s = new ArrayList<>();
        Long menuId = menu1.getMenuId();
        List<Menutree> menutrees1 = map.get(menuId);
        for (Menutree menutree : menutrees1) {
            menu2s.add(menutree);
        }
        for (Menutree menu2 : menu2s) {
            Long menuId1 = menu2.getMenuId();
            List<Menutree> menus2 = map.get(menuId1);
            menu2.setMenus(menus2);
        }
        menu1.setMenus(menutrees1);
    }
    AjaxResponse success = AjaxResponse.success(menu1s);
    long endTime=System.nanoTime(); //获取结束时间
    System.out.println("程序运行时间: "+(endTime-@Autowired
private LevelService levelService;//此处引入service
private final static Logger LOGGER = LoggerFactory.getLogger(LevelController.class);//这里是日志不用管
@LogAop(menuName = "树结构查询", operationDesc = "查询", operationType = "3")//这边是自定义类型的注解也可以不用看
@RequestMapping(value = "/list", method = RequestMethod.GET)
public AjaxResponse getallmenu(HttpSession session){
    long startTime=System.nanoTime(); //获取开始时间
    UserInfo userInfo = WebHelper.getSessionAttribute(session, LOGIN_USER_KEY, UserInfo.class);//此处是从session中获取用户
    Integer roleid = userInfo.getRoleid();//获取用户角色权限
    List<Menutree> menus = levelService.getallmenu(roleid);//因为我们这里只介绍三层树结构的封装,在参数方面大家可以不用太关心
    //这里创建一个map集合,map集合的作用是用来进行分类处理,此处的key是实体类中的 menuLevelId,value值是查出来的list集合
    HashMap<Long, List<Menutree>> map = new HashMap<>();
    //我们先将所有父id相同的数据进行归类处理
    //先遍历menus集合
    for (int i=0;i<menus.size();i++){
        Menutree menu = menus.get(i);//获取第i个对象
        //此处判断当前key在map集合中是否存在,倘若不存在和父id相等的key,将当前的父id当作key,然后存入一个空的list集合
        if (!map.containsKey(menu.getMenuLevelId())){
            map.put(menu.getMenuLevelId(), new ArrayList<Menutree>());
            map.get(menu.getMenuLevelId()).add(menu);
        }else {
            //倘若存在直接将menu存进已经存在的key的map中,
            map.get(menu.getMenuLevelId()).add(menu);
        }
    }

    ArrayList<Menutree> menu1s = new ArrayList<>();
    List<Menutree> menutrees = map.get(0L);//先封装顶级菜单,顶级菜单的父id为0,直接取出然后添加当上面的集合中
    for (Menutree menutree : menutrees) {
        menu1s.add(menutree);
    }
    //遍历上面的menu1s集合
    for (Menutree menu1 : menu1s) {
        ArrayList<Menutree> menu2s = new ArrayList<>();//创建一个新的集合来存储二级菜单
        Long menuId = menu1.getMenuId();//根据已经封装好的集合,查出当前对象的menuId
        List<Menutree> menutrees1 = map.get(menuId);//在上面存储所有的map集合中根据上面的menuId查出二级菜单
        for (Menutree menutree : menutrees1) {
            menu2s.add(menutree);//遍历menutrees1后将二级菜单存入集合
        }
        //编辑二级菜单的集合
        for (Menutree menu2 : menu2s) {
            Long menuId1 = menu2.getMenuId();//获取二级菜单的id
            List<Menutree> menus2 = map.get(menuId1);//根据 二级菜单的id查出三级菜单
            menu2.setMenus(menus2);//将三级菜单存入二级菜单的集合中
        }
        menu1.setMenus(menutrees1);//此时二级菜单里已经存在二级菜单和三级菜单,将二级菜单存入顶级菜单
    }
    AjaxResponse success = AjaxResponse.success(menu1s);//这里是我的一个基础类,所有的查出的结果都会传到这里然后进行json格式的转换
    long endTime=System.nanoTime(); //获取结束时间
    System.out.println("程序运行时间: "+(endTime-startTime)+"ns");
    return success;
}startTime)+"ns");
    return success;
}

这里的逻辑还算清晰,可能我讲的不太好,可以把代码复制到自己的编辑器 看看,因为排版不太好。

在这里我们需要封装几层的树就需要些写几层for循环。可能觉得稍微有一点麻烦。

后面的这种相对简单些,上面的service和dao以及mapper已经给出来了,这里贴 controller代码

先看工具类吧

/**
 * @Author 段瑜
 * @Description //TODO 
 * @Date 2019/1/1 21:45
 * @Param [list, pid]   list为查出的结果集 ,pid为父id,首次传入父id为 0 也就是顶级
 * @return java.util.List<java.util.Map<java.lang.String,java.lang.Object>>
 **/
public static List<Map<String,Object>> getalltree(List<Map<String,Object>> list,String pid){
    List<Map<String, Object>> maps = new ArrayList<Map<String, Object>>();//创建一个list集合用于等会返回
    for (Map<String, Object> map : list){//遍历传入的list集合
        if (pid.equals(map.get("pid").toString())) {//判断传入的pid和当前的对象遍历出来的pid值是否相等
            //假如相等的话就将list集合在传入,并获取当前对象的id作为pid传入,并再次自己调用该方法,直到获取 当前对象最下级后停止调用
            List<Map<String, Object>> getalltree = getalltree(list,map.get("id").toString());
            map.put("getalltree",getalltree);//将获取到的list集合存入定义好map中
            maps.add(map);//将map存入maps集合
        }
    }
    return maps;//返回list集合
}

然后我们再来看controller

@LogAop(menuName = "树结构查询", operationDesc = "查询", operationType = "3")
    @RequestMapping(value = "/gettree")
    public AjaxResponse gettree(HttpSession session){
        long startTime=System.nanoTime(); //获取开始时间
        UserInfo userInfo = WebHelper.getSessionAttribute(session, LOGIN_USER_KEY, UserInfo.class);
        Integer roleid = userInfo.getRoleid();
        List<Map<String, Object>> getalltree = levelService.getalltree(roleid);

        List<Map<String, Object>> getalltree1 = getalltree(getalltree, "0");
        AjaxResponse success = AjaxResponse.success(getalltree1);
        long endTime=System.nanoTime(); //获取结束时间
        System.out.println("程序运行时间: "+(endTime-startTime)+"ns");
        return success;
    }

controller简单很多直接调用刚才的方法就可以了,传入查出来的集合,然后传入顶级的pid

第二种方法是一个朋友给我看的,感觉挺简洁的,原本我考虑到效率问题,应该是不能使用递归的,因为递归会影响性能,但是在这样的数据量下,我测试了一下代码速度,其实是差不多 的,觉得还是挺好的

接下来测试一下

我们看第二个方法的测试

我们来看一下两串代码没有用一级缓存的速度

第一种方法

程序运行时间: 29317000ns

第二种方法

程序运行时间: 20133600ns

然后我们在调用一次方法那么就会调用缓存

第一种方法

程序运行时间: 9198400ns

第二种方法

程序运行时间: 9627000ns

可以看出虽然有影响在100多条数据的时候影响是不那么明显的,不过当有庞大数据量的时候可能就会有一定影响了,但是当有庞大数据量时我们往往会用二级缓存去处理,所以第二种方法还是有很广的适用范围的

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/ccsert/article/details/86300352
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-03-07 10:50:48
  • 阅读 ( 1219 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢