今天看了csdn中lifetragedy的一篇精华博文《通向架构师的道路(第六天)之漫谈基于数据库的权限系统的设计》,里面提到的菜单表的设计方式。其中使用lft,rgt两个字段的标示和预计算,从而jquery等tree控件能够从检索结果,通过一次遍历达到构建菜单目录树的效果。详情参见:http://blog.csdn.net/lifetragedy/article/details/7734864。
笔者想到创建表的时候可以直接从树形结构的菜单,通过计算获得lft,rgt,level字段,并与其他所需信息一同入库,这个方式在初始建库的时候特别有用。不需要每一个菜单项都是用lifetragedy给出的四步,当然,如果后续添加一些菜单,还是使用lifetragedy的方法直接一些。
下面给出笔者的模拟代码(其中的tree使用的是lifetragedy在原文中给出的示例,这里的Tree可以是从xml文件中读取,那么这段代码的实用性就大了。):
package com.ss.util;
public class CreateMenuTable {
private static Node tree;
static{
//create a menu tree
tree = new Node("caidan");
//first level
Node baobiao = new Node("baobiao");
Node xitong = new Node("xitongguanli");
tree.addChild(baobiao);
baobiao.setParent(tree);
tree.addChild(xitong);
xitong.setParent(tree);
//second level
Node nianbao = new Node("nianbao");
Node jibao = new Node("jibao");
Node yuebao = new Node("yuebao");
Node zhoubao = new Node("zhoubao");
baobiao.addChild(zhoubao);
zhoubao.setParent(baobiao);
baobiao.addChild(yuebao);
yuebao.setParent(baobiao);
baobiao.addChild(jibao);
jibao.setParent(baobiao);
baobiao.addChild(nianbao);
nianbao.setParent(baobiao);
Node yonghuguanli = new Node("yonghuguanli");
Node jueseguanli = new Node("jeuseguanli");
xitong.addChild(yonghuguanli);
yonghuguanli.setParent(xitong);
xitong.addChild(jueseguanli);
jueseguanli.setParent(xitong);
//third level
Node zengjia = new Node("zengjia");
Node shanchu = new Node("shanchu");
yonghuguanli.addChild(zengjia);
zengjia.setParent(yonghuguanli);
yonghuguanli.addChild(shanchu);
shanchu.setParent(yonghuguanli);
Node zengjia1 = new Node("zengjia");
Node shanchu1 = new Node("shanchu");
jueseguanli.addChild(zengjia1);
zengjia1.setParent(jueseguanli);
jueseguanli.addChild(shanchu1);
shanchu1.setParent(jueseguanli);
}
//use depth first traversal method to calc width of each node.
//every node's span is 2, so set it's width = 2
private void calculate_width(Node tree){
//leaf node set width = 2
if(tree.getChilds().size() == 0){
tree.setWidth(2);
return;
}
//calc each child's width
for(Node child : tree.getChilds()){
calculate_width(child);
}
//calc this root node's width
for(Node child : tree.getChilds()){
tree.setWidth(tree.getWidth() + child.getWidth());
}
tree.setWidth(tree.getWidth() + 2);//his own width
}
//calc by breadth first traversal method and use the following equals:
//for the first child node--
//lft = parent.lft + 1
//rgt = lft + width -1
//
//for orther child node--
//lft = rgt_before + 1
//rgt = lft + width -1
private void calculate_landr(Node tree){
//for leaf node
if(tree.getChilds().size() == 0){
return;
}
//for trees has child node
//calc first node's landr
Node firstNode = tree.getChilds().get(0);
firstNode.setLft(firstNode.getParent().getLft() + 1);
firstNode.setRft(firstNode.getLft() + firstNode.getWidth() -1);
printNodeInfo(firstNode);
Node preNode = firstNode;
for(int index=1, size=tree.getChilds().size(); index<size; index++){
Node otherNode = tree.getChilds().get(index);
otherNode.setLft(preNode.getRft() + 1);
otherNode.setRft(otherNode.getLft() + otherNode.getWidth() -1);
printNodeInfo(otherNode);
preNode = otherNode;
}
//calc tree's child's child's landr
for(Node child : tree.getChilds()){
calculate_landr(child);
}
}
private void printNodeInfo(Node node){
StringBuilder sbuilder = new StringBuilder();
sbuilder.append(node.getName());
sbuilder.append(" ");
sbuilder.append(node.getLft());
sbuilder.append(" ");
sbuilder.append(node.getRft());
sbuilder.append(" ");
sbuilder.append(node.getWidth());
sbuilder.append(" ");
System.out.println(sbuilder.toString());
}
public static void main(String[] arg){
CreateMenuTable cmt = new CreateMenuTable();
cmt.calculate_width(tree);
tree.setLft(1);
tree.setRft(1 + tree.getWidth() - 1);
cmt.printNodeInfo(tree);
cmt.calculate_landr(tree);
}
}
下面给出Node类的代码:
package com.ss.util;
import java.util.ArrayList;
import java.util.List;
public class Node {
private Node parent;
private List<Node> childs = new ArrayList<Node>();
private String name;
private int lft;
private int rft;
private int depth;
private int width;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public Node(String name){
this.name = name;
}
public Node getParent() {
return parent;
}
public void setParent(Node parent) {
this.parent = parent;
}
public String getName() {
return name;
}
public void addChild(Node child){
this.childs.add(child);
}
public List<Node> getChilds(){
return this.childs;
}
public int getLft() {
return lft;
}
public void setLft(int lft) {
this.lft = lft;
}
public int getRft() {
return rft;
}
public void setRft(int rft) {
this.rft = rft;
}
public int getDepth() {
return depth;
}
public void setDepth(int depth) {
this.depth = depth;
}
}
运行输出的控制台结果如下:
caidan 1 26 26
baobiao 2 11 10
xitongguanli 12 25 14
zhoubao 3 4 2
yuebao 5 6 2
jibao 7 8 2
nianbao 9 10 2
yonghuguanli 13 18 6
jeuseguanli 19 24 6
zengjia 14 15 2
shanchu 16 17 2
zengjia 20 21 2
shanchu 22 23 2