Java编程:将具有父子关系的数据库表数据转换为树形结构,支持无限层级

在平时的开发工作中,经常遇到这样一个场景,在数据库中存储了具有父子关系的数据,需要将这些数据以树形结构的形式在界面上进行展示。本文的目的是提供了一个通用的编程模型,解决将具有父子关系的数据转换成树形结构的问题。如有不正之处,欢迎大家批评指正。

编程模型

我们以北京行政区划为例,讲解整个模型。

北京市:市辖区 县

市辖区:东城区 西城区 朝阳区 丰台区 石景山区 海淀区 门头沟区 房山区 通州区 顺义区 昌平区 大兴区 怀柔区 平谷区

县:密云县 延庆县

UML类图如下:

Tree结构

TreeNode:定义树的节点对象

  • nodeId:树节点Id。
  • nodeName:树节点名称。
  • parentNodeId:树节点父Id。
  • orderNum:节点在树中的排序号,在同一节点下进行排序。
  • level:当前树节点所在的层级,根节点为第一层。
  • parent:当前树节点的父节点。
  • children:当前树节点的儿子节点。
  • allChildren:当前树节点的子孙节点。

ITree:定义树对象要实现的方法。

  • getTree():以 List 形式返回树的所有的 TreeNode 对象。
  • getRoot():以 List 形式返回树的根节点,可能是一个或者多个。
  • getTreeNode(String nodeId):根据 nodeId 返回对应的 TreeNode 对象。

Tree:实现 ITree 接口,提供树的完整功能。

  • getTree():以 List 形式返回树的所有的 TreeNode 对象。
  • getRoot():以 List 形式返回树的根节点,可能是一个或者多个。
  • getTreeNode(String nodeId):根据 nodeId 返回对应的 TreeNode 对象。

ITreeNode:定义模板方法,构造树形结构的类要实现该接口,Tree 通过调用该接口中的方法获取 nodeId nodeName parentNodeId orderNum 数据。

  • getNodeId():获取树节点Id。
  • getNodeName():获取树节点名称。
  • getParentNodeId():获取树节点父Id。
  • getOrderNum():获取节点在树中的排序号,在同一节点下进行排序。

Org:定义行政区划类,实现 ItreeNode 接口。

实现代码

TreeNode类:

package com.ips.tree;

import java.util.ArrayList;
import java.util.List;

import com.alibaba.fastjson.annotation.JSONField;

/**
 * <p>Title: 树节点</p>
 * <p>Description:一棵树由许多包含父子关系的节点组成 </p>
 * @author  liuzhibo
 * @date    2017年1月18日
 */
public class TreeNode {
    //树节点ID
    @JSONField(ordinal=1)
    private String nodeId;
    //树节点名称
    @JSONField(ordinal=2)
    private String nodeName;
    //父节点ID
    @JSONField(ordinal=3)
    private String parentNodeId;
    //节点在树中的排序号
    @JSONField(ordinal=4)
    private int orderNum;
    //节点所在的层级
    @JSONField(ordinal=5)
    private int level;
    private TreeNode parent;
    //当前节点的二子节点
    @JSONField(ordinal=6)
    private List<TreeNode> children = new ArrayList<TreeNode>();
    //当前节点的子孙节点
    private List<TreeNode> allChildren = new ArrayList<TreeNode>();

    public TreeNode(ITreeNode obj){
        this.nodeId = obj.getNodeId();
        this.nodeName = obj.getNodeName();
        this.parentNodeId = obj.getNodeParentId();
        this.orderNum = obj.getOrderNum();
    }
    public void addChild(TreeNode treeNode){
        this.children.add(treeNode);
    }
    public void removeChild(TreeNode treeNode){
        this.children.remove(treeNode);
    }
    public String getNodeId() {
        return nodeId;
    }
    public void setNodeId(String nodeId) {
        this.nodeId = nodeId;
    }
    public String getNodeName() {
        return nodeName;
    }
    public void setNodeName(String nodeName) {
        this.nodeName = nodeName;
    }
    public String getParentNodeId() {
        return parentNodeId;
    }
    public void setParentNodeId(String parentNodeId) {
        this.parentNodeId = parentNodeId;
    }
    public int getLevel() {
        return level;
    }
    public void setLevel(int level) {
        this.level = level;
    }
    public TreeNode getParent() {
        return parent;
    }
    public void setParent(TreeNode parent) {
        this.parent = parent;
    }
    public List<TreeNode> getChildren() {
        return children;
    }
    public void setChildren(List<TreeNode> children) {
        this.children = children;
    }
    public int getOrderNum() {
        return orderNum;
    }
    public void setOrderNum(int orderNum) {
        this.orderNum = orderNum;
    }

    public List<TreeNode> getAllChildren() {
        if(this.allChildren.isEmpty()){
            for(TreeNode treeNode : this.children){
                this.allChildren.add(treeNode);
                this.allChildren.addAll(treeNode.getAllChildren());
            }
        }
        return this.allChildren;
    }   
}

