springboot怎样构造树形结构数据并查询?

spring 专栏收录该内容
4 篇文章 0 订阅

因为项目需要,页面上需要树形结构的数据进行展示(类似下图这样),因此需要后端返回相应格式的数据。在这里插入图片描述
不说废话,直接开干!!!

我这里用的是springboot+mybatis-plus+mysql,示例的接口是查询一级权限以及二级权限、三级权限整个权限树…

下面是导入的maven依赖

 		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
<!--数据库连接-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.21</version>
        </dependency>
 
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!--mybatis增强工具-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.0</version>
        </dependency>
  		<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.0.6</version>
        </dependency>

下面是实体类Permission

@Data
public class Permission implements Serializable {
    @TableId
    private String permissionId;
    @NotNull(message = "权限名称不能为空")
    private String permissionName;
    /**
     * 权限标识
     */
    @NotNull(message = "权限标识不能为空")
    private String permissionCode;
    /**
     * 父菜单ID,如果是-1就表示是一级权限菜单。
     */
    @NotBlank(message = "父菜单ID不能为空")
    private String parentId;

    /**
     * 前端URL访问接口路径
     */
    private String path;

    /**
     * 排序值
     */
    private Integer sort;
    /**
     * 创建时间
     */

    private LocalDateTime createTime;

    /**
     * 更新时间
     */

    private LocalDateTime updateTime;

    /**
     * 0--正常 1--删除
     */
    private String delFlag;

    public Permission() {
        this.permissionId = IdUtil.simpleUUID();
    }

树形结点类

@Data
public class TreeNode {
    protected String id;
    protected String parentId;
    protected List<TreeNode> children = new ArrayList<TreeNode>();
    protected boolean hasChildren;

   public void addTreeNode(TreeNode node){
       children.add(node);
   }
}

树形结点详细信息类

@Data
@EqualsAndHashCode(callSuper = true)
public class PermissionTree extends TreeNode implements Serializable {
    private String permissionName;
    private String permissionCode;
    private String path;
    private Integer sort;
    private String label;
    private boolean hasChildren;
    public PermissionTree() {
    }
}

构建树形结点工具类(关键),在这里我用@UtilityClass注解就表示这个类中的方法都是静态方法:

@UtilityClass
public class TreeUtil {


    public <T extends TreeNode> List<T> build(List<T> treeNodes, String root) {
        List<T> trees = new ArrayList<>();
        for (T treeNode : treeNodes) {

            if (root.equals(treeNode.getParentId())) {
                trees.add(treeNode);
            }

            for (T node : treeNodes) {
                if (node.getParentId().equals(treeNode.getId())) {
                    treeNode.addTreeNode(node);
                    treeNode.setHasChildren(true);

                }
            }
        }
        return trees;
    }


