java|多级分类、菜单等的数据库设计(一张表),以及mybatis-plus的多级分类查询(一条SQL语句)

  • 注重版权,转载请注明原作者和原文链接
  • 作者:Yuan-Programmer的
  • 链接:https://blog.csdn.net/weixin_47971206/article/details/124199978
  • 进来的小伙伴点点赞呀

文章目录
    • 一、前言
    • 二、问题需求
    • 三、数据库设计
    • 四、SQL多级分类查询
    • 五、项目结构搭建
        • (1)创建实体类
        • (2)创建DAO
        • (3)创建业务层
            • service -- 接口
            • servieImpl -- 实现类
        • (4)控制层
    • 六、Vo对象
    • 七、MyBatis多级分类查询
        • (1)在resources目录下创建CategoryMapper.xml静态文件
        • (2)设计模式
        • (3)自定义返回类型模板(重点)
        • (4)编写SQL
    • 八、Swagger接口测试

一、前言 我们在项目开发过程,经常会遇到有文章分类分栏,菜单分类,视频分类等多级分类
那么这种多级分类我们在数据中又是如何设计的呢?在mybatis查询过程中又是如何多级查询的呢?
别着急,今年我们来解决这个需求,
当然,解决的方法有很多,这里我只介绍我自己使用的一种,有更好的方法可以评论区评论大家一起探讨
本次用到了 Maven工程SwaggerRESTful接口风格MyBatis-Plus
整体目录结构如下
java|多级分类、菜单等的数据库设计(一张表),以及mybatis-plus的多级分类查询(一条SQL语句)
文章图片

二、问题需求 以文章分类为例(这里以二级分类举例,三级、四级甚至多级会了二级的之后可以自行思考)
博客项目中少不了文章分类,如下图所示,那么在数据库如何只使用一张表、一条SQL语句就能完美实现多级目录结构的存储和查询
java|多级分类、菜单等的数据库设计(一张表),以及mybatis-plus的多级分类查询(一条SQL语句)
文章图片

三、数据库设计 我们需要设计三个字段
字段名 类型 注释
id int 这个不用说了吧,主键
name varchar 分类名称
parent_id int 指向父级分类的ID,如果是父级分类则填0,如果是子分类则填父级分类的ID
接下来我们在mysql中创建出来
java|多级分类、菜单等的数据库设计(一张表),以及mybatis-plus的多级分类查询(一条SQL语句)
文章图片

四、SQL多级分类查询 不知道大家都有没有用过 join 这个关键字呢,想必大家都挺少用到的吧,忘记的同学记得先去补补 join 的知识哦
采用 left join 左拼接的查询方式,完整的查询语句如下:
SELECT x.id AS parentId, x.name AS parentName, y.id AS childrenId ,y.name AS childrenName FROM xy_category AS x LEFT JOIN xy_category AS y ON y.parent_id = x.id WHERE x.parent_id = 0

查询结果如下(所有分类结构):
java|多级分类、菜单等的数据库设计(一张表),以及mybatis-plus的多级分类查询(一条SQL语句)
文章图片

当然你也可以指定某个父级分类,查询它所有的子分类,只需要改变一下 where 的条件就行了
java|多级分类、菜单等的数据库设计(一张表),以及mybatis-plus的多级分类查询(一条SQL语句)
文章图片

是不是就满足了我们的需求了呢,接下来我们在项目MyBatis-Plus中实现多级分类查询
查询SQL语句我们已经写出来了,难的是如何存储这样的结构数据,别着急慢慢来
五、项目结构搭建 先把完整结构创建完成,这个大家应该都很熟悉了吧
我这里使用的MyBatis-Plus,操作都差不多,用MyBatis也可以
(1)创建实体类
/** * FileName:Category * Author:小袁 * Date:2022/4/15 10:42 * Description: 分类实体 */ @Data @EqualsAndHashCode(callSuper = false) @Accessors(chain = true) // 调用Setting方法后 回传对象 public class Category {/** * 分类ID */ @TableId(value = "https://www.it610.com/article/id", type = IdType.AUTO) private Integer id; /** * 分类栏目名称 */ private String name; /** * 父级栏目 */ private Integer parent_id; }

