三级分类实现

前言

使用工具:IDEA
使用技术栈:spring boot , mysql

建表sql:


CREATE TABLE `menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键递增',
  `name` varchar(255) DEFAULT NULL COMMENT '分类名称',
  `parent_id` int(11) DEFAULT NULL COMMENT '父节点id',
  `url` varchar(255) DEFAULT NULL COMMENT '分类链接',
  `icon` varchar(255) DEFAULT NULL COMMENT '分类图标',
  `t_order` int(11) DEFAULT NULL COMMENT '分类排序权重',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT;

INSERT INTO `menu` VALUES ('1', '新闻', '0', '/www/xinwen', 'xxx', '1');
INSERT INTO `menu` VALUES ('2', '每日日报', '1', '/www/meiriribao', 'xxx', '2');
INSERT INTO `menu` VALUES ('3', '每日晚报', '1', '/www/zaobao', 'xxx', '1');
INSERT INTO `menu` VALUES ('4', '河南日报', '2', '/www/henan', 'xxx', '2');
INSERT INTO `menu` VALUES ('5', '上海日报', '2', '/www/shanghai', 'xxx', '1');
INSERT INTO `menu` VALUES ('6', '南京日报', '2', '/www/nanjing', 'xxx', '3');
INSERT INTO `menu` VALUES ('7', '开封日报', '4', '/www/zhoukou', 'xxx', '2');
INSERT INTO `menu` VALUES ('8', '郑州日报', '4', '/www/zhenghzou', 'xxx', '3');

使用的maven依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.threeBody</groupId>
    <artifactId>threeBody</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>threeBody</name>
    <description>三级分类</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>

        <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

配置:application.properties

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/testjson
spring.datasource.username=
spring.datasource.password=

#控制台打印日志
logging.level.com.threebody.dao=debug

启动类

package com.threebody;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.threebody.dao")
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

项目结构

如下图所示:
在这里插入图片描述

代码部分

实体类: entity

package com.threebody.entity;

import lombok.Data;

import java.io.Serializable;
import java.util.List;

/**
 * (Menu)实体类
 *
 * @author makejava
 * @since 2022-02-02 15:15:18
 */
@Data
public class Menu implements Comparable<Menu>, Serializable {
    private static final long serialVersionUID = -30738037318430454L;
    /**
    * 主键递增
    */
    private Integer id;
    /**
    * 分类名称
    */
    private String name;
    /**
    * 父节点id
    */
    private Integer parentId;
    /**
    * 分类链接
    */
    private String url;
    /**
    * 分类图标
    */
    private String icon;
    /**
    * 分类排序权重
    */
    private Integer tOrder;

    //子菜单列表
    private List<Menu> children;

    @Override
    public int compareTo(Menu o) {
        if(this.tOrder != o.tOrder){
            return this.tOrder - o.tOrder;
        }
        return 0;
    }

}

dao层 :dao

package com.threebody.dao;

import com.threebody.entity.Menu;
import org.apache.ibatis.annotations.Param;
import java.util.List;

/**
 * (Menu)表数据库访问层
 *
 * @author makejava
 * @since 2022-02-02 15:15:19
 */
public interface MenuDao {

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    Menu queryById(Integer id);

    /**
     * 查询全部数据
     * @param offset 查询起始位置
     * @param limit 查询条数
     * @return
     */
    List<Menu> queryAllByLimit(Integer offset, Integer limit);

    /**
     * 三级分类
     * @return
     */
    List<Menu> threeBody();
}

mapper层

<?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.threebody.dao.MenuDao">

    <resultMap id="BaseResultMap" type="com.threebody.entity.Menu">
        <!--@Table menu-->
        <result property="id" column="id" jdbcType="INTEGER"/>
        <result property="name" column="name" jdbcType="VARCHAR"/>
        <result property="parentId" column="parent_id" jdbcType="INTEGER"/>
        <result property="url" column="url" jdbcType="VARCHAR"/>
        <result property="icon" column="icon" jdbcType="VARCHAR"/>
        <result property="tOrder" column="t_order" jdbcType="INTEGER"/>
    </resultMap>

    <!--查询单个-->
    <select id="queryById" resultMap="BaseResultMap">
        select
          id, name, parent_id, url, icon, t_order
        from testjson.menu
        where id = #{id}
    </select>

    <!--查询全部数据-->
    <select id="queryAllByLimit" resultMap="BaseResultMap">
        select
          *
        from testjson.menu
    </select>

    <!--三级分类-->
    <select id="threeBody" resultMap="BaseResultMap">
        select
          *
        from testjson.menu
    </select>

</mapper>

业务层:service

package com.threebody.service;

import com.threebody.entity.Menu;
import java.util.List;

/**
 * (Menu)表服务接口
 *
 * @author makejava
 * @since 2022-02-02 15:15:19
 */
public interface MenuService {

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    Menu queryById(Integer id);

    /**
     * 查询全部数据
     * @param offset 查询起始位置
     * @param limit 查询条数
     * @return
     */
    List<Menu> queryAllByLimit(Integer offset, Integer limit);

    /**
     * 三级分类
     * @param menu
     * @return
     */
    List<Menu> threeBody(Menu menu);
}

业务实现类: serviceImpl

package com.threebody.service.impl;

import com.threebody.entity.Menu;
import com.threebody.dao.MenuDao;
import com.threebody.service.MenuService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * (Menu)表服务实现类
 *
 * @author makejava
 * @since 2022-02-02 15:15:20
 */
@Service("menuService")
public class MenuServiceImpl implements MenuService {
    @Resource
    private MenuDao menuDao;

    /**
     * 通过ID查询单条数据
     *
     * @param id 主键
     * @return 实例对象
     */
    @Override
    public Menu queryById(Integer id) {
        return this.menuDao.queryById(id);
    }

    /**
     * queryAllByLimit
     * @param offset 查询起始位置
     * @param limit 查询条数
     * @return
     */
    @Override
    public List<Menu> queryAllByLimit(Integer offset, Integer limit) {
        return menuDao.queryAllByLimit(offset,limit);
    }

    /**
     * 三级分类
     * @param menu
     * @return
     */
    @Override
    public List<Menu> threeBody(Menu menu) {

        try {
            //查询出所有的菜单
            List<Menu> allMenu = menuDao.threeBody();

            //根节点存储
            List<Menu> rootMenu = new ArrayList<>();
            //根据传递的参数设置根节点
            if(menu != null && menu.getId()!= null){
                //父节点为传递的id为根节点
                for (Menu nav : allMenu) {
                    if(nav.getParentId().equals(menu.getId())){
                        rootMenu.add(nav);
                    }
                }
            }else {
                //父节点是0的,为根节点
                for (Menu nav : allMenu) {
                    if(nav.getParentId().equals(0)){
                        rootMenu.add(nav);
                    }
                }
            }
            // 根据Menu类的order排序
            Collections.sort(rootMenu);

            //为根节点设置子菜单,getChild是递归调用
            for (Menu nv : rootMenu) {
                //获取根节点下的所有子节点,使用getChild方法
                List<Menu> childList = getChild(nv.getId(),allMenu);
                //给根节点设置子节点
                nv.setChildren(childList);
            }

            return rootMenu;
        } catch (Exception e) {
            e.printStackTrace();
            return new ArrayList<>();
        }

    }

    private List<Menu> getChild(Integer id, List<Menu> allMenu) {
        //子菜单
        List<Menu> childList = new ArrayList<>();
        for (Menu nav : allMenu) {
            //遍历所有节点,将所有菜单的父id与传过来的根节点的id比较
            //相等说明:为该根节点的子节点
            if(nav.getParentId().equals(id)){
                childList.add(nav);
            }
        }
        //递归设置子节点
        for (Menu nav : childList) {
            nav.setChildren(getChild(nav.getId(),allMenu));
        }
        //排序
        Collections.sort(childList);
        //如果节点下没有子节点,返回一个空List(递归退出)
        if(childList.size() == 0){
            return new ArrayList<Menu>();
        }
        return childList;
    }

}

控制层:controller

package com.threebody.controller;

import com.threebody.entity.Menu;
import com.threebody.service.MenuService;
import com.threebody.utils.R;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

/**
 * (Menu)表控制层
 *
 * @author makejava
 * @since 2022-02-02 15:15:20
 */
@RestController
@RequestMapping("menu")
public class MenuController {
    /**
     * 服务对象
     */
    @Resource
    private MenuService menuService;

    /**
     * 通过主键查询单条数据
     *
     * @param id 主键
     * @return 单条数据
     */
    @GetMapping("selectOne")
    public R selectOne(Integer id) {
        Menu queryById = menuService.queryById(id);
        return R.ok().put("data",queryById);
    }

    /**
     * 查询全部数据
     * @param offset 查询起始位置
     * @param limit 查询条数
     * @return
     */
    @GetMapping("queryAllByLimit")
    public R queryAllByLimit(Integer offset, Integer limit){
        List<Menu> queryAllByLimit = menuService.queryAllByLimit(offset, limit);
        return R.ok().put("data",queryAllByLimit);
    }

    /**
     * 三级分类
     * @param menu
     * @return
     */
    @GetMapping("threeBody")
    public R threeBody(Menu menu){
        List<Menu> threeBody = menuService.threeBody(menu);
        return R.ok().put("data",threeBody);
    }
}

组件

package com.threebody.utils;


import java.util.HashMap;
import java.util.Map;

/**
 * 返回数据
 *
 * @author Mark sunlightcs@gmail.com
 */
public class R extends HashMap<String, Object> {
    private static final long serialVersionUID = 1L;

    public R() {
        put("code", 0);
        put("msg", "success");
    }

    public static R error(int code, String msg) {
        R r = new R();
        r.put("code", code);
        r.put("msg", msg);
        return r;
    }

    public static R ok(String msg) {
        R r = new R();
        r.put("msg", msg);
        return r;
    }

    public static R ok(Map<String, Object> map) {
        R r = new R();
        r.putAll(map);
        return r;
    }

    public static R ok() {
        return new R();
    }

    public R put(String key, Object value) {
        super.put(key, value);
        return this;
    }
    public  Integer getCode() {

        return (Integer) this.get("code");
    }

}

最后效果图:

localhost:8080/menu/selectOne?id=1
在这里插入图片描述
localhost:8080/menu/queryAllByLimit?offset=0&limit=2
在这里插入图片描述
localhost:8080/menu/threeBody
在这里插入图片描述

{
	"msg": "success",
	"code": 0,
	"data": [{
		"id": 1,
		"name": "新闻",
		"parentId": 0,
		"url": "/www/xinwen",
		"icon": "xxx",
		"children": [{
				"id": 3,
				"name": "每日晚报",
				"parentId": 1,
				"url": "/www/zaobao",
				"icon": "xxx",
				"children": [],
				"torder": 1
			},
			{
				"id": 2,
				"name": "每日日报",
				"parentId": 1,
				"url": "/www/meiriribao",
				"icon": "xxx",
				"children": [{
						"id": 5,
						"name": "上海日报",
						"parentId": 2,
						"url": "/www/shanghai",
						"icon": "xxx",
						"children": [],
						"torder": 1
					},
					{
						"id": 4,
						"name": "河南日报",
						"parentId": 2,
						"url": "/www/henan",
						"icon": "xxx",
						"children": [{
								"id": 7,
								"name": "开封日报",
								"parentId": 4,
								"url": "/www/zhoukou",
								"icon": "xxx",
								"children": [],
								"torder": 2
							},
							{
								"id": 8,
								"name": "郑州日报",
								"parentId": 4,
								"url": "/www/zhenghzou",
								"icon": "xxx",
								"children": [],
								"torder": 3
							}
						],
						"torder": 2
					},
					{
						"id": 6,
						"name": "南京日报",
						"parentId": 2,
						"url": "/www/nanjing",
						"icon": "xxx",
						"children": [],
						"torder": 3
					}
				],
				"torder": 2
			}
		],
		"torder": 1
	}]
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值