springboot项目中构建树结构数据返回

需要构建一个树结构返回(ID,PID),具体示例如下

1、创建实体数据表

-- 测试省份数据表
DROP TABLE IF EXISTS test_province;
CREATE TABLE IF NOT EXISTS test_province (
    id                  BIGINT(32) NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '表ID',
    pid                 BIGINT(32) COMMENT '父级ID',
    name                 VARCHAR(255) COMMENT '名称',
    code                 VARCHAR(255) COMMENT '邮政编号',
    type                 INTEGER      COMMENT '类型(1=省, 2=市, 3=区/县)',
    abbreviation         VARCHAR(255) COMMENT '简称',

    status        VARCHAR(10) COMMENT '状态(0=正常)' DEFAULT '0',
    remarks       VARCHAR(255) COMMENT '备注',
    create_by     VARCHAR(50) COMMENT '创建人',
    create_time   TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_by     VARCHAR(50) COMMENT '更新人',
    update_time   TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '测试省份数据表';

-- 修改字段名
ALTER TABLE test_province CHANGE code postal_code INT DEFAULT 000000 COMMENT '邮政编号';
ALTER TABLE test_province MODIFY COLUMN postal_code VARCHAR(255);
-- 如果是PostgreSQL,语法是:
--  ALTER TABLE test_province ALTER COLUMN postal_code TYPE VARCHAR(255);
-- 添加字段名
ALTER TABLE test_province ADD COLUMN area DECIMAL(10,2) DEFAULT NULL COMMENT '面积(保留2位小数)';
ALTER TABLE test_province ADD COLUMN area_code VARCHAR(255) DEFAULT NULL COMMENT '区号';

添加数据

INSERT INTO test_province (pid, name, postal_code, type, abbreviation, area, area_code) VALUES (0, '广东省', '0', 1, null, null, '020');
INSERT INTO test_province (pid, name, postal_code, type, abbreviation, area, area_code) VALUES (0, '北京市', '0', 1, null, null, '010');
INSERT INTO test_province (pid, name, postal_code, type, abbreviation, area, area_code) VALUES (1, '广州市', '0', 2, null, null, null);
INSERT INTO test_province (pid, name, postal_code, type, abbreviation, area, area_code) VALUES (1, '深圳市', '0', 2, null, null, '0755');
INSERT INTO test_province (pid, name, postal_code, type, abbreviation, area, area_code) VALUES (2, '朝阳区', '0', 3, null, null, null);
INSERT INTO test_province (pid, name, postal_code, type, abbreviation, area, area_code) VALUES (2, '海淀区', '0', 3, null, null, null);
INSERT INTO test_province (pid, name, postal_code, type, abbreviation, area, area_code) VALUES (3, '天河区', '0', 3, null, null, null);
INSERT INTO test_province (pid, name, postal_code, type, abbreviation, area, area_code) VALUES (4, '福田区', '0', 3, null, null, null);

2、创建一个实体

import lombok.Data;

import java.util.List;

@Data
public class Province {
    private Integer id;
    private Integer pid;
    private String name;
    private String postalCode;
    private Integer type;
    private String abbreviation;
    private Double area;
    private String areaCode;
    // 子级数据
    private List children;
}

3、编写代码(返回树结构方法)

/**
     * 构建父子关系的数据列表(树形结构)
     *
     * @param allData 已查询的所有数据列表
     * @param  数据类型
     * @return 父子关系结构化数据列表
     */
    public static  List buildHierarchicalData(List allData) {
        // 存储每个对象的ID到对象的映射
        Map dataMap = new HashMap<>();
        // 存储顶级节点
        List rootData = new ArrayList<>();
        // 遍历所有数据并放入 dataMap 中
        for (T item : allData) {
            try {
                // 通过反射获取 item 的 parentId 和 id
                Integer parentId = (Integer) item.getClass().getMethod("getPid").invoke(item);
                Integer id = (Integer) item.getClass().getMethod("getId").invoke(item);

                dataMap.put(id, item);

                // 如果是顶级节点,添加到 rootData 中
                if (parentId == 0) {
                    rootData.add(item);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        // 构建父子关系
        for (T item : allData) {
            try {
                Integer parentId = (Integer) item.getClass().getMethod("getPid").invoke(item);
                if (parentId != 0) {
                    T parent = dataMap.get(parentId);
                    if (parent != null) {
                        // 获取父节点的 children 列表并添加当前节点
                        List children = (List) parent.getClass().getMethod("getChildren").invoke(parent);
                        if (children == null) {
                            children = new ArrayList<>();
                            parent.getClass().getMethod("setChildren", List.class).invoke(parent, children);
                        }
                        children.add(item);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        // 返回顶级节点列表
        return rootData;
    }

4、编写接口

    // controller
    @GetMapping("/getProvinceListInfo")
    public ResponseEntity getProvinceListInfo() {
        return ResponseEntity.status(HttpStatus.OK).body(evModel3dInfoService.getProvinceListInfo());
    } 
  
// service
    List getProvinceListInfo();
    // impl
    @Override
    public List getProvinceListInfo() {
        // 新建返回数据列表
        List provinces = new ArrayList<>();
        // 查询数据库中的所有数据
        List allProvinces = evModel3dInfoMapper.getProvinceListInfo();
        // 构建父子关系
        for (Province province : allProvinces) {
            if (province.getPid() == 0) {
                // 顶级省份
                provinces.add(province);
            }
        }
        // 为每个省份添加子级数据
        for (Province parent : provinces) {
            List children = new ArrayList<>();
            for (Province child : allProvinces) {
                if (child.getPid().equals(parent.getId())) {
                    children.add(child);
                }
            }
            // 设置子级
            parent.setChildren(children);
        }
        return provinces;
    }

当然这个实现类,可以直接使用这个buildHierarchicalData方法实现:

    @Override
    public List getProvinceListInfo() {
        return buildHierarchicalData(evModel3dInfoMapper.getListInfo());
    }
    // mapper
    List getProvinceListInfo();
    // xml
    

最后调用返回结构:

[
    {
        "id": 1,
        "pid": 0,
        "name": "广东省",
        "postalCode": "0",
        "type": 1,
        "abbreviation": null,
        "area": null,
        "areaCode": "020",
        "children": [
            {
                "id": 3,
                "pid": 1,
                "name": "广州市",
                "postalCode": "0",
                "type": 2,
                "abbreviation": null,
                "area": null,
                "areaCode": null,
                "children": [
                    {
                        "id": 7,
                        "pid": 3,
                        "name": "天河区",
                        "postalCode": "0",
                        "type": 3,
                        "abbreviation": null,
                        "area": null,
                        "areaCode": null,
                        "children": null
                    }
                ]
            },
            {
                "id": 4,
                "pid": 1,
                "name": "深圳市",
                "postalCode": "0",
                "type": 2,
                "abbreviation": null,
                "area": null,
                "areaCode": "0755",
                "children": [
                    {
                        "id": 8,
                        "pid": 4,
                        "name": "福田区",
                        "postalCode": "0",
                        "type": 3,
                        "abbreviation": null,
                        "area": null,
                        "areaCode": null,
                        "children": null
                    }
                ]
            }
        ]
    },
    {
        "id": 2,
        "pid": 0,
        "name": "北京市",
        "postalCode": "0",
        "type": 1,
        "abbreviation": null,
        "area": null,
        "areaCode": "010",
        "children": [
            {
                "id": 5,
                "pid": 2,
                "name": "朝阳区",
                "postalCode": "0",
                "type": 3,
                "abbreviation": null,
                "area": null,
                "areaCode": null,
                "children": null
            },
            {
                "id": 6,
                "pid": 2,
                "name": "海淀区",
                "postalCode": "0",
                "type": 3,
                "abbreviation": null,
                "area": null,
                "areaCode": null,
                "children": null
            }
        ]
    }
]

至此,就可以实现快速的查询返回一个树结构的数据了!

扩展内容:根据前端传值如何查询任意一级以及下属内容

方法一:修改查询sql

WITH RECURSIVE hierarchy AS (
    -- 基础查询:找到深圳市的 ID
    SELECT 
        id,
        pid,
        name,
        postal_code,
        type,
        abbreviation,
        area,
        area_code
    FROM test_province
    WHERE name LIKE '%深圳市%'
    
    UNION ALL
    
    -- 递归查询:查找深圳市下的所有子节点(区县、街道乡镇)
    SELECT 
        t.id,
        t.pid,
        t.name,
        t.postal_code,
        t.type,
        t.abbreviation,
        t.area,
        t.area_code
    FROM test_province t
    INNER JOIN hierarchy h ON t.pid = h.id
)
-- 最终查询:输出整个树形结构
SELECT * 
FROM hierarchy
ORDER BY id;

方法二:修改方法

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.function.Function;



    public static  List buildHierarchicalData(List allData, Function filterCondition) {
        // 泛型类型检查
        if (allData == null || allData.isEmpty()) {
            throw new IllegalArgumentException("输入数据不能为空");
        }

        // 定义反射方法引用,避免多次获取
        Method getIdMethod;
        Method getPidMethod;
        Method setChildrenMethod;
        Method getChildrenMethod;
        try {
            getIdMethod = allData.get(0).getClass().getMethod("getId");
            getPidMethod = allData.get(0).getClass().getMethod("getPid");
            setChildrenMethod = allData.get(0).getClass().getMethod("setChildren", List.class);
            getChildrenMethod = allData.get(0).getClass().getMethod("getChildren");
        } catch (NoSuchMethodException e) {
            throw new IllegalStateException("输入数据类缺少必要的方法: getId, getPid, setChildren, getChildren", e);
        }

        // 存储每个对象的ID到对象的映射
        Map dataMap = new HashMap<>();
        // 存储符合条件的节点
        List filteredRootData = new ArrayList<>();

        // 遍历所有数据并放入 dataMap 中
        for (T item : allData) {
            try {
                Integer id = (Integer) getIdMethod.invoke(item);
                Integer parentId = (Integer) getPidMethod.invoke(item);

                dataMap.put(id, item);

                // 如果符合条件,添加到 filteredRootData 中
                if (filterCondition.apply(item)) {
                    filteredRootData.add(item);
                }
            } catch (Exception e) {
                handleReflectionError(e);
            }
        }

        // 构建父子关系
        for (T item : allData) {
            try {
                Integer parentId = (Integer) getPidMethod.invoke(item);
                if (parentId != 0 && dataMap.containsKey(parentId)) {
                    T parent = dataMap.get(parentId);
                    if (dataMap.containsKey((Integer) getIdMethod.invoke(item))) {
                        // 获取父节点的 children 列表并添加当前节点
                        List children = (List) getChildrenMethod.invoke(parent);
                        if (children == null) {
                            children = new ArrayList<>();
                            setChildrenMethod.invoke(parent, children);
                        }
                        children.add(item);
                    }
                }
            } catch (Exception e) {
                handleReflectionError(e);
            }
        }

        // 返回符合条件的顶级节点及其下属节点的列表
        return filteredRootData;
    }

    // 统一处理反射异常
    private static void handleReflectionError(Exception e) {
        e.printStackTrace(); // 或者记录日志
    }

调用示例:

    public List getProvinceListInfo(String name) {
        // 查询所有数据
        List provinceList = evModel3dInfoMapper.getListInfos();
        // 判断name是否为空
        if (name != null) {
            return buildHierarchicalData(provinceList,province -> name.equals(province.getName()));
        }else {
            return buildHierarchicalData(provinceList);
        }
    }

返回示例:

[
    {
        "id": 4,
        "pid": 1,
        "name": "深圳市",
        "postalCode": "0",
        "type": 2,
        "abbreviation": null,
        "area": null,
        "areaCode": "0755",
        "children": [
            {
                "id": 8,
                "pid": 4,
                "name": "福田区",
                "postalCode": "0",
                "type": 3,
                "abbreviation": null,
                "area": null,
                "areaCode": null,
                "children": [
                    {
                        "id": 9,
                        "pid": 8,
                        "name": "蛇口街道",
                        "postalCode": null,
                        "type": null,
                        "abbreviation": null,
                        "area": null,
                        "areaCode": null,
                        "children": null
                    }
                ]
            }
        ]
    }
]

至此,也至此根据条件查询任意一级及下属节点了。

你可能感兴趣的:(java,数据结构,数据处理,spring,boot,java,树结构)