(2)创建DAO
/** * FileName:CategoryMapper * Author:小袁 * Date:2022/4/15 10:52 * Description: 分类DAO */ @Repository public interface CategoryMapper extends BaseMapper {/** * 查询所有分类的目录结构 * @return */ List findCategoryList(); /** * 通过某个父级分类的ID查询该父级的所有子分类 * @param id * @return */ CategoryParentVo getCategoryById(Integer id); }

(3)创建业务层
我这里把完整的增删改查全部放出来了,这是我之前做的博客项目,可自行删减
R 这个类是统一结果返回类,前后端分析基本都是这样操作,网上也有很多模板
service – 接口
/** * FileName:CategoryService * Author:小袁 * Date:2022/4/15 10:52 * Description: 分类栏目 Service */ public interface CategoryService extends IService {/** * 新增分类栏目数据 * @param category * @return */ R insert(Category category); /** * 根据分类栏目的ID进行修改数据 * @param category * @return */ R modify(Category category); /** * 根据分类栏目的ID进行删除 * @param id * @return */ R remove(Integer id); /** * 通过某个父级分类的ID查询该父级的所有子分类 * @param id * @return */ R getCategoryById(Integer id); /** * 查询所有分类的目录结构 * @return */ R listCategory(); }

servieImpl – 实现类
/** * FileName:CategoryServiceImpl * Author:小袁 * Date:2022/4/15 10:54 * Description: 分类栏目的实现类 */ @Service @Transactional public class CategoryServiceImpl extends ServiceImpl implements CategoryService {@Autowired private CategoryMapper categoryMapper; @Override public R insert(Category category) { return categoryMapper.insert(category) == 0 ? R.error() : R.ok(); }@Override public R modify(Category category) { return categoryMapper.updateById(category) == 0 ? R.error() : R.ok(); }@Override public R remove(Integer id) { QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("id", id); wrapper.or(); wrapper.eq("parent_id", id); return categoryMapper.delete(wrapper) == 0 ? R.error() : R.ok(); }@Override public R getCategoryById(Integer id) { CategoryParentVo categoryParentVo = categoryMapper.getCategoryById(id); return categoryParentVo == null ? R.error() : R.ok().data("category", categoryParentVo); }@Override public R listCategory() { return R.ok().data("categoryList", categoryMapper.findCategoryList()); } }

(4)控制层 这里我也是将所有的代码贴出来,自行需要删除
采用RESTful的接口风格,@Api相关的注解是 Swagger 的相关配置,用于接口测试,你用其他测试方法可以把这个删掉
/** * FileName:CategoryController * Author:小袁 * Date:2022/4/15 10:54 * Description: 分类栏目的控制层 */ @RestController @RequestMapping("/category") @Api(tags = "分类栏目控制层") public class CategoryController {@Autowired private CategoryService categoryService; /** * 新增分类栏目数据 * @param category * @return */ @PostMapping public R insertCategory(@RequestBody Category category) { return categoryService.insert(category); }/** * 根据ID删除分类栏目 * @param id * @return */ @DeleteMapping("{id}") @ApiOperation(value = "https://www.it610.com/article/根据ID删除所有子分类栏目(包括父级分类如果有)") public R removeCategoryById(@PathVariable(value = "https://www.it610.com/article/id") Integer id) { return categoryService.remove(id); }@PutMapping public R modifyCategoryById(@RequestBody Category category) { return categoryService.modify(category); }/** * 查询所有分类的目录结构 * @return */ @GetMapping @ApiOperation(value = "https://www.it610.com/article/查询所有分类的目录结构") public R getCategoryList() { return categoryService.listCategory(); }/** * 根据ID获取对象 * @param id * @return */ @GetMapping("{id}") @ApiOperation(value = "https://www.it610.com/article/根据ID获取对象") public R getCategoryById(@PathVariable(value = "https://www.it610.com/article/id") Integer id) { return categoryService.getCategoryById(id); } }

