- 注重版权,转载请注明原作者和原文链接
- 作者: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工程
、Swagger
、RESTful接口风格
、MyBatis-Plus
整体目录结构如下
文章图片
二、问题需求 以文章分类为例(这里以二级分类举例,三级、四级甚至多级会了二级的之后可以自行思考)
博客项目中少不了文章分类,如下图所示,那么在数据库如何只使用一张表、一条SQL语句就能完美实现多级目录结构的存储和查询
文章图片
三、数据库设计 我们需要设计三个字段
字段名 | 类型 | 注释 |
---|---|---|
id | int | 这个不用说了吧,主键 |
name | varchar | 分类名称 |
parent_id | int | 指向父级分类的ID,如果是父级分类则填0,如果是子分类则填父级分类的ID |
文章图片
四、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
查询结果如下(所有分类结构):
文章图片
当然你也可以指定某个父级分类,查询它所有的子分类,只需要改变一下
where
的条件就行了文章图片
是不是就满足了我们的需求了呢,接下来我们在项目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)创建业务层
我这里把完整的增删改查全部放出来了,这是我之前做的博客项目,可自行删减service – 接口
R
这个类是统一结果返回类,前后端分析基本都是这样操作,网上也有很多模板
/**
* 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映射路径
文章图片
(2)设计模式 回顾一下刚刚的数据格式,有点像什么?细心的同学已经发现了,没错,是不是和
JSON
数据格式有点类似?将父级分类(Java、实战项目教学等)对应一个CategoryParentVo类
将每个父级分类的所有子分类对应一个List集合
每个CategoryParentVo有一个字分类的集合属性变量
看懂上面三句话就说明你已经掌握了,看不懂的结合创建Vo类看一下
文章图片
(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语句)】打开网页进行测试
文章图片
执行查询所有分类的接口,测试结果如下,ok完美获取数据
文章图片
文章图片
或者直接在浏览器访问请求路径测试
文章图片
- 都看到这里啦,点点赞呀
- 感谢阅读
推荐阅读
- SpringBoot教学|如何快速使用SpringBoot+Vue前后端分离实现echarts图形可视化(入门详细教程)
- SpringBoot教学|使用SpringBoot一小时快速搭建一个简单后台管理(增删改查)(超详细教程)
- Hadoop|如何在IDEA编译器中连接HDFS,运行MapReduce程序
- SpringBoot教学|使用SpringBoot一小时快速搭建一个简单后台管理(后端篇)
- SpringBoot+Vue实现用户管理(一)
- 前端工程师面试题|冲刺前端一线大厂面试题(react)持续更新中
- java|java byte转成int数组_Java任意长度byte数组转换为int数组的方法
- java|java byte 强转 int_Java byte[]转int如何实现
- java|java byte转int原理_java中int与byte数组互转代码详细分析