
比如把这种平层数据转化为下面这种树形结构树
json
[
{
"id": 2,
"parentId": null,
"name": "有声书",
"type": "category",
"children": [
{
"id": 1,
"parentId": 2,
"name": "有声书分类",
"type": "attribute",
"children": [
{"id": 1, "name": "男频小说", "type": "value"},
{"id": 2, "name": "女频小说", "type": "value"}
]
}
]
}
]
方法1:非递归建树
首先创建一个实体类,作为返回给前端的json结构
java
@Data
class TreeNode {
private Long id;
private Long parentId;
private String name;
private List<TreeNode> children;
}
核心方法
java
/**
* 核心:三层平层数据 转 树形结构
* @param flatList 平层数据(分类+属性+属性值)
* @return 树形数据
*/
public List<TreeNode> buildTree(List<FlatData> flatList) {
// 1. 核心容器:key=节点ID,value=节点(自动去重)
Map<Long, TreeNode> nodeMap = new HashMap<>();
// 2. 遍历平层数据,封装三层节点
for (FlatData data : flatList) {
// 分类(第一层)
buildNode(nodeMap, data.getCategoryId(), null, data.getCategoryName());
// 属性(第二层,父=分类ID)
buildNode(nodeMap, data.getAttributeId(), data.getCategoryId(), data.getAttributeName());
// 属性值(第三层,父=属性ID)
buildNode(nodeMap, data.getValueId(), data.getAttributeId(), data.getValueName());
}
// 3. 核心挂载:子节点 → 父节点 children
for (TreeNode node : nodeMap.values()) {
Long parentId = node.getParentId();
if (parentId != null && nodeMap.containsKey(parentId)) {
nodeMap.get(parentId).getChildren().add(node);
}
}
// 4. 返回根节点(parentId=null)
return nodeMap.values().stream()
.filter(node -> node.getParentId() == null)
.toList();
}
// 通用节点构建(去重)
private void buildNode(Map<Long, TreeNode> map, Long id, Long parentId, String name) {
if (id == null || map.containsKey(id)) return;
TreeNode node = new TreeNode();
node.setId(id);
node.setParentId(parentId);
node.setName(name);
node.setChildren(new ArrayList<>());
map.put(id, node);
}
2.分组嵌套
很简单的一种写法,就是层级需要固定
java
// 1. 查询平层宽表数据
List<FlatAttributeDTO> flatList = attributeMapper.selectFlatData(category1Id);
List<JSONObject> result = new ArrayList<>();
// 第一层:按 一级分类ID 分组
Map<Long, List<FlatAttributeDTO>> groupByCategory = flatList.stream()
.collect(Collectors.groupingBy(FlatAttributeDTO::getCategory1Id));
// 组装一级分类节点
groupByCategory.forEach((categoryId, categoryList) -> {
JSONObject categoryNode = new JSONObject();
categoryNode.put("id", categoryId);
categoryNode.put("name", categoryList.get(0).getCategory1Name());
List<JSONObject> attributeList = new ArrayList<>();
// 第二层:组内按 属性ID 分组
Map<Long, List<FlatAttributeDTO>> groupByAttribute = categoryList.stream()
.collect(Collectors.groupingBy(FlatAttributeDTO::getAttributeId));
// 组装属性节点
groupByAttribute.forEach((attributeId, attributeListData) -> {
JSONObject attributeNode = new JSONObject();
attributeNode.put("id", attributeId);
attributeNode.put("name", attributeListData.get(0).getAttributeName());
List<JSONObject> valueList = new ArrayList<>();
// 第三层:遍历属性值(叶子节点)
for (FlatAttributeDTO dto : attributeListData) {
JSONObject valueNode = new JSONObject();
valueNode.put("id", dto.getValueId());
valueNode.put("name", dto.getValueName());
valueList.add(valueNode);
}
attributeNode.put("children", valueList);
attributeList.add(attributeNode);
});
categoryNode.put("children", attributeList);
result.add(categoryNode);
});
3.使用MybatisPlus的ResultMap映射
实体类
这里的实体类异地昂要是这种,里面有list这种
java
// 一级分类(根节点)
public class CategoryTree {
private Long id; // category1_id
private String name; // category1_name
private List<CategoryLevel2> children;
// getter/setter 略
}
// 二级分类
public class CategoryLevel2 {
private Long id; // category2_id
private String name; // category2_name
private List<CategoryLevel3> children;
// getter/setter 略
}
// 三级分类(叶子节点)
public class CategoryLevel3 {
private Long id; // category3_id
private String name; // category3_name
// getter/setter 略
}
mapper文件
mapper中最外层就是我们的最外的是体力,里面的collection可以嵌套对应实体类里面的list说白了还是一一对应的关系
xml
<resultMap id="categoryTreeMap" type="com.xxx.dto.CategoryTree" autoMapping="false">
<!-- 一级分类 -->
<id column="c1_id" property="id"/>
<result column="c1_name" property="name"/>
<!-- 二级分类集合 -->
<collection property="children" ofType="com.xxx.dto.CategoryLevel2">
<id column="c2_id" property="id"/>
<result column="c2_name" property="name"/>
<!-- 三级分类集合 -->
<collection property="children" ofType="com.xxx.dto.CategoryLevel3">
<id column="c3_id" property="id"/>
<result column="c3_name" property="name"/>
</collection>
</collection>
</resultMap>
<select id="selectAttributeTree" resultMap="attributeTreeMap">
SELECT
bc.category1_id AS c1_id,
bc.category1_name AS c1_name,
ba.attribute_id AS a_id,
ba.attribute_name AS a_name,
bav.value_id AS v_id,
bav.value_name AS v_name
FROM base_category bc
INNER JOIN base_attribute ba
ON bc.category1_id = ba.category1_id
INNER JOIN base_attribute_value bav
ON ba.attribute_id = bav.attribute_id
WHERE bc.category1_id = #{category1Id}
</select>