前言
在开发中,层级数据(树状结构)的获取往往可能是我们一大难点,我现在将自己获取的树状结构数据方法总结如下,希望能给有需要的小伙伴有所帮助!
一、测试数据准备
/*
Navicat Premium Data Transfer
Source Server : 本地MySQL-local
Source Server Type : MySQL
Source Server Version : 80100
Source Host : localhost:33306
Source Schema : test
Target Server Type : MySQL
Target Server Version : 80100
File Encoding : 65001
Date: 06/09/2023 11:21:45
*/
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for region
-- ----------------------------
DROP TABLE IF EXISTS `region`;
CREATE TABLE `region` (
`id` bigint(0) NOT NULL COMMENT '主键id',
`region_id` bigint(0) NULL DEFAULT NULL COMMENT '区域id',
`region_code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '区域编码',
`region_name` varchar(250) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '区域名称',
`parent_id` bigint(0) NULL DEFAULT NULL COMMENT '父节点id',
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `region_id`(`region_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '地区信息' ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of region
-- ----------------------------
INSERT INTO `region` VALUES (1, 10001, 'CODEA0001', '中国', 0);
INSERT INTO `region` VALUES (2, 10002, 'CODEB0001', '安徽省', 10001);
INSERT INTO `region` VALUES (3, 10003, 'CODEB0002', '黑龙江省', 10001);
INSERT INTO `region` VALUES (4, 10004, 'CODEB0003', '广东省', 10001);
INSERT INTO `region` VALUES (5, 10005, 'CODEC0001', '合肥市', 10002);
INSERT INTO `region` VALUES (6, 10006, 'CODEC0002', '淮北市', 10002);
INSERT INTO `region` VALUES (7, 10007, 'CODEC0003', '哈尔滨市', 10003);
INSERT INTO `region` VALUES (8, 10008, 'CODEC0004', '鹤岗市', 10003);
INSERT INTO `region` VALUES (9, 10009, 'CODEC0005', '广州市', 10004);
INSERT INTO `region` VALUES (10, 10010, 'CODEC0006', '深圳市', 10004);
INSERT INTO `region` VALUES (11, 10011, 'CODED0001', '龙华区', 10010);
INSERT INTO `region` VALUES (12, 10012, 'CODED0002', '南山区', 10010);
INSERT INTO `region` VALUES (13, 10013, 'CODED0003', '天河区', 10009);
SET FOREIGN_KEY_CHECKS = 1;
二、对应表数据java实体类
import lombok.Data;
import java.util.List;
/**
* @Project
* @Description
* @Author songwp
* @Date 2023/9/5 15:16
**/
@Data
public class Region{
private Long id;
private Long regionId;
private String regionCode;
private String regionName;
private Long parentId;
private List<Region> children;
}
三、利用<collection>标签实现;通过collection节点继续调用获取下级节点数据的方法进行循环调用
1、对应mapper的调用方法
import com.songwp.pojo.entity.Region;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* @Project
* @Description 在 持久层,我们只调用 getNodeTree 方法,parent_id = 0 代表顶级节点。 * 然后通过 collection 节点继续调用 getNextNodeTree 方法进行循环调用。
* @Author songwp
* @Date 2023/9/5 15:22
**/
@Mapper
public interface RegionMapper {
List<Region> getNodeTree();
}
2、对应mapper.xml的写法(重点)
- column 代表会拿父节点 region_id ,作为参数获取 region对象
- javaType 代表 children对象是个列表,其实可以省略不写
- ofType 用来区分 JavaBean 属性类型和集合包含的类型
- select 是用来执行循环哪个SQL
<?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.songwp.mapper.RegionMapper">
<sql id="Base_Column_List">
id,
region_id,
parent_id,
region_code,
region_name
</sql>
<resultMap id="BaseTreeResultMap" type="com.songwp.pojo.entity.Region">
<result property="id" column="id" jdbcType="BIGINT"/>
<result property="regionId" column="region_id" jdbcType="BIGINT"/>
<result property="regionCode" column="region_code" jdbcType="VARCHAR"/>
<result property="regionName" column="region_name" jdbcType="VARCHAR"/>
<result property="parentId" column="parent_id" jdbcType="BIGINT"/>
<collection column="region_id" property="children" javaType="java.util.ArrayList"
ofType="com.songwp.pojo.entity.Region" select="getNextNodeTree"/>
</resultMap>
<resultMap id="NextTreeResultMap" type="com.songwp.pojo.entity.Region">
<result property="id" column="id" jdbcType="BIGINT"/>
<result property="regionId" column="region_id" jdbcType="BIGINT"/>
<result property="regionCode" column="region_code" jdbcType="VARCHAR"/>
<result property="regionName" column="region_name" jdbcType="VARCHAR"/>
<result property="parentId" column="parent_id" jdbcType="BIGINT"/>
<collection column="region_id" property="children" javaType="java.util.ArrayList"
ofType="com.songwp.pojo.entity.Region" select="getNextNodeTree"/>
</resultMap>
<select id="getNextNodeTree" resultMap="NextTreeResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM region
WHERE parent_id = #{id}
</select>
<select id="getNodeTree" resultMap="BaseTreeResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM region
WHERE parent_id = 0
</select>
</mapper>
3、业务方法、控制层方法代码编写
/**
* @Project
* @Description Service-业务接口
* @Author songwp
* @Date 2023/9/5 15:35
**/
public interface RegionService {
List<Region> getRegionList();
}
------------------------------------------------------------------------------------------------------/**
* @Project
* @Description ServiceImpl-业务实现类
* @Author songwp
* @Date 2023/9/5 15:36
**/
@Service
public class RegionServiceImpl implements RegionService {
@Resource
private RegionMapper regionMapper;
@Override
public List<Region> getRegionList() {
return regionMapper.getNodeTree();
}
}
-----------------------------------------------------------------------------------------------------/**
* @Project
* @Description Controller-控制层
* @Author songwp
* @Date 2023/9/5 15:39
**/
@RestController
@RequestMapping(value = "region",produces = "application/json; charset=utf-8")
@Api(tags = "行政区划")
public class RegionController {
@Autowired
private RegionService regionService;
/**
* 查询行政区划数据
* @param
* @return
*/
@GetMapping(value = "/findRegionList")
@ApiOperation("查询行政区划数据")
public List<Region> findRegionList(){
return regionService.getRegionList();
}
}
4、具体调用结果如下:
[
{
"id": 1,
"regionId": 10001,
"regionCode": "CODEA0001",
"regionName": "中国",
"parentId": 0,
"children": [
{
"id": 2,
"regionId": 10002,
"regionCode": "CODEB0001",
"regionName": "安徽省",
"parentId": 10001,
"children": [
{
"id": 5,
"regionId": 10005,
"regionCode": "CODEC0001",
"regionName": "合肥市",
"parentId": 10002,
"children": []
},
{
"id": 6,
"regionId": 10006,
"regionCode": "CODEC0002",
"regionName": "淮北市",
"parentId": 10002,
"children": []
}
]
},
{
"id": 3,
"regionId": 10003,
"regionCode": "CODEB0002",
"regionName": "黑龙江省",
"parentId": 10001,
"children": [
{
"id": 7,
"regionId": 10007,
"regionCode": "CODEC0003",
"regionName": "哈尔滨市",
"parentId": 10003,
"children": []
},
{
"id": 8,
"regionId": 10008,
"regionCode": "CODEC0004",
"regionName": "鹤岗市",
"parentId": 10003,
"children": []
}
]
},
{
"id": 4,
"regionId": 10004,
"regionCode": "CODEB0003",
"regionName": "广东省",
"parentId": 10001,
"children": [
{
"id": 9,
"regionId": 10009,
"regionCode": "CODEC0005",
"regionName": "广州市",
"parentId": 10004,
"children": [
{
"id": 13,
"regionId": 10013,
"regionCode": "CODED0003",
"regionName": "天河区",
"parentId": 10009,
"children": []
}
]
},
{
"id": 10,
"regionId": 10010,
"regionCode": "CODEC0006",
"regionName": "深圳市",
"parentId": 10004,
"children": [
{
"id": 11,
"regionId": 10011,
"regionCode": "CODED0001",
"regionName": "龙华区",
"parentId": 10010,
"children": []
},
{
"id": 12,
"regionId": 10012,
"regionCode": "CODED0002",
"regionName": "南山区",
"parentId": 10010,
"children": []
}
]
}
]
}
]
}
]
四、利用Java递归调用构造树状数据
1、构造树节点的实体类
/**
* @Project
* @Description
* @Author songwp
* @Date 2023/9/7 14:43
**/
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class TreeNode {
private Long id;
private Long parentId;
private String name;
private List<TreeNode> children;
public TreeNode(long id, long parentId, String name) {
this.id=id;
this.parentId=parentId;
this.name=name;
}
}
2、构造树的工具类
/**
* @Project
* @Description
* @Author songwp
* @Date 2023/9/7 14:45
**/
public class TreeUtils {
public List<TreeNode> nodeList = new ArrayList<>();
/**
* 构造方法
* @param nodeList 将数据集合赋值给nodeList,即所有数据作为所有节点。
*/
public TreeUtils(List<TreeNode> nodeList){
this.nodeList = nodeList;
}
/**
* 获取需构建的所有根节点 这里默认父节点值为"0"作为根节点
* @return 所有根节点List集合
*/
public List<TreeNode> getRootNode(){
// rootNodeList 保存所有根节点(所有根节点的数据)
// treeNode:查询出的每一条数据(节点)
List<TreeNode> rootNodeList = nodeList.stream().filter(i->i.getParentId()==0).collect(Collectors.toList());
return rootNodeList;
}
/**
* 根据所有根节点进行构建树形结构
* @return 构建整棵树
*/
public List<TreeNode> buildTree(){
// treeNodes:保存一个顶级节点所构建出来的完整树形
// getRootNode():获取所有的根节点
List<TreeNode> treeNodes = getRootNode().stream()
.map(this::buildChildTree)
.collect(Collectors.toList());
return treeNodes;
}
/**
* 递归-----构建单个子树形结构
* @param pNode 根节点
* @return 整棵树
*/
public TreeNode buildChildTree(TreeNode pNode){
List<TreeNode> childTree = new ArrayList<TreeNode>();
// nodeList:所有节点集合(所有数据)
nodeList.stream()
.filter(i-> i.getParentId().equals(pNode.getId()))
.forEach(i -> childTree.add(buildChildTree(i)));
// 处理数据结束,即节点下没有任何节点,树形构建结束,设置树结果
pNode.setChildren(childTree);
return pNode;
}
}
3、持久层方法以及对应mapper.xml
## 返回字段
<sql id="Base_Column_List">
id,
region_id,
parent_id,
region_code,
region_name
</sql>
## 获取所有数据
<select id="selectNodeTree" resultMap="BaseTreeResultMap">
SELECT
<include refid="Base_Column_List"/>
FROM region
</select>
## mapper方法
List<Region> selectNodeTree();
4、业务方法及控制层方法
##业务方法-Service
List<TreeNode> getRegionTree();
## 业务方法具体实现-ServiceImpl
@Override
public List<TreeNode> getRegionTree() {
List<TreeNode> nodes = new ArrayList<>();
List<Region> nodeTree = regionMapper.selectNodeTree();
if (CollectionUtil.isNotEmpty(nodeTree)) {
for (Region region : nodeTree) {
TreeNode node = new TreeNode();
node.setId(region.getRegionId());
node.setName(region.getRegionName());
node.setParentId(region.getParentId());
nodes.add(node);
}
}
TreeUtils treeBuild = new TreeUtils(nodes);
nodes = treeBuild.buildTree();
return nodes;
}
## 控制层方法-Controller
/**
* 查询行政区划数据
* @param
* @return
*/
@GetMapping(value = "/getRegionTree")
@ApiOperation("查询行政区划数据-方法02")
public List<TreeNode> getRegionTree02(){
return regionService.getRegionTree();
}
5、具体调用结果如下:
[
{
"id": 10001,
"parentId": 0,
"name": "中国",
"children": [
{
"id": 10002,
"parentId": 10001,
"name": "安徽省",
"children": [
{
"id": 10005,
"parentId": 10002,
"name": "合肥市",
"children": []
},
{
"id": 10006,
"parentId": 10002,
"name": "淮北市",
"children": []
}
]
},
{
"id": 10003,
"parentId": 10001,
"name": "黑龙江省",
"children": [
{
"id": 10007,
"parentId": 10003,
"name": "哈尔滨市",
"children": []
},
{
"id": 10008,
"parentId": 10003,
"name": "鹤岗市",
"children": []
}
]
},
{
"id": 10004,
"parentId": 10001,
"name": "广东省",
"children": [
{
"id": 10009,
"parentId": 10004,
"name": "广州市",
"children": [
{
"id": 10013,
"parentId": 10009,
"name": "天河区",
"children": []
}
]
},
{
"id": 10010,
"parentId": 10004,
"name": "深圳市",
"children": [
{
"id": 10011,
"parentId": 10010,
"name": "龙华区",
"children": []
},
{
"id": 10012,
"parentId": 10010,
"name": "南山区",
"children": []
}
]
}
]
}
]
}
]
来源:https://www.cnblogs.com/songweipeng/p/17681897.html
没有回复内容