    /**
     * 通过permission创建树形节点
     *
     * @param permissionList
     * @param root
     * @return
     */
    public List<PermissionTree> buildTree(List<Permission> permissionList, String root) {
        System.out.println(Arrays.toString(permissionList.toArray()));
        List<PermissionTree> treeNodeList = new ArrayList<>();
        PermissionTree treeNode = null;
        for (Permission permission : permissionList) {
            treeNode = new PermissionTree();
            treeNode.setId(permission.getPermissionId());
            treeNode.setPermissionName(permission.getPermissionName());
            treeNode.setPath(permission.getPath());
            treeNode.setSort(permission.getSort());
            treeNode.setParentId(permission.getParentId());
            treeNode.setLabel(permission.getPermissionName());
            treeNode.setHasChildren(false);
            treeNodeList.add(treeNode);
        }
        return TreeUtil.build(treeNodeList, root);
    }
  }

响应消息主体类

/**
 * 响应信息主体
 *
 * @param <T>
 */
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class R<T> implements Serializable {

	private static final long serialVersionUID = 1L;

	private int code;

	private String msg;

	private T data;

	public int getCode() {
		return code;
	}

	public void setCode(int code) {
		this.code = code;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}

	public T getData() {
		return data;
	}

	public void setData(T data) {
		this.data = data;
	}

	public static <T> R<T> ok() {
		return restResult(null, CommonConstants.SUCCESS, CommonConstants.MSG_SUCCESS);
	}

	public static <T> R<T> ok(T data) {
		return restResult(data, CommonConstants.SUCCESS, CommonConstants.MSG_SUCCESS);
	}

	public static <T> R<T> ok(T data, String msg) {
		return restResult(data, CommonConstants.SUCCESS, msg);
	}

	public static <T> R<T> failed() {
		return restResult(null, CommonConstants.FAIL, null);
	}

	public static <T> R<T> failed(String msg) {
		return restResult(null, CommonConstants.FAIL, msg);
	}

	public static <T> R<T> failed(T data) {
		return restResult(data, CommonConstants.FAIL, null);
	}

	public static <T> R<T> failed(T data, String msg) {
		return restResult(data, CommonConstants.FAIL, msg);
	}

	private static <T> R<T> restResult(T data, int code, String msg) {
		R<T> apiResult = new R<>();
		apiResult.setCode(code);
		apiResult.setData(data);
		apiResult.setMsg(msg);
		return apiResult;
	}

}

数据查询接口mapper类

@Mapper
public interface PermissionMapper extends BaseMapper<Permission>{
    
}

数据逻辑处理业务接口

public interface PermissionService extends IService<Permission> {
  

    /**
     * 构建权限树
     *
     * @param lazy
     * @param parentId
     * @return
     */
    List<PermissionTree> treePermission(boolean lazy, String parentId);
}

数据逻辑处理业务接口实现类

@Service
public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permission> implements PermissionService {
/**
     * 构建权限树:1、不是懒加载情况,查询全部
     * 2、是懒加载,根据parentId查询
     *
     * @param lazy
     * @param parentId
     * @return
     */

    @Override
    public List<PermissionTree> treePermission(boolean lazy, String parentId) {

        if (!lazy) {
            return TreeUtil.buildTree(
                    baseMapper.selectList(Wrappers.<Permission>lambdaQuery().orderByAsc(Permission::getSort)),
                    CommonConstants.PERMISSION_ROOT_ID);
        }
        String parent = parentId == null ? CommonConstants.PERMISSION_ROOT_ID : parentId;
        return TreeUtil.buildTree(
                baseMapper.selectList(Wrappers.<Permission>lambdaQuery().eq(Permission::getParentId, parent).orderByAsc(Permission::getSort)), parent
        );
    }
}

查询权限树请求接口类

@RestController
@RequestMapping("/permission")
public class PermissionController {
    @Autowire
    private PermissionService permissionService;