六、Vo对象
一会接收SQL多级查询结果要用到的,也叫视图对象(View Object),返回给前端看的
父级分类
/** * FileName:CategoryVo * Author:小袁 * Date:2022/4/15 14:16 * Description: */ @Data public class CategoryParentVo {// 父级分类编号ID private Integer parentId; // 父级分类名称 private String parentName; // 子分类 private List childrenCategory; }

子分类
/** * FileName:CategoryChildrenVo * Author:小袁 * Date:2022/4/15 14:18 * Description: 子分类 */ @Data public class CategoryChildrenVo {// ID编号 private Integer childrenId; // 分类栏目名称 private String childrenName; }

七、MyBatis多级分类查询 (1)在resources目录下创建CategoryMapper.xml静态文件 用来写SQL语句的
别忘了在 application 加上mapper映射路径
java|多级分类、菜单等的数据库设计(一张表),以及mybatis-plus的多级分类查询(一条SQL语句)
文章图片


(2)设计模式 回顾一下刚刚的数据格式,有点像什么?细心的同学已经发现了,没错,是不是和 JSON 数据格式有点类似?
将父级分类(Java、实战项目教学等)对应一个CategoryParentVo类
将每个父级分类的所有子分类对应一个List集合
每个CategoryParentVo有一个字分类的集合属性变量
看懂上面三句话就说明你已经掌握了,看不懂的结合创建Vo类看一下
java|多级分类、菜单等的数据库设计(一张表),以及mybatis-plus的多级分类查询(一条SQL语句)
文章图片

(3)自定义返回类型模板(重点)
MyBatis的知识哦,不知道大伙忘了没?

(4)编写SQL 完整的CategoryMapper.xml
id="findCategoryList" resultMap="categoryMap"> select x.id as parentId, x.name as parentName, y.id as childrenId ,y.name as childrenName from xy_category x left join xy_category as y on y.parent_id = x.id where x.parent_id = 0 id="getCategoryById" parameterType="int" resultMap="categoryMap"> select x.id as parentId, x.name as parentName, y.id as childrenId ,y.name as childrenName from xy_category x left join xy_category as y on y.parent_id = x.id where x.id = #{id}

八、Swagger接口测试 我这个项目整合了 Swagger 进行接口测试,网上很多整合的教程,只需要加一个配置类就搞定了,一分钟就行,我这里附上吧
依赖
io.springfox springfox-swagger2 2.7.0 io.springfox springfox-swagger-ui 2.7.0

/** * FileName:SwaggerConfig * Author:小袁 * Date:2022/3/11 19:09 * Description: */ @Configuration @EnableSwagger2 public class SwaggerConfig {@Bean public Docket webApiConfig(){return new Docket(DocumentationType.SWAGGER_2) .groupName("webApi") .apiInfo(webApiInfo()) .select() .paths(Predicates.not(PathSelectors.regex("/admin/.*"))) .paths(Predicates.not(PathSelectors.regex("/error.*"))) .build(); }private ApiInfo webApiInfo(){return new ApiInfoBuilder() .title("Swagger接口测试") .description("小袁同学") .version("1.0") .contact(new Contact("Helen", "http://www.baidu.com", "1971788445@qq.com")) .build(); } }

【java|多级分类、菜单等的数据库设计(一张表),以及mybatis-plus的多级分类查询(一条SQL语句)】打开网页进行测试
java|多级分类、菜单等的数据库设计(一张表),以及mybatis-plus的多级分类查询(一条SQL语句)
文章图片

执行查询所有分类的接口,测试结果如下,ok完美获取数据
java|多级分类、菜单等的数据库设计(一张表),以及mybatis-plus的多级分类查询(一条SQL语句)
文章图片
java|多级分类、菜单等的数据库设计(一张表),以及mybatis-plus的多级分类查询(一条SQL语句)
文章图片

或者直接在浏览器访问请求路径测试
java|多级分类、菜单等的数据库设计(一张表),以及mybatis-plus的多级分类查询(一条SQL语句)
文章图片

  • 都看到这里啦,点点赞呀
  • 感谢阅读

    推荐阅读