SpringBoot獲取樹狀結構數據
前言
在開發中,層級數據(樹狀結構)的獲取往往可能是我們一大難點,我現在將自己獲取的樹狀結構數據方法總結如下,希望能給有需要的小伙伴有所幫助!
一、測試數據準備
/* 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": [] } ] } ] } ] } ]
古今成大事者,不唯有超世之才,必有堅韌不拔之志!

浙公網安備 33010602011771號