數據庫中的樹結構-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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章