ITree接口:

package com.ips.tree;

import java.util.List;

public interface ITree {
    public List<TreeNode> getTree();
    public List<TreeNode> getRoot();
    public TreeNode getTreeNode(String nodeId);
}

Tree类:

package com.ips.tree;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

public class Tree implements ITree {
    private HashMap<String, TreeNode> treeNodesMap = new HashMap<String, TreeNode>();
    private List<TreeNode> treeNodesList = new ArrayList<TreeNode>();

    public Tree(List<ITreeNode> list){
        initTreeNodeMap(list);
        initTreeNodeList();
    }

    private void initTreeNodeMap(List<ITreeNode> list){
        TreeNode treeNode = null;
        for(ITreeNode item : list){
            treeNode = new TreeNode(item);
            treeNodesMap.put(treeNode.getNodeId(), treeNode);
        }

        Iterator<TreeNode> iter = treeNodesMap.values().iterator();
        TreeNode parentTreeNode = null;
        while(iter.hasNext()){
            treeNode = iter.next();
            if(treeNode.getParentNodeId() == null || treeNode.getParentNodeId() == ""){
                continue;
            }

            parentTreeNode = treeNodesMap.get(treeNode.getParentNodeId());
            if(parentTreeNode != null){
                treeNode.setParent(parentTreeNode);
                parentTreeNode.addChild(treeNode);
            }
        }
    }

    private void initTreeNodeList(){
        if(treeNodesList.size() > 0){
            return;
        }
        if(treeNodesMap.size() == 0){
            return;
        }
        Iterator<TreeNode> iter = treeNodesMap.values().iterator();
        TreeNode treeNode = null;
        while(iter.hasNext()){
            treeNode = iter.next();
            if(treeNode.getParent() == null){
                this.treeNodesList.add(treeNode);
                this.treeNodesList.addAll(treeNode.getAllChildren());
            }
        }
    }

    @Override
    public List<TreeNode> getTree() {
        return this.treeNodesList;
    }

    @Override
    public List<TreeNode> getRoot() {
        List<TreeNode> rootList = new ArrayList<TreeNode>();
        if (this.treeNodesList.size() > 0) {
            for (TreeNode node : treeNodesList) {
                if (node.getParent() == null)
                    rootList.add(node);
            }
        }
        return rootList;
    }

    @Override
    public TreeNode getTreeNode(String nodeId) {
        return this.treeNodesMap.get(nodeId);
    }

}

ITreeNode 接口:

package com.ips.tree;

public interface ITreeNode {
    public String getNodeId();
    public String getNodeName();
    public String getNodeParentId();
    public Integer getOrderNum();
}

Org 类:

package com.ips.tree;

public class Org implements ITreeNode {
    private String uuid;
    private String parentId;
    private String name;
    private Integer orderNum;
    private String code;
    private String type;

    public Org(){

    }
    public Org(String uuid, String parentId, String name, Integer orderNum, String code, String type){
        this.uuid = uuid;
        this.parentId = parentId;
        this.name = name;
        this.orderNum = orderNum;
        this.code = code;
        this.type = type;
    }
    @Override
    public String getNodeId() {
        return this.uuid;
    }

    @Override
    public String getNodeName() {
        return this.name;
    }

    @Override
    public String getNodeParentId() {
        return this.parentId;
    }

    @Override
    public Integer getOrderNum() {
        return this.orderNum;
    }

    public String getUuid() {
        return uuid;
    }

    public void setUuid(String uuid) {
        this.uuid = uuid;
    }

    public String getParentId() {
        return parentId;
    }

    public void setParentId(String parentId) {
        this.parentId = parentId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }
    public void setOrderNum(Integer orderNum) {
        this.orderNum = orderNum;
    }

}

TreeDemo 类:执行该类的 main 方法,查看效果。

package com.ips.tree;

import java.util.ArrayList;
import java.util.List;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;

public class TreeDemo {

    public static void main(String[] args) {
        Tree tree = new Tree(genOrgList());
        TreeNode treeNode = tree.getTreeNode("2");

        SimplePropertyPreFilter filter = new SimplePropertyPreFilter(); // 构造方法里,也可以直接传需要序列化的属性名字
        filter.getExcludes().add("parent");
        filter.getExcludes().add("allChildren");
        String data = JSONObject.toJSONString(treeNode, filter);
        System.out.println(data);
    }

