需要构建一个树结构返回(ID,PID),具体示例如下
-- 测试省份数据表
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);
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;
}
/**
* 构建父子关系的数据列表(树形结构)
*
* @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;
}
// controller
@GetMapping("/getProvinceListInfo")
public ResponseEntity
// 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
}
]
}
]
}
]
至此,也至此根据条件查询任意一级及下属节点了。