Table of contents
1. Prepare classification data
foreword
I believe that everyone often encounters classified data when dealing with business. How should we deal with this situation? Here I use two ways to solve it: one way to use sql recursion , and the other way to deal with java code (next issue).
1. SQL recursion
1. Prepare classification data
The code is as follows (example):
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);
表图如下所示:
2. The principle of recursion
To implement sql recursive query, we must first understand its principle.
Recursive query principle : Recursive query in SQL Server is implemented through CTE (table expression). Contains at least two subqueries, the first query is a fixed-point member (seed query), and the seed query is only used as a root query for recursive positioning; the second query is called a recursive query, and these two subqueries can be connected together through UNION, UNION ALL or UNION DISTINCT .
Note : The RECURSIVE keyword is only valid in MySQL8+ version. The seed query will only be executed once and get the initial data as the root subset, while the recursive query has no explicit recursive termination condition, and the recursion will only stop when the second recursive query is repeatedly executed (recursive) until no new rows are generated, an empty result set is returned, or the maximum limit of recursion times is exceeded. Finally, all the result sets are queried, which is very useful for deep queries (such as queries with parent-child relationships).
Advantages: high efficiency, under a large amount of data sets, the speed is faster than the query of the program.
Common forms of SQL recursion:
WITH RECURSIVE CTE AS (
SELECT column1,column2... FROM tablename WHERE conditions
UNION ALL
SELECT column1,column2... FROM tablename
INNER JOIN CTE ON conditions
)
3. Realize
The sql is as follows:
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
The query results are as follows:
4. Combined with mybatis query
The import dependencies are as follows:
<?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.3.12.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>testdemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>testdemo</name>
<description>testdemo</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<!-- xml放在java目录下-->
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
yml configuration:
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# Note that the configuration here is mybatis-plus: mapper-locations: com/example/testdemo/dao/*.xml after putting the mapper mapping in the java directory
The directory structure is as follows:
Course entity class:
@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<Course> courseList;
}
CourseMapper mapping class:
@Mapper
public interface CourseMapper extends BaseMapper<Course> {
List<Course> selectTreeByRootId(String id);
}
CourseMapper.xml:
<?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.example.testdemo.mapper.CourseMapper">
<select id="selectTreeByRootId" resultType="com.example.testdemo.entity.Course" parameterType="string">
with recursive t1 as (
select * from course_category p where id= #{id}
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
</select>
</mapper>
Because the returned data does not satisfy the classification format, it is enough to encapsulate the data into Crouse at the end. The encapsulation operation is as follows:
CourseServiceImpl class:
@Autowired
CourseMapper courseMapper;
@Override
public List<Course> selectTreeNode(String id){
List<Course> courseList = courseMapper.selectTreeByRootId(id);
List<Course> categoryTreeDtos = new ArrayList<>();
HashMap<String, Course> 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<Course>());
}
//向节点的下级节点list加入节点
fatherCourse.getCourseList().add(item);
}
});
return categoryTreeDtos;
}
test:
@Autowired
CourseService courseService;
@Test
void context() {
List<Course> courseList = courseService.selectTreeNode("1");
System.out.println(courseList);
}
The result is as shown in the figure:
Summarize
Thank you for watching
This article is just an attempt to practice recursively searching classified data. For beginners, please take care of me.