Java树形结构

本文讲述了如何在Java中处理数据库表结构数据,形成树形菜单。由于新增列的位置不确定,但列名以特定前缀开头,因此采用正则匹配处理。面临页面展示过多列的问题,选择使用树形结构解决,探讨了程序中递归实现和数据库操作两种方法,最终通过C#递归代码转换为Java实现,并找到了适用于无parentId规则的jQuery树形控件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        最近公司有一个需求,在页面展示一个树形菜单栏,里面取的数据不是原数据库存在的已维护的数据,而是在某几张特定表中的表结构数据。取的是表结构中的列名与列注释。类似下图表结构:


        本来是想在mysql中使用类似于oracle的rownum取某部分行数据的,取出列名在11行以后且不为83的列与注释

SELECT column_name,column_comment FROM (SELECT @rownum:=@rownum+1 rownum,column_name,column_comment FROM (SELECT @rownum:=0 rownum,column_name, column_comment,table_name FROM information_schema.COLUMNS WHERE table_schema = '数据库名' AND table_name ='表名' ) t) t WHERE rownum >11 AND rownum <> 83  

        但是这段sql代码在用jdbc使用时一直报错,我一直没找到原因.但是在sql中是可以执行的.最后我们项目负责人说列所在的顺序也是不确定的。新加的列是可以在任意一个位置。但是列开头的为A_,B_,C_...。所以就打算用正则匹配。代码如下,BINARY为强制匹配大写字母,不加的话大小写都能匹配上

SELECT column_name, column_comment FROM information_schema.COLUMNS WHERE table_schema = '数据库名' AND table_name ='表名'  AND column_name  REGEXP BINARY '^[A-Z]\\_'


        添加到此处,我原以为菜单就完成了。但是问题来了,在页面显示太多列对用户体验不好,决定使用树形结构解决这个问题。以"__"进行分割组成树形菜单。

        两种思路,第一个是在程序中使用递归解决,第二种是操作数据库得到树形结构。正是在数据库这个坑中呆了一天,发现无法解决(能力有限)。接着产品经理用C#写了个递归,我将它转为了java代码。看着挺简单的,但是就是没有想到。

        当时想到了几种树组件,如zTree,但是好像需要传入Id与parentId这种东西。一般都是有维护表上下级关系才使用该种树形控件。果断查找jquery无规则的树形控件。

直接上代码

public class RuleTree {
    /// <summary>
    /// 节点列表
    /// </summary>
    private List<RuleTreeNode> NodeList ;

    public RuleTree()
    {
        NodeList = new ArrayList<>();
    }

    /// <summary>
    /// 从数据库构建规则树
    /// </summary>
    /// <param name="dt"></param>
    public static RuleTree CreateRuleTree(List<IndexCustom> dt)
    {
        RuleTree rt = new RuleTree();

        for (IndexCustom row: dt)
        {
            String colName = row.getFieldName();
            String colComment = row.getFieldComment();
            RuleTreeNode node = RuleTreeNode.CreateRuleTreeNode(colName, colComment);
            rt.InsertNodeWithMerge(node);
        }
        return rt;
    }

    /// <summary>
    /// 用合并的方法插入一个节点
    /// </summary>
    /// <param name="node"></param>
    public void InsertNodeWithMerge(RuleTreeNode newNode)
    {
        for (RuleTreeNode existNode : NodeList)
        {
            if (RuleTreeNode.IsSameRoot(existNode, newNode))
            {
                existNode.Merge(newNode);
                return;
            }
        }

        // 没有相同根的节点
        NodeList.add(newNode);
    }

    /// <summary>
    /// 打印
    /// </summary>
    /// <param name="ruleTree"></param>
    public static void PrintRuleTreeNodeList(List<RuleTreeNode> nodeList , int nodeLevel)
    {
        nodeLevel = 0;
        for(RuleTreeNode node : nodeList)
        {
            StringBuilder sb = new StringBuilder();
            sb.append(' '+nodeLevel + 4);
//            sb.Append(' ', nodeLevel + 4);
        }
    }

    public List<RuleTreeNode> getNodeList() {
        return NodeList;
    }

    public void setNodeList(List<RuleTreeNode> nodeList) {
        NodeList = nodeList;
    }
}
public class RuleTreeNode {

    private String name ;

    private List<RuleTreeNode> Children ;

    public RuleTreeNode(String name) {
        this.name = name;
        Children = new ArrayList<>();
    }

    public RuleTreeNode()
    {
        // 空树
        Children = new ArrayList<>();
    }

    /// <summary>
    ///  构建树的节点
    /// </summary>
    /// <param name="leaveName">最后节点的名称</param>
    /// <param name="pathString">路径支付串</param>
    /// <returns></returns>
    public static RuleTreeNode CreateRuleTreeNode(String leaveName, String pathString)
    {
        String[] pathGroup = pathString.split("__");

        return BuildRuleTreeNode(leaveName, pathGroup, 0);
    }

    protected static RuleTreeNode BuildRuleTreeNode(String leaveName, String[] pathGroup, int nodeIndex)
    {
        // 如果没有树路径使用叶作为节点
        if (nodeIndex >= pathGroup.length) return new RuleTreeNode(leaveName);

        RuleTreeNode node = new RuleTreeNode();
        node.name = pathGroup[nodeIndex];

        RuleTreeNode subNode = BuildRuleTreeNode(leaveName, pathGroup, nodeIndex + 1);
        node.Children.add(subNode);

        return node;
    }

    /// <summary>
    /// 把这个节点的子节点和自己的子几点和在一起
    /// </summary>
    /// <param name="newNode"></param>
    public void Merge(RuleTreeNode newNode)
    {

        for (RuleTreeNode subNode : newNode.Children)
        {
            RuleTreeNode existNode = FindSameRootNode(Children, subNode);
            if (existNode == null)
            {
                // 不存在相同的子节点,该节点作为新节点加入
                this.Children.add(subNode);
            }
            else
            {
                // 合并子节点
                existNode.Merge(subNode);
            }
        }
    }

    public RuleTreeNode FindSameRootNode(List<RuleTreeNode> nodeList, RuleTreeNode expectNode)
    {
        for (RuleTreeNode existNode : nodeList)
        {
            if (IsSameRoot(existNode, expectNode)) return existNode;
        }
        return null;
    }

    public static Boolean IsSameRoot(RuleTreeNode existNode, RuleTreeNode newNode)
    {
        return (existNode.name).equals(newNode.name);
    }

    public String getName() {
        return name;
    }

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

    public List<RuleTreeNode> getChildren() {
        return Children;
    }

    public void setChildren(List<RuleTreeNode> children) {
        this.Children = children;
    }
}
调用CreateRuleTree(List<IndexCustom> dt) 传入数据库查到的集合,再将它转为json字符串即可满足很多树形控件的条件。类似于下图的树形结构。多选框其实为叶子节点。根据项目需要才将叶子节点当做多选框展示并能勾选。









$(function(){ $.fn.extend({ SimpleTree:function(options){ //初始化参数 var option = $.extend({ click:function(a){ } },options); option.tree=this; /* 在参数对象中添加对当前菜单树的引用,以便在对象中使用该菜单树 */ option._init=function(){ /* * 初始化菜单展开状态,以及分叉节点的样式 */ this.tree.find("ul ul").hide(); /* 隐藏所有子级菜单 */ this.tree.find("ul ul").prev("li").removeClass("open"); /* 移除所有子级菜单父节点的 open 样式 */ this.tree.find("ul ul[show='true']").show(); /* 显示 show 属性为 true 的子级菜单 */ this.tree.find("ul ul[show='true']").prev("li").addClass("open"); /* 添加 show 属性为 true 的子级菜单父节点的 open 样式 */ }/* option._init() End */ /* 设置所有超链接不响应单击事件 */ this.find("a").click(function(){ $(this).parent("li").click(); return false; }); /* 菜单项 接受单击 */ this.find("li").click(function(){ /* * 当单击菜单项 * 1.触发用户自定义的单击事件,将该 标签中的第一个超链接做为参数传递过去 * 2.修改当前菜单项所属的子菜单的显示状态(如果等于 true 将其设置为 false,否则将其设置为 true) * 3.重新初始化菜单 */ option.click($(this).find("a")[0]); /* 触发单击 */ /* * 如果当前节点下面包含子菜单,并且其 show 属性的值为 true,则修改其 show 属性为 false * 否则修改其 show 属性为 true */ /* if($(this).next("ul").attr("show")=="true"){ $(this).next("ul").attr("show","false"); }else{ $(this).next("ul").attr("show","true"); }*/ /* 初始化菜单 */ option._init(); }); /* 设置所有父节点样式 */ this.find("ul").prev("li").addClass("folder"); /* 设置节点“是否包含子节点”属性 */ this.find("li").find("a").attr("hasChild",false); this.find("ul").prev("li").find("a").attr("hasChild",true); /* 初始化菜单 */ option._init(); }/* SimpleTree Function End */ }); });
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kaisnm

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值