`

数据库中的树形结构 - JAVA 设计 (通用)

阅读更多

我们通常会在应用中碰到树形结构的内容,比如 文件夹/文件模型, 部门组织结构,目录树等等,通常在设计模式中叫做 compose 模式。

在数据库中常常这样表示: 我们以Catalog (分级目录) 为例子

Catalog (分级目录)

字段名称

字段

类型

备注

目录ID

catalog_id

varchar(36)

pk, not null

目录名称

catalog_name

varchar(50)

not null

父目录ID

parent_id

varchar(36)

fk

创建时间

create_datetime

datetime

not null

目录描述

description

varchar(200)

 




我们考虑在数据库中一次将所有数据读入内存,然后在内存中生成一个Tree,这样可以减少数据库的访问,增加性能,并且只有的数据方式改变的时候,全部重新从数据库中生成Tree,否则一直保持在内存中。

我们使用标准的DAO模式先生成 POJO类(Catalog)和DAO类(CatalogDAO)。

然后我们建立相对通用的 Tree 和 TreeNode 类。

Tree.java

package com.humpic.helper.tree;

import java.util.*;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public abstract class Tree {
    protected static Log log = LogFactory.getLog(Tree.class);

    private Map treeNodeMaps = new Hashtable();
    private TreeNode root;

    /**
     * root if it's parent is empty
     */
    protected void reload(List nodes) {
        log.info("tree will start reload all data");
        
        synchronized (this) {
            // initialize
            treeNodeMaps.clear();
            root = null;

            List treeNodes = new Vector(nodes.size());
            for (int i = 0; i < nodes.size(); i++) {
                TreeNode node = this.transform(nodes.get(i)); // transform
                treeNodes.add(node);
                node.setTree(this);
                treeNodeMaps.put(node.getNodeId(), node);
            }
            
            for (int i = 0; i < treeNodes.size(); i++) {
                TreeNode node = (TreeNode) treeNodes.get(i);
                String parentId = node.getParentId();
                if (this.isRootNode(node)) {
                    if (root == null) {
                        root = node;
                    } else {
                        log.error("find more then one root node. ignore.");
                    }
                } else {
                    TreeNode parent = (TreeNode) treeNodeMaps.get(parentId);
                    if (parent != null) {
                        parent.addChild(node);
                        node.setParent(parent);
                    } else {
                        log.warn("node [id=" + node.getNodeId() + "]: missing parent node.");
                    }
                }
            }
        }

        if (root == null) {
            log.error("the root node is not be defined");
        }
    }

    protected boolean isRootNode(TreeNode node) {
        return StringUtils.isBlank(node.getParentId());
    }

    public TreeNode getRootNode() {
        return root;
    }

    public TreeNode getTreeNode(String nodeId) {
        return (TreeNode) treeNodeMaps.get(nodeId);
    }

    public void addTreeNode(TreeNode node) {
        synchronized (this) {
            treeNodeMaps.put(node.getNodeId(), node);

            String parentId = node.getParentId();
            if (StringUtils.isNotBlank(parentId)) {
                TreeNode parent = getTreeNode(parentId);
                if (parent != null) {
                    parent.addChild(node);
                    node.setParent(parent);
                } else {
                    log.error("parent cannot be found: " + node.getParentId());
                }
            } else {
                if (root == null) {
                    root = node;
                } else {
                    log.error("find more then one root node. ignore.");
                }
            }
        }
    }

    public void deleteTreeNode(String nodeId) {
        synchronized (this) {
            TreeNode node = getTreeNode(nodeId);
            if (node == null)
                throw new IllegalArgumentException(nodeId + " cannot be found.");

            if (node.getParent() == null) {
                root = null;
                treeNodeMaps.clear();
                log.warn("the root node has been removed.");
            } else {
                node.getParent().getChildren().remove(node);

                treeNodeMaps.remove(nodeId);
                List children = node.getAllChildren();
                for (int i = 0; i < children.size(); i++) {
                    TreeNode n = (TreeNode) children.get(i);
                    treeNodeMaps.remove(n.getNodeId());
                }
            }
        }
    }

    /**
     * <pre>
     * Usage: Office -&gt;
     * 
     * public TreeNode transform(Object info) {
     *     OfficeInfo office_info = (OfficeInfo) info;
     *     TreeNode node = new TreeNode();
     *     node.setNodeId(office_info.getOfficeId());
     *     node.setParentId(office_info.getParentId());
     *     node.setBindData(office_info);
     *     return node;
     * }
     * </pre>
     */
    protected abstract TreeNode transform(Object info);
}

 

 

 

TreeNode.java

 

package com.humpic.helper.tree;

import java.util.List;
import java.util.Vector;

import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.ObjectUtils;

public class TreeNode {

    private Tree tree;
    private TreeNode parent;
    private List children = new Vector();
    private List childrenGroup = new Vector();
    private String nodeId;
    private String parentId;
    private Object bindData;

    public String getNodeId() {
        return nodeId;
    }

    public void setNodeId(String nodeId) {
        this.nodeId = nodeId;
    }

    public String getParentId() {
        return parentId;
    }

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

    public Object getBindData() {
        return bindData;
    }

    public void setBindData(Object bindData) {
        this.bindData = bindData;
    }

    public Tree getTree() {
        return tree;
    }

    public void setTree(Tree tree) {
        this.tree = tree;
    }

    public void setParent(TreeNode parent) {
        this.parent = parent;
    }

    public TreeNode getParent() {
        return this.parent;
    }

    public List getChildren() {
        return this.children;
    }

    public void addChild(TreeNode node) {
        children.add(node);
    }

    /**
     * get all children, and chilren's children
     */
    public List getAllChildren() {
        if (this.childrenGroup.isEmpty()) {
            synchronized (this.tree) {
                for (int i = 0; i < this.children.size(); i++) {
                    TreeNode node = (TreeNode) this.children.get(i);
                    this.childrenGroup.add(node);
                    this.childrenGroup.addAll(node.getAllChildren());
                }
            }
        }
        return this.childrenGroup;
    }

    /**
     * get all children, and chilren's children
     */
    public List getAllChildren(Predicate predicate) {
        List groups = new Vector();
        fillAllChildren(groups, predicate);
        return groups;
    }

    private void fillAllChildren(List groups, Predicate predicate) {
        for (int i = 0; i < this.children.size(); i++) {
            TreeNode node = (TreeNode) this.children.get(i);
            if (predicate.evaluate(node)) {
                groups.add(node);
                node.fillAllChildren(groups, predicate);
            }
        }
    }

    /**
     * get all parents, and parent's parent
    */
    public List getParents() {
        List results = new Vector();
        TreeNode parent = this.getParent();
        while (parent != null) {
            results.add(parent);
            parent = parent.getParent();
        }
        return results;
    }

    /**
     * A.isMyParent(B) == B is A' parent ? <br>
     * root.isMyParent(null) == true; <br>
     * root.isMyParent(*) == false <br>
     * *.isMyParent(null) == false
     */
    public boolean isMyParent(String nodeId) {
        TreeNode target = tree.getTreeNode(nodeId);
        TreeNode parent = this.getParent();
        if (parent == null) {
            return target == null;
        } else {
            return parent.equals(target);
        }
    }

    /**
     * A.isMyAncestor(B) == B is A' ancestor ? <br>
     * *.isMyAncestor(null) == true;
     */
    public boolean isMyAncestor(String nodeId) {
        TreeNode target = tree.getTreeNode(nodeId);
        if (target == null)
            return true;

        return target.getAllChildren().contains(this);
    }

    /**
     * A.isMyBrother(B) == B is A' brother ? <br>
     * *.isMyBrother(null) == false
     */
    public boolean isMyBrother(String nodeId) {
        TreeNode target = tree.getTreeNode(nodeId);
        if (target == null)
            return false;

        TreeNode p1 = this.getParent();
        TreeNode p2 = target.getParent();
        return ObjectUtils.equals(p1, p2);
    }

}

 

然后建立业务 Tree

 

CatalogTree.java

package com.humpic.helper.tree;

import java.util.List;
import org.apache.commons.collections.Predicate;

public class CatalogTree extends Tree {
    private static CatalogTree instance = null;

    private CatalogTree() {}
    
    public static synchronized CatalogTree getInstance() {
        if (instance == null) {
            instance = new CatalogTree();
            instance.reloadCatalogs();
        }
        return instance;
    }

    protected TreeNode transform(Object info) {
        Catalog catalog = (Catalog) info;
        TreeNode node = new TreeNode();
        node.setNodeId(catalog.getCatalogId());
        node.setParentId(catalog.getParentId());
        node.setBindData(catalog);
        return node;
    }

    public void reloadCatalogs() {
        List nodes = CatalogDAO.getInstance().findAll();
        super.reload(nodes);
    }

    public Catalog getCatalogNode(String catalogId) {
        TreeNode node = super.getTreeNode(catalogId);
        return node == null ? null : (Catalog) node.getBindData();
    }
}

 

 

最后,我们只要使用以下的语句就可以了:

1. CatalogTree.getInstance().getTreeNode(...)
2. CatalogTree.getInstance().getCatalogNode(...)
3. CatalogTree.getInstance().getRootNode()

然后通过 TreeNode,就可以得到 parent, parents 和 children, allChildren

 

 

分享到:
评论

相关推荐

    java递归树型结构通用数据库

    在Java递归树型结构通用数据库中,使用关系型数据库来存储部门信息,数据库表结构设计包括部门表、用户表、部门用户表等,通过这些表之间的关系实现树型结构的部门管理。 6. 递归算法实现 在Java递归树型结构通用...

    AlaiJSCtr.rar_树形结构 java

    在计算机科学中,树形结构常用于表示文件系统、网页链接、数据库索引、表达式解析、任务调度等多种场景。 在JavaScript中实现树形结构,一般会涉及到以下几个核心概念: 1. 节点(Node):树的基本单元,通常包含...

    对于java的树形结构的抽象与拓展.docx

    这里,我们探讨的是如何通过Hibernate来抽象和扩展Java中的树形结构,并考虑到了在数据库表设计时的灵活性。 首先,我们看到一个具体的树形结构实体类`Modual2`,它代表了一个模块,具有ID(dbid)、名称(nane)、...

    数据结构与算法分析-java语言描述(第二版)

    2. **树形结构**:树和二叉树的概念,以及它们在查找和排序操作中的应用,例如二叉搜索树、AVL树、堆等。 3. **图结构**:图的表示方法,包括邻接矩阵和邻接表,以及图的遍历算法,如深度优先搜索(DFS)和广度优先...

    2011年软件大赛-java.本科-决赛真题.pdf

    综上所述,该决赛试题主要考察了 Java 语言基础(如 `BigInteger` 类的使用)、数据结构(树形结构的构建和表示)、程序设计原则(代码的通用性、正确性)以及编程规范,同时也涉及到了问题解决和算法设计的能力。...

    java 实用jar包 集合

    Java开发过程中,jar(Java Archive)包是必不可少的资源,它们包含了预编译的类、接口和其他资源,方便开发者在项目中直接引用。本压缩包集合涵盖了多种实用的jar包,旨在提供对不同数据库的连接支持,SSH(Spring...

    xml与数据库中数据的导入导出

    DOM将整个XML文档加载到内存中,形成一个树形结构,适合小规模数据处理。SAX是事件驱动的解析器,逐个处理XML元素,节省内存,适合大文件。StAX则允许程序员以流式方式读写XML,提供更好的性能和控制。 2. **JDBC...

    Java web开发进阶

    - 树形结构、图结构等高级数据结构 - **学习资源**: - 数据结构教材(适合大二学生) **1.3 Java集合框架** - **定义**: Java集合框架是一套用于存储和操作对象集合的标准API,提供了多种数据结构实现。 - **内容...

    求职宝典-Java 基础面试题

    - **组合模式**:允许你将对象组合成树形结构来表现“整体/部分”层次结构,使得用户对单个对象和组合对象的使用具有一致性。 - **装饰器模式**:动态地给一个对象添加一些额外的职责,可以提供比继承更具弹性的...

    管理系统系列--通用的java后台管理系统(权限管理+用户管理+菜单管理).zip

    通常,菜单结构会被设计成树形结构,便于层次化展示。菜单与权限管理紧密关联,用户看到的菜单由其角色所拥有的权限决定。这部分可能涉及到前端的路由配置,例如使用React Router或Vue Router来实现。 【技术栈】:...

    list转树状结构通用工具类

    我们的目标是将这个列表转换为树形结构,这样可以更好地体现数据间的层次关系。 对于非递归方法,我们可以利用栈或队列等数据结构来实现。这种方法避免了递归可能导致的堆栈溢出问题,尤其适用于层级较深的树结构。...

    Java的23种设计模式百度云下载链接.rar

    6. **组合模式**:允许你将对象组合成树形结构来表现“整体/部分”层次结构。在Java UI组件树中广泛应用。 7. **装饰器模式**:动态地给一个对象添加一些额外的职责。Java的IO流体系结构就是装饰器模式的经典应用。...

    Java设计模式导读.pdf

    组合模式(Composite)将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。 代理模式(Proxy)为其他对象提供一个代理以控制对这个对象的访问。代理模式...

    通用树形目录生成

    需要修改Hibernate的配置信息,并创建一个与数据库表同名的Java Bean类和一个Vo类并继承Vo(类名:数据库表名+Vo) 将代码部署在服务器上后,访问index.jsp,输入类名和分页大小,就可以生成该表的树形目录

    树形菜单tag框架 非树形的点击事件

    标题中的“树形菜单tag框架 非树形的点击事件”指的是在Web开发中,如何在非树形结构的菜单系统中实现类似树形菜单的点击事件处理。树形菜单通常用于展示层级关系的数据,而这里的“非树形”的含义可能是指在没有...

    JAVAJDBC基础.pdf

    - **层次型数据库**:以树形结构组织数据,记录间通过指针关联,适用于特定场景,但查询和更新操作复杂。 - **网状数据库**:以网格结构表示实体,支持多对多联系,但应用程序编写复杂。 - **关系型数据库**:...

    通用数据权限管理系统设计

    1. **增加系统资源信息和操作类型信息**:系统资源形成树形结构,如销售模块、销售订单等;操作类型记录各种可能的操作,如增加、删除等。 2. **增加数据对象类型和数据对象表**:数据对象类型记录需控制的对象类型...

    java设计模式(设计公司出品)

    10. **组合模式**:将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。在Java UI框架中,如Swing和JavaFX,组件树就是一个典型的组合模式应用。 以上只是设计...

    jsp jstl 递归 输出树 Tree 后台 Java 集合 递归 实现通用 树Tree

    本主题将深入探讨如何使用Java集合、JSP和JSTL来递归地创建并输出树形结构(Tree),特别是用于前端展示。 首先,我们要理解Java集合在构建树结构中的作用。在Java中,可以使用ArrayList、LinkedList或者自定义的...

Global site tag (gtag.js) - Google Analytics