    public static List<ITreeNode> genOrgList(){
        List<ITreeNode> list = new ArrayList<ITreeNode>();

        Org org = new Org("2", "1", "北京市", 2, "110000", "2");
        list.add(org);
        org = new Org("3", "2", "市辖区", 3, "110100", "3");
        list.add(org);
        org = new Org("4", "3", "东城区", 4, "110101", "4");
        list.add(org);
        org = new Org("5", "3", "东城区", 5, "110102", "4");
        list.add(org);
        org = new Org("6", "3", "东城区", 6, "110105", "4");
        list.add(org);
        org = new Org("7", "3", "东城区", 7, "110106", "4");
        list.add(org);
        org = new Org("8", "3", "东城区", 8, "110107", "4");
        list.add(org);
        org = new Org("9", "3", "东城区", 9, "110108", "4");
        list.add(org);
        org = new Org("10", "3", "东城区", 10, "110109", "4");
        list.add(org);
        org = new Org("11", "3", "东城区", 11, "110111", "4");
        list.add(org);
        org = new Org("12", "3", "东城区", 12, "110112", "4");
        list.add(org);
        org = new Org("13", "3", "东城区", 13, "110113", "4");
        list.add(org);
        org = new Org("14", "3", "东城区", 14, "110114", "4");
        list.add(org);
        org = new Org("15", "3", "东城区", 15, "110115", "4");
        list.add(org);
        org = new Org("16", "3", "东城区", 16, "110116", "4");
        list.add(org);
        org = new Org("17", "3", "东城区", 17, "110117", "4");
        list.add(org);
        org = new Org("18", "2", "县", 3, "110200", "3");
        list.add(org);
        org = new Org("19", "18", "密云县", 19, "110228", "4");
        list.add(org);
        org = new Org("20", "18", "延庆县", 20, "110229", "4");
        list.add(org);
        return list;
    }

}

执行结果如下:

{
    "nodeId": "2",
    "nodeName": "北京市",
    "parentNodeId": "1",
    "orderNum": 2,
    "level": 0,
    "children": [{
        "nodeId": "18",
        "nodeName": "县",
        "parentNodeId": "2",
        "orderNum": 3,
        "level": 0,
        "children": [{
            "nodeId": "19",
            "nodeName": "密云县",
            "parentNodeId": "18",
            "orderNum": 19,
            "level": 0,
            "children": []
        },
        {
            "nodeId": "20",
            "nodeName": "延庆县",
            "parentNodeId": "18",
            "orderNum": 20,
            "level": 0,
            "children": []
        }]
    },
    {
        "nodeId": "3",
        "nodeName": "市辖区",
        "parentNodeId": "2",
        "orderNum": 3,
        "level": 0,
        "children": [{
            "nodeId": "17",
            "nodeName": "东城区",
            "parentNodeId": "3",
            "orderNum": 17,
            "level": 0,
            "children": []
        },
        {
            "nodeId": "15",
            "nodeName": "东城区",
            "parentNodeId": "3",
            "orderNum": 15,
            "level": 0,
            "children": []
        },
        {
            "nodeId": "16",
            "nodeName": "东城区",
            "parentNodeId": "3",
            "orderNum": 16,
            "level": 0,
            "children": []
        },
        {
            "nodeId": "13",
            "nodeName": "东城区",
            "parentNodeId": "3",
            "orderNum": 13,
            "level": 0,
            "children": []
        },
        {
            "nodeId": "14",
            "nodeName": "东城区",
            "parentNodeId": "3",
            "orderNum": 14,
            "level": 0,
            "children": []
        },
        {
            "nodeId": "11",
            "nodeName": "东城区",
            "parentNodeId": "3",
            "orderNum": 11,
            "level": 0,
            "children": []
        },
        {
            "nodeId": "12",
            "nodeName": "东城区",
            "parentNodeId": "3",
            "orderNum": 12,
            "level": 0,
            "children": []
        },
        {
            "nodeId": "10",
            "nodeName": "东城区",
            "parentNodeId": "3",
            "orderNum": 10,
            "level": 0,
            "children": []
        },
        {
            "nodeId": "7",
            "nodeName": "东城区",
            "parentNodeId": "3",
            "orderNum": 7,
            "level": 0,
            "children": []
        },
        {
            "nodeId": "6",
            "nodeName": "东城区",
            "parentNodeId": "3",
            "orderNum": 6,
            "level": 0,
            "children": []
        },
        {
            "nodeId": "5",
            "nodeName": "东城区",
            "parentNodeId": "3",
            "orderNum": 5,
            "level": 0,
            "children": []
        },
        {
            "nodeId": "4",
            "nodeName": "东城区",
            "parentNodeId": "3",
            "orderNum": 4,
            "level": 0,
            "children": []
        },
        {
            "nodeId": "9",
            "nodeName": "东城区",
            "parentNodeId": "3",
            "orderNum": 9,
            "level": 0,
            "children": []
        },
        {
            "nodeId": "8",
            "nodeName": "东城区",
            "parentNodeId": "3",
            "orderNum": 8,
            "level": 0,
            "children": []
        }]
    }]
}

注:该示例中使用了 alibaba 的 fastjson 实现类对象序列化,maven 依赖如下:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.20</version>
</dependency>
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值