    /**
     * 查询权限列表,并以树状结构显示
     *
     * @param lazy 懒加载: false时, parentId这个参数失效, 加载所有的权限; true时, 根据parentId加载
     * @param parentId
     * @return
     */
    @RequestMapping(value = "/getTree", method = RequestMethod.GET)
    public R getTree(boolean lazy, String parentId) {
        return R.ok(permissionService.treePermission(lazy, parentId));
    }
 }

表中测试数据如下(注意它的parent_id
在这里插入图片描述

测试一:不是懒加载,查询整个权限树。 结果如下。
在这里插入图片描述

{
    "code": 0,
    "msg": "SUCCESS",
    "data": [
        {
            "id": "1",
            "parentId": "-1",
            "children": [
                {
                    "id": "2",
                    "parentId": "1",
                    "children": [
                        {
                            "id": "3",
                            "parentId": "2",
                            "children": [],
                            "hasChildren": false,
                            "permissionName": "update",
                            "permissionCode": null,
                            "path": null,
                            "sort": 3,
                            "label": "update",
                            "owned": false
                        },
                        {
                            "id": "4",
                            "parentId": "2",
                            "children": [],
                            "hasChildren": false,
                            "permissionName": "insert_role",
                            "permissionCode": null,
                            "path": null,
                            "sort": 4,
                            "label": "insert_role",
                            "owned": false
                        }
                    ],
                    "hasChildren": true,
                    "permissionName": "delete",
                    "permissionCode": null,
                    "path": null,
                    "sort": 2,
                    "label": "delete",
                    "owned": false
                }
            ],
            "hasChildren": true,
            "permissionName": "add",
            "permissionCode": null,
            "path": null,
            "sort": 1,
            "label": "add",
            "owned": false
        },
        {
            "id": "5",
            "parentId": "-1",
            "children": [],
            "hasChildren": false,
            "permissionName": "role:saveRole",
            "permissionCode": null,
            "path": "/role/saveRole",
            "sort": 5,
            "label": "role:saveRole",
            "owned": false
        }
    ]
}

测试二:是懒加载,根据parent_id查询当前分支。 结果如下。
在这里插入图片描述

{
    "code": 0,
    "msg": "SUCCESS",
    "data": [
        {
            "id": "3",
            "parentId": "2",
            "children": [],
            "hasChildren": false,
            "permissionName": "update",
            "permissionCode": null,
            "path": null,
            "sort": 3,
            "label": "update",
            "owned": false
        },
        {
            "id": "4",
            "parentId": "2",
            "children": [],
            "hasChildren": false,
            "permissionName": "insert_role",
            "permissionCode": null,
            "path": null,
            "sort": 4,
            "label": "insert_role",
            "owned": false
        }
    ]
}
  • 0
    点赞
  • 5
    评论
  • 10
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

评论 5 您还未登录,请先 登录 后发表或查看评论
<div> 适用人群 <p> Java开发人员,Vue开发人员,前后端分离开发人员,权限管理和配置开发人员 </p> </div> <div> 课程概述 <div style=&quot;color:#666666;&quot;> 【讲师介绍】<br /> 讲师职称:<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;现某知名大型互联网公司资深架构师,技术总监,职业规划师,首席面试官,曾在某上市培训机构,高校任教多年。<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Array(Array老师)10多年互联网公司实战经验,知名的大型互联网公司的架构师,高管等职,在企业长期从事于技术的源码阅读和新技术的研究;擅长于职业规划,面试辅导,从事面试官多年;擅长于JAVA,人工智能AI应用,Xmind等等,曾服役于国内某上市培训机构数年,独特的培训思路,培训体系,培训方式,实践的职场技能,职场现状,职场晋升等让你快速适应企业职场的所需。&nbsp;<br /> 【课程介绍】<br /> 技术选型<br /> 开发环境:Eclipse/Idea&nbsp;,JDK&nbsp;1.8以上&nbsp;<br /> 后端技术<br /> 核心框架:SpringBoot2.x框架系列(同样适用Springcloud&nbsp;F版本以后的版本),如下(节选):&nbsp;&nbsp;&nbsp;&nbsp;<br /> 持久层框架:MyBatis&nbsp;3.x&nbsp;+&nbsp;Mybatis-plus&nbsp;3.x<br /> 日志管理:SLF4J&nbsp;1.7&nbsp;+&nbsp;Log4j2&nbsp;2.7<br /> 工具类:Apache&nbsp;Commons、Jackson&nbsp;、fastjson、Gson<br /> 权限验证<br /> 前端技术&nbsp;&nbsp;<br /> Vue&nbsp;&nbsp;<br /> Vue-cli<br /> ElementUI&nbsp;---https://element.eleme.io/<br /> JSX&nbsp;(JavaScript&nbsp;Xml)<br /> 前台的权限验证和路由设置<br /> 开发模式&nbsp;&nbsp;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;前后端分离的开发<br /> 数据库&nbsp;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Mysql5<br /> IDE<br /> &nbsp;&nbsp;&nbsp;&nbsp;Intellij&nbsp;Idea </div> </div>
©️2022 CSDN 皮肤主题:博客之星2020 设计师:CSDN官方博客 返回首页

打赏作者

qzxl

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值