一、引言
小编之前写过如何实现一二级菜单的文章,回顾了下之前所实现的逻辑方式,简直惨不忍睹~~
由于近期小编接触新的项目需要实现展示菜单功能,但这次的菜单是需要多级,并且级数不固定。
像这种需求,一般很简单的来说就是用递归实现了,可以从第一级一直往下查,一直查询到为空为止。
二、数据库表结构
这里小编之列出来几个基本的字段,但是够用了
CREATE TABLE `mall_category` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '类别Id',
`parent_id` int(11) DEFAULT NULL COMMENT '父类别id当id=0时说明是根节点,一级类别',
`name` varchar(50) DEFAULT NULL COMMENT '类别名称',
`status` tinyint(1) DEFAULT '1' COMMENT '类别状态1-正常,2-已废弃',
`sort_order` int(4) DEFAULT NULL COMMENT '排序编号,同类展示顺序,数值相等则自然排序',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=100034 DEFAULT CHARSET=utf8;
三、代码实现
步骤一:一共有两个实体类,一个是对应数据库的实体类,还有一个是返回给前端的实体类
/**
* @Auther: IT贱男
* @Date: 2019/12/16 16:45
* @Description: 商品类目
*/
@Data
public class Category {
private Integer id;
private Integer parentId;
private String name;
private Integer status;
private Integer sortOrder;
private Date createTime;
private Date updateTime;
}
/**
* @Auther: IT贱男
* @Date: 2020/2/26 13:58
* @Description: 返回前端对应实体类
*/
@Data
public class CategoryVo {
private Integer id;
private Integer parentId;
private String name;
private Integer sortOrder;
/**
* 存放类目子级目录
*/
private List<CategoryVo> subCategories;
}
步骤二:具体实现逻辑,小编这是从项目里面直接截取出来的代码,但是能够实现多级菜单功能~~
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Auther: IT贱男
* @Date: 2020/2/26 13:47
* @Description: 类目查询
*/
@Service
public class ICategoryServiceImpl implements ICategoryService {
@Autowired
private CategoryMapper categoryMapper;
/**
* 对象转换 将数据库实体类对象转还给前端实体
*
* @param category
* @return
*/
private CategoryVo categoryTOCategoryVo(Category category) {
CategoryVo categoryVo = new CategoryVo();
BeanUtils.copyProperties(category, categoryVo);
return categoryVo;
}
@Override
public ResponseVo selectCategoryAll() {
// 先查询出全部类目
List<Category> categories = categoryMapper.selectCategoryAll();
// 获取一级菜单 , 0 代表一级目录
List<CategoryVo> rootCategory = categories.stream()
.filter(e -> e.getParentId().equals("0"))
.map(this::categoryTOCategoryVo)
.sorted(Comparator.comparing(CategoryVo::getSortOrder).reversed())
.collect(Collectors.toList());
// 查找子节点
findSubCategory(categories, rootCategory);
return ResponseVo.success(rootCategory);
}
/**
* 根据一级递归调用子级
*
* @param categoryList
* @param rootCategory
* @return
*/
private void findSubCategory(List<Category> categoryList, List<CategoryVo> rootCategory) {
// 遍历一级
for (CategoryVo categoryVo : rootCategory) {
List<CategoryVo> subCategoryVoList = new ArrayList<>();
// 查找子级
for (Category category : categoryList) {
// 判断当前目录是否是子父级关系
if (categoryVo.getId().equals(category.getParentId())) {
subCategoryVoList.add(categoryTOCategoryVo(category));
}
// 递归调用,不管有几级菜单,都能够适用
findSubCategory(categoryList, subCategoryVoList);
// 类目显示排序,reversed 表示数字越大靠前
subCategoryVoList.sort(Comparator.comparing(CategoryVo::getSortOrder).reversed());
}
// 最后把查到的子级保存到一级目录中
categoryVo.setSubCategories(subCategoryVoList);
}
}
}
四、补充说明
一、小伙伴可能会有疑问,这样循环嵌套会不会影响效率
其实像这些循环操作来说,都是基于内存操作,内存操作是很快的,这点可以不用考虑。
二、需要避免的问题
其实对于这种数据的操作,最好一次性能够查询出来最好,商品目录最多也不过几千条。千万不要循环去查询数据库,这种是极度危险的行为。小编就在工作当中遇到有在循环一直去查询数据库的情况,如果不考虑效率可以这么操作。
扫描二维码关注公众号,回复:
10014568 查看本文章
好了,最后提醒大家,疫情期间,好好保护自己
不出门,就是对祖国做出最大的贡献哈哈哈 (老板要求去公司,俺也没办法,赚钱要紧,赚钱要紧) ~~~~