社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
最近需要将一个菜单存入数据库,然后在用户登录时根据用户等级或者用户权限显示相应的菜单。这里我们只做后端的数据处理。
这里我会介绍两种方法:
第一种方法是将树结构封装进一个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多条数据的时候影响是不那么明显的,不过当有庞大数据量的时候可能就会有一定影响了,但是当有庞大数据量时我们往往会用二级缓存去处理,所以第二种方法还是有很广的适用范围的
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!