Bootstrap

关于数据菜单如何从后端获取数据

通常前端页面会设计一个树型类型的菜单,而这些菜单的内容又需要根据我们的业务而定,这些数据是动态变化的
在这里插入图片描述
那么我们的后端该如何设计数据库呢?
这里我们采用id-parentId的形式进行设计数据库
在这里插入图片描述
只要知道了一个跟结点的id,我们就可以根据这个id看成parentId去数据库中查询
where parentId=#{parentId}就可以查到一个结点的子节点,这样一层一层遍历。就可以遍历出整个树形结构。当然上面只是简单的说一下,具体的逻辑还要看下面的讲解。

如何设计数据库

首先每个结点需要需要一个唯一的i主键id。另外需要一个parentId记录该结点的父节点。这样我们可以在最开始获取根节点的所有id,在把根结点的id当作parentId递归进行查询,这样我们就可以获取所有的结点数据了。
下面是一个很简单的数据库设计,但是可以用于讲解。
在这里插入图片描述
我们主要关注的是id和parentId。titile相当于结点的名字。其他字段可以不用管了。
这里我们将这个树型结构列表看成课程列表,如下:
在这里插入图片描述
每一个title代表的是一个课程信息。

java类如何设计

首先我们需要在java中创建一个属性和该表字段一模一样的类(原因就不说了,做过java开发的应该都懂):

说明:下面使用了Lombok、Swagger、mybatis-plus 不懂可以省略跳过,对后面得讲解无影响

@Data
@EqualsAndHashCode(callSuper = false)
@TableName("edu_subject")
@ApiModel(value="Subject对象", description="课程科目")
public class Subject implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "课程类别ID")
      @TableId(value = "id", type = IdType.ASSIGN_ID)
    private String id;

    @ApiModelProperty(value = "类别名称")
    private String title;

    @ApiModelProperty(value = "父ID")
    private String parentId;

    @ApiModelProperty(value = "排序字段")
    private Integer sort;

    @ApiModelProperty(value = "创建时间")
      @TableField(fill = FieldFill.INSERT)
    private Date gmtCreate;

    @ApiModelProperty(value = "更新时间")
      @TableField(fill = FieldFill.INSERT_UPDATE)
    private Date gmtModified;

}

另外,与前端的数据进行交互,可能不会交互数据库中的全部字段,所以我们需要根据前端需要的数据再设计一个类,里面的属性为前端所需要的部分数据。我的设计如下:

/**
 * 前后端数据交互所使用的类
 * @author:zhengjian
 */
@Data
@ApiModel("课程树形列表")
public class SubjectQueryVo {

    @ApiModelProperty("课程表id")
    private String id;

    @ApiModelProperty("课程标题")
    private String title;

    @ApiModelProperty("课程排名")
    private Integer sort;

    @ApiModelProperty("课程子节点")
    private List<SubjectQueryVo> children;
}

该类放在vo包下,都是用来进行前后端数据传输的类。

可以看出这里多出了一个children属性,主要是存放自己的孩子结点的。可以把这里理解成数据结构中树的链式存储。

sql语句如何设计

在开发中通常会使用mybatis-plus,简单的sql逻辑可以直接调用mybatis-plus的api,但是对于复杂的逻辑还是需要我们在xml中编写sql语句
下面是根据parentId查询结点的代码

<mapper namespace="com.zj.service.edu.mapper.SubjectMapper">

    <select id="selectNestedListByParentId" parameterType="String" resultMap="nestedSubject">
        select id,sort,title from edu_subject where parent_id = #{parentId}
    </select>

    <resultMap id="nestedSubject" type="com.zj.service.edu.vo.SubjectQueryVo">
        <id column="id" property="id"></id>
        <result column="sort" property="sort"></result>
        <result column="title" property="title"></result>
        <collection property="children" column="id" select="selectNestedListByParentId"
        ofType="com.zj.service.edu.vo.SubjectQueryVo">

        </collection>
    </resultMap>

</mapper>

我直接贴出了代码:
这里使用到了mybatis的级联查询。
因为在SubjectQueryVo 中有List类型的children,我们就需要使用resultmap进行数据与字段的映射绑定了。在resultMap中我们设计好查询到的数据与SubjectQueryVo 的映射关系。另外resultMap的Collection就是用来处理List着中国情况的。
property用来指定SubjectQueryVo 中的属性
column表示执行此次Collection查询需要提供的查询变量
select表示执行查询Collection中每条数据需要执行的sql语句,我们上面的id就可以用作变量传入
ofType表示Collection中每个数据的数据类型是什么

我们这里相当于是递归查询了,所以select就是我们写的select语句

我们把根节点的parentId设置为0;
其他孩子结点的parentId设置为对应父节点id。
这样只要我们开始传入的id为0,就可以遍历整个树

下面看看service和Controller层的设计

service

//传入的parentId为0,这样就可以遍历整个结点树
 @Override
    public List<SubjectQueryVo> nestedList() {
        return subjectMapper.selectNestedListByParentId("0");
    }

controller

@ApiOperation("嵌套数据列表")
    @GetMapping("/nested-list")
    public Result nestedList(){
        List<SubjectQueryVo> subjectQueryVos = subjectService.nestedList();
        return Result.ok().data("items",subjectQueryVos);
    }

把vo对象传给前台,我们看看数据是怎么样的:

"items": [
      {
        "id": "1507540020762435586",
        "title": "后端开发",
        "sort": 0,
        "children": [
          {
            "id": "1507540020909236225",
            "title": "Java",
            "sort": 0,
            "children": []
          },
          {
            "id": "1507540021043453954",
            "title": "Python",
            "sort": 0,
            "children": []
          }
        ]
      },
      {
        "id": "1507540021110562818",
        "title": "前端开发",
        "sort": 0,
        "children": [
          {
            "id": "1507540021177671681",
            "title": "HTML/CSS",
            "sort": 0,
            "children": []
          },
          {
            "id": "1507540021244780546",
            "title": "JavaScript",
            "sort": 0,
            "children": []
          }
        ]
      },
      {
        "id": "1507540021311889409",
        "title": "数据库开发",
        "sort": 0,
        "children": [
          {
            "id": "1507540021374803970",
            "title": "mysql",
            "sort": 0,
            "children": []
          },
          {
            "id": "1507540021441912833",
            "title": "oracle",
            "sort": 0,
            "children": []
          }
        ]
      }
    ]

可以看出我们已经成功的遍历了整棵树了。

;