后端实现树形结构
比如多级菜单,我们需要一个结构可以更好的展示。
我做了一个简单的多级菜单树形结构,使用的是递归的方式,想要多级也是可以的。
从数据库开始展示。
一、数据库设计
表结构需要包含类似pid的字段存储父id
CREATE TABLE `menu` (
`id` int(11) NOT NULL,
`pid` int(11) DEFAULT '0',
`menu_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
设计的表如图
*二、Controller层代码和Mapper层代码,我用的是mybatis-plus
Controller层代码和Mapper层代码很简单。
controller:
@RestController
@RequestMapping("/menu")
public class MenuController {
@Autowired
private MenuService menuService;
@PostMapping("/getMenuList")
public Result getMenuList(){
List<Map<String, Object>> menuList = menuService.getMenuList();
return Result.success(menuList);
}
}
Mapper:
@Mapper
public interface MenuMapper extends BaseMapper<Menu> {
}
三、逻辑放在了实现层
service:
public interface MenuService {
List<Map<String,Object>> getMenuList();
}
serviceImpl:
@Service
public class MenuServiceImpl implements MenuService {
@Resource
private MenuMapper menuMapper;
@Override
public List<Map<String, Object>> getMenuList() {
//从数据库中获取菜单数据
Wrapper<Menu> wrapper = Wrappers.<Menu>lambdaQuery();
List<Menu> menus = menuMapper.selectList(wrapper);
//创建一个集合存放属性数据
List<Map<String, Object>> menuList = new ArrayList<>();
//遍历先放入父节点
for (Menu menu : menus) {
Map<String, Object> map = new HashMap<>();
if (menu.getPid() == 0){
map.put("id",menu.getId());
map.put("pid",menu.getPid());
map.put("menuName",menu.getMenuName());
map.put("children",getChildren(menus, menu.getId()));
menuList.add(map);
}
}
return menuList;
}
/**
* 使用递归
*通过id获取子级
*/
public List<Map<String, Object>> getChildren(List<Menu> data, Integer id){
//新建结果集
List<Map<String, Object>> list = new ArrayList<>();
//非空判断
if (data == null || data.size() == 0 || id == null) {
return list;
}
//遍历,比较当前id和pid,一样的话说明当前id存在子级
for (Menu menu : data) {
Map<String, Object> map = new HashMap<>();
if (id.equals(menu.getPid())){
map.put("id", menu.getId());
map.put("pid", menu.getPid());
map.put("menuName", menu.getMenuName());
//递归查询子级
map.put("children", getChildren(data, menu.getId()));
list.add(map);
}
}
return list;
}
}
四、下面是我用postman测试的结果
{
"code": 0,
"msg": "成功",
"data": [
{
"children": [
{
"children": [],
"pid": 1,
"menuName": "菜单1-1",
"id": 3
},
{
"children": [],
"pid": 1,
"menuName": "菜单1-2",
"id": 4
}
],
"pid": 0,
"menuName": "菜单1",
"id": 1
},
{
"children": [
{
"children": [],
"pid": 2,
"menuName": "菜单2-1",
"id": 5
}
],
"pid": 0,
"menuName": "菜单2",
"id": 2
}
]
}
======================================================
我在开发过程中遇到并不确认根节点的情况,我直接把我的业务代码放了上去
@Override
public List<SysTypeInfoDTO> getDictionaryForTree(SysTypeInfoVo sysTypeInfoVo) {
//获取全部
// PageHelper.startPage(sysTypeInfoVo.getPageNum(), sysTypeInfoVo.getPageSize());
List<SysTypeInfoDTO> sysTypeInfos = sysTypeInfoMapper.selectList(sysTypeInfoVo);
Set<SysTypeInfoDTO> checkList = new HashSet<>();//1.定义了一个空集合
//实现多级树形结构的查询,但是并不确定根节点,只通过pid和id进行父级和子级的查询
if (sysTypeInfos!=null){
for (SysTypeInfoDTO sysTypeInfo : sysTypeInfos) {
for (SysTypeInfoDTO typeInfo : sysTypeInfos) {
if (typeInfo.getParentId().equals(sysTypeInfo.getTypeId())){
//递归查询
//存入递归完成的对象
checkList.add(typeInfo);
List<SysTypeInfoDTO> children = getChildren(sysTypeInfos, sysTypeInfo.getTypeId(), checkList);
sysTypeInfo.setTypeChildren(children.size()>0?children:null);
}
}
}
}
//直接删除已经存进的子级
sysTypeInfos.removeAll(checkList);
return sysTypeInfos.stream().sorted(Comparator.comparing(SysTypeInfoDTO::getSn).reversed()).collect(Collectors.toList());
}
/**
* 使用递归 通过id获取子级
*
* @param data 入参集合
* @param typeId 父级id
* @param checkList
* @return 树形集合
*/
public List<SysTypeInfoDTO> getChildren(List<SysTypeInfoDTO> data, String typeId, Set<SysTypeInfoDTO> checkList) {
//新建结果集
List<SysTypeInfoDTO> list = new ArrayList<>();
//非空判断
if (data == null || data.size() == 0 || typeId == null) {
return null;
}
//遍历,比较当前typeId和pid,一样的话说明当前id存在子级
for (SysTypeInfoDTO sysTypeInfo : data) {
if (typeId.equals(sysTypeInfo.getParentId())) {
if (checkList != null) {
//这里也存入
checkList.add(sysTypeInfo);
}
//递归查询子级
List<SysTypeInfoDTO> children = getChildren(data, sysTypeInfo.getTypeId(), checkList);
sysTypeInfo.setTypeChildren(children.size()>0?children:null);
list.add(sysTypeInfo);
}
}
return list;
}