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

转载 2007年10月09日 19:39:00
数据库中的树形结构 - 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 

树形结构 数据库表设计

转载:逻辑数据库设计 - 单纯的树(递归关系数据) 相信有过开发经验的朋友都曾碰到过这样一个需求。假设你正在为一个新闻网站开发一个评论功能,读者可以评论原文甚至相互回复。   这个需求并不...
  • tiantiandjava
  • tiantiandjava
  • 2015年04月30日 09:46
  • 34048

目录树结构的数据库设计思考

昨天一同事遇到一问题,找我帮忙解决一下,自己对问题的一些思考
  • sinat_22657459
  • sinat_22657459
  • 2016年10月28日 13:17
  • 1891

树形结构的数据库表Schema设计

树形结构的数据库表Schema设计     程序设计过程中,我们常常用树形结构来表征某些数据的关联关系,如企业上下级部门、栏目结构、商品分类等等,通常而言,这些树状结构需要借助于数据...
  • zhaoxuejie
  • zhaoxuejie
  • 2016年09月22日 15:03
  • 617

左右值编码来存储无限分级树形结构的数据库表设计

采用左右值编码来存储无限分级树形结构的数据库表设计 该设计方案的优点是:只用一条查询语句即可得到某个根节点及其所有子孙节点的先序遍历。由于消除了递归,在数据记录量较大时,可以大大提高...
  • u013019820
  • u013019820
  • 2017年10月24日 19:15
  • 164

java构建树,构建tree,组装树结构,通用算法,用到递归算法

java利用递归算法,map和list对树进行组装。
  • heliangb46
  • heliangb46
  • 2017年06月10日 09:09
  • 1709

树形结构的数据库表设计

树形结构的数据库表设计 最近研究树形菜单网上找了很多例子看了。一下是网上找的一些资料,然后自己重新实践,记录下免得下次又忘记了。 程序设计过程中,我们常常用树形结构来表征某些数据的关联关系,如企业上下...
  • lj1314ailj
  • lj1314ailj
  • 2016年07月30日 18:20
  • 444

java工程积累——树形结构的操作

通过这几天的观察,我们发现,集体的智慧永远是最强大的,每个人贡献一点点,我们就能将问题的解决方案在设计之初就升级到一个客观的程度,包括这次的树形结构的研究,相信你的队友不论他在你心理到底是什么状态,他...
  • xvshu
  • xvshu
  • 2014年12月23日 22:42
  • 4165

树形结构的数据如何保存到关系型的数据库

需求: 文档型数据,结构是树形的,如图: 想要读取生成树形结构、添加子节点、查找修改数据的代价最小。 看知乎上牛人的答案: 一般比较普遍的就是四种方法:(具体见 SQL Ant...
  • jim8757
  • jim8757
  • 2016年08月31日 15:41
  • 3563

使用递归算法结合数据库解析成java树形结构

感谢有奉献精神的人 转自:http://blog.sina.com.cn/s/blog_5d911a3f0102v0ue.html 使用递归算法结合数据库解析成...
  • zhangliao613
  • zhangliao613
  • 2015年08月31日 17:47
  • 1733

通用列表数据转化为树形结构

最近做项目用的是JQuery  easyUI 控件, 虽然很强大,但是高级控件对于数据的格式要求比较严格遇到 如下问题     1.使用comboTree 控件   绑定树形的组织结构   但...
  • zhxh0376
  • zhxh0376
  • 2015年07月16日 18:03
  • 1289
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:数据库中的树形结构 - JAVA 设计 (通用)
举报原因:
原因补充:

(最多只允许输入30个字)