目录
前言
1.准备分类数据
2.递归原理
3.实现
4.结合mybatis查询
总结
相信大家在处理业务的时候经常会遇到分类数据,当面对这种情况时该如何处理呢?在这里我使用了两种方式解决:一种使用sql递归的方式,另一种是java代码方式处理(下一期)。
一、SQL递归
代码如下(示例):
DROP TABLE IF EXISTS `course_category`;
CREATE TABLE `course_category` (
`id` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '主键',
`name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '分类名称',
`label` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '分类标签默认和名称一样',
`parentid` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '0' COMMENT '父结点id(第一级的父节点是0,自关联字段id)',
`is_show` tinyint NULL DEFAULT NULL COMMENT '是否显示',
`orderby` int NULL DEFAULT NULL COMMENT '排序字段',
`is_leaf` tinyint NULL DEFAULT NULL COMMENT '是否叶子',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '课程分类' ROW_FORMAT = DYNAMIC;
-- ----------------------------
-- Records of course_category
-- ----------------------------
INSERT INTO `course_category` VALUES ('1-3-3', '.NET', '.NET', '1-3', 1, 3, 1);
INSERT INTO `course_category` VALUES ('1-3-4', 'Objective-C', 'Objective-C', '1-3', 1, 4, 1);
INSERT INTO `course_category` VALUES ('1-3-5', 'Go语言', 'Go语言', '1-3', 1, 5, 1);
INSERT INTO `course_category` VALUES ('1-4', '数据库', '数据库', '1', 1, 4, 0);
INSERT INTO `course_category` VALUES ('1-4-1', 'Oracle', 'Oracle', '1-4', 1, 1, 1);
INSERT INTO `course_category` VALUES ('1-4-2', 'MySQL', 'MySQL', '1-4', 1, 2, 1);
INSERT INTO `course_category` VALUES ('1-4-3', 'SQL Server', 'SQL Server', '1-4', 1, 3, 1);
INSERT INTO `course_category` VALUES ('1-4-4', 'DB2', 'DB2', '1-4', 1, 4, 1);
INSERT INTO `course_category` VALUES ('1-4-5', 'NoSQL', 'NoSQL', '1-4', 1, 5, 1);
INSERT INTO `course_category` VALUES ('1', '根结点', '根结点', '0', 1, 1, 0);
INSERT INTO `course_category` VALUES ('1-1', '前端开发', '前端开发', '1', 1, 1, 0);
INSERT INTO `course_category` VALUES ('1-1-1', 'HTML/CSS', 'HTML/CSS', '1-1', 1, 1, 1);
INSERT INTO `course_category` VALUES ('1-1-2', 'JavaScript', 'JavaScript', '1-1', 1, 2, 1);
INSERT INTO `course_category` VALUES ('1-1-3', 'jQuery', 'jQuery', '1-1', 1, 3, 1);
INSERT INTO `course_category` VALUES ('1-1-4', 'ExtJS', 'ExtJS', '1-1', 1, 4, 1);
INSERT INTO `course_category` VALUES ('1-1-5', 'AngularJS', 'AngularJS', '1-1', 1, 5, 1);
INSERT INTO `course_category` VALUES ('1-2', '移动开发', '移动开发', '1', 1, 2, 0);
INSERT INTO `course_category` VALUES ('1-2-1', '微信开发', '微信开发', '1-2', 1, 1, 1);
INSERT INTO `course_category` VALUES ('1-2-2', 'iOS', 'iOS', '1-2', 1, 2, 1);
INSERT INTO `course_category` VALUES ('1-2-3', '手游开发', '手游开发', '1-2', 1, 3, 1);
INSERT INTO `course_category` VALUES ('1-2-4', 'Swift', 'Swift', '1-2', 1, 4, 1);
INSERT INTO `course_category` VALUES ('1-2-5', 'Android', 'Android', '1-2', 1, 5, 1);
INSERT INTO `course_category` VALUES ('1-3', '编程开发', '编程开发', '1', 1, 3, 0);
INSERT INTO `course_category` VALUES ('1-3-1', 'C/C++', 'C/C++', '1-3', 1, 1, 1);
INSERT INTO `course_category` VALUES ('1-3-2', 'Java', 'Java', '1-3', 1, 2, 1);
表图如下所示:
要实现sql递归查询,首先要明白其原理。
递归查询原理:SQL Server中的递归查询是通过CTE(表表达式)来实现。至少包含两个子查询,第一个查询为定点成员(种子查询),种子查询只是作为一个根查询,用于递归的定位;第二个查询被称为递归查询, 这两个子查询可以通过 UNION、UNION ALL或UNION DISTINCT 连接在一起。
注意:RECURSIVE 关键字只在MySQL8+版本生效。种子查询只会执行一次,并得到初始的数据作为根子集,而递归查询没有显式的递归终止条件,只有当第二个递归查询重复执行(实现递归)直到没有新的行产生,返回空结果集或是超出了递归次数的最大限制时才停止递归。最终将所有的结果集都查询出来,这对于深层查询(如具有父子关系的查询)是非常有用的。
优点:效率高,大量数据集下,速度比程序的查询快。
SQL递归的常见形式:
WITH RECURSIVE CTE AS (
SELECT column1,column2... FROM tablename WHERE conditions
UNION ALL
SELECT column1,column2... FROM tablename
INNER JOIN CTE ON conditions
)
sql如下:
with recursive t1 as (
select * from course_category p where id= "1"
union all
select t.* from course_category t inner join t1 on t1.id = t.parentid
)
select * from t1 order by t1.id, t1.orderby
查询结果如下:
引入依赖如下:
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.3.12.RELEASE
com.example
testdemo
0.0.1-SNAPSHOT
testdemo
testdemo
1.8
mysql
mysql-connector-java
org.projectlombok
lombok
com.baomidou
mybatis-plus-boot-starter
3.5.1
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
src/main/java
**/*.xml
org.apache.maven.plugins
maven-compiler-plugin
3.8.1
1.8
UTF-8
org.springframework.boot
spring-boot-maven-plugin
yml配置:
spring: datasource: username: root password: root url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai driver-class-name: com.mysql.cj.jdbc.Driver# 注意这里的配置 我是把mapper映射放在java目录下 才这样写的 mybatis-plus: mapper-locations: com/example/testdemo/dao/*.xml
目录结构如下所示:
Course实体类:
@Data
@TableName(value = "course_category")
public class Course implements Serializable {
@TableField(value = "id")
private String id;
private String name;
private String label;
@TableField(value = "parentid")
private String parentId;
@TableField(value = "is_show")
private Integer isShow;
private Integer orderby;
@TableField(value = "is_leaf")
private Integer isLeaf;
@TableField(exist = false)
List courseList;
}
CourseMapper映射类:
@Mapper
public interface CourseMapper extends BaseMapper {
List selectTreeByRootId(String id);
}
CourseMapper.xml:
因为返回数据并不满足分类格式,所以 最后再把数据封装进Crouse就行 ,封装操作如下:
CourseServiceImpl类:
@Autowired
CourseMapper courseMapper;
@Override
public List selectTreeNode(String id){
List courseList = courseMapper.selectTreeByRootId(id);
List categoryTreeDtos = new ArrayList<>();
HashMap mapTemp = new HashMap<>();
courseList.stream().forEach(item->{
mapTemp.put(item.getId(),item);
//只将根节点的下级节点放入list
if(item.getParentId().equals("1")){
categoryTreeDtos.add(item);
}
Course fatherCourse = mapTemp.get(item.getParentId());
if(fatherCourse!=null){
if(fatherCourse.getCourseList() ==null){
fatherCourse.setCourseList(new ArrayList());
}
//向节点的下级节点list加入节点
fatherCourse.getCourseList().add(item);
}
});
return categoryTreeDtos;
}
测试:
@Autowired
CourseService courseService;
@Test
void context() {
List courseList = courseService.selectTreeNode("1");
System.out.println(courseList);
}
结果如图遍成功完成:
感谢大家的观看
此文章只是尝试练习递归查分类数据,新手上路,大家多多关照。