树形结构工具类

  前言

  日常开发中,树形结构的数据是比较常见的一种数据结构,比如系统菜单、组织机构、数据字典等,有时候需要后端把数据转成树形结构再返回给前端,对此特意封装通用树形结构工具类

  封装了以下方法:

  根据父id,递归获取所有子节点,转为树结构

 

  根据子id,递归获取所有父节点,转为树结构

 

  拼接 union sql脚本,根据查询查询条件、id字段名、pid字段名,拼接出sql

   

  依赖hutool

        <!-- hutool -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.18</version>
        </dependency>

 

  完整代码

package cn.huanzi.qch.util;

import cn.hutool.core.bean.BeanUtil;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

/**
 * 树形结构工具类
 */
public class TreeUtil{

    /**
     * 根据父id,递归获取所有子节点,转为树结构
     *
     * @param idFieldName id字段名称
     * @param pIdFieldName pid字段名称
     * @param childrenFieldName children字段名称
     * @param pxFieldName px字段名称
     * @param pId 父节点id
     * @param allList 所有菜单列表
     * @return 每个根节点下,所有子菜单列表
     */
    public static <M>  List<M> toTreeByParentId(String idFieldName, String pIdFieldName, String childrenFieldName, String pxFieldName,String pId, List<M> allList){
        //子节点
        List<M> childList = new ArrayList<>(allList.size());
        for (int i = 0; i < allList.size(); i++) {
            M model = allList.get(i);

            //遍历所有节点将节点的父id与传过来的根节点的id比较
            //父节点id字段,例如:pid
            if (BeanUtil.getFieldValue(model,pIdFieldName).equals(pId)){
                childList.add(model);

                //删除,减少下次循环次数
                allList.remove(i);
                i--;
            }
        }
        //递归
        for (M model : childList) {
            //主键字段,例如:id,子节点字段,例如:children
            BeanUtil.setFieldValue(model,childrenFieldName,TreeUtil.toTreeByParentId(idFieldName,pIdFieldName,childrenFieldName,pxFieldName,String.valueOf(BeanUtil.getFieldValue(model,idFieldName)), allList));
        }

        //排序字段,例如:px,如果不需要排序可以注释
        if(null != pxFieldName && !pxFieldName.isEmpty()){
            childList.sort(Comparator.comparingInt(m -> Integer.parseInt(String.valueOf(BeanUtil.getFieldValue(m, pxFieldName))))); //排序
        }

        //底层节点的子节点赋空值,节省内存空间
        if(childList.size() <= 0){
            childList = null;
        }

        return childList;
    }
    public static <M>  List<M> toTreeByParentId(String pId, List<M> allList){
        //设置一下默认值
        return TreeUtil.toTreeByParentId("id","pid","children","px",pId,allList);
    }

    /**
     * 根据子id,递归获取所有父节点,转为树结构
     *
     * @param idFieldName id字段名称
     * @param pIdFieldName pid字段名称
     * @param childrenFieldName children字段名称
     * @param cId 子节点id
     * @param allList 所有菜单列表
     * @return 每个根节点下,所有子菜单列表
     */
    public static <M> M toTreeByChildrenId(String idFieldName, String pIdFieldName, String childrenFieldName,String cId, List<M> allList){
        return TreeUtil.toTreeByChildrenId(idFieldName,pIdFieldName,childrenFieldName,null,cId,allList);
    }
    private static <M> M toTreeByChildrenId(String idFieldName, String pIdFieldName, String childrenFieldName,M parent,String cId, List<M> allList){
        //父节点
        M newParent = null;

        for (int i = 0; i < allList.size(); i++) {
            M model = allList.get(i);

            // 相等说明:找出当前节点
            if (BeanUtil.getFieldValue(model,idFieldName).equals(cId)){
                newParent = model;

                //设置子节点
                if(parent != null){
                    ArrayList<M> childList = new ArrayList<>(1);
                    childList.add(parent);
                    BeanUtil.setFieldValue(newParent,childrenFieldName, childList);

                }

                //删除,减少下次循环次数
                allList.remove(i);
                i--;
                break;
            }
        }

        //父节点为空,则说明为顶层节点
        String menuParentId = newParent == null ? "" : String.valueOf(BeanUtil.getFieldValue(newParent,pIdFieldName));
        if("".equals(menuParentId)){
            return parent;
        }

        //父节点递归
        newParent = TreeUtil.toTreeByChildrenId(idFieldName,pIdFieldName,childrenFieldName,newParent,menuParentId,allList);

        return newParent;
    }
    public static <M> M toTreeByChildrenId(String cId, List<M> allList){
        //设置一下默认值
        return TreeUtil.toTreeByChildrenId("id", "pid", "children",null,cId,allList);

    }

    /**
     * 拼接 union sql脚本,根据查询查询条件、id字段名、pid字段名,拼接出sql
     * @param select 查询字段,例如: select id
     * @param tableName 表名,例如 sys_dept
     * @param initWhere 查询条件,例如:pid = '-1'
     * @param idField id字段名
     * @param pidField pid字段名
     * @param maxLevel 拼接最大层级
     * @return union拼接好的sql脚本
     */
    public static String getParentSql(String select, String tableName, String initWhere, String idField, String pidField, int maxLevel) {
        return getUnionSql(select,tableName,initWhere,idField,pidField,maxLevel);
    }
    public static String getChildSql(String select, String tableName, String initWhere, String idField, String pidField, int maxLevel) {
        return getUnionSql(select,tableName,initWhere,pidField,idField,maxLevel);
    }
    private static String getUnionSql(String select, String tableName, String initWhere, String whereIn, String selectIn, int maxLevel) {
        StringBuilder stringBuilder = new StringBuilder(select);
        stringBuilder.append(" from ").append(tableName).append(" where 1=1 ");
        if (null != initWhere && !initWhere.isEmpty()) {
            stringBuilder.append(" and ").append(initWhere);
        }

        String tmp = String.format("from %s where %s", tableName, initWhere);

        for(int i = 0; i < maxLevel; ++i) {
            tmp = String.format(" from %s where %s in ( select %s %s)", tableName, whereIn, selectIn, tmp);
            stringBuilder.append(" union ").append(select).append(tmp);
        }

        return stringBuilder.toString();
    }

}
TreeUtil

 

  完整main测试

    /**
     * 测试
     */
    public static void main(String[] args) {
        ArrayList<Menu> list = new ArrayList<>();
        list.add(new Menu("1","-1","系统管理",1));
        list.add(new Menu("11","1","菜单维护",1));
        list.add(new Menu("12","1","角色维护",2));
        list.add(new Menu("13","1","系统安全",3));
        list.add(new Menu("131","13","日志管理",1));
        list.add(new Menu("12","-1","用户管理",2));

        //备份list
        List<Menu> list1 = BeanUtil.copyToList(list, Menu.class);

        List<Menu> menus = TreeUtil.toTreeByParentId("-1", list);
        System.out.println(menus);

        Menu menu = TreeUtil.toTreeByChildrenId("131", list1);
        System.out.println(menu);

        String sql = TreeUtil.getChildSql("select id", "lp_sys_menu", "pid = '-1'", "id", "pid", 3);
        System.out.println(sql);
    }

    /**
     * 测试菜单类
     */
    public static class Menu {
        private String id; //节点id
        private String pid; //父节点id
        private List<Menu> children; //子节点
        private int px; //排序字段
        private String menuName; //菜单名称

        public Menu() {
        }

        public Menu(String id, String pid, String name, int px) {
            this.id = id;
            this.pid = pid;
            this.menuName = name;
            this.px = px;
        }

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getPid() {
            return pid;
        }

        public void setPid(String pid) {
            this.pid = pid;
        }

        public List<Menu> getChildren() {
            return children;
        }

        public void setChildren(List<Menu> children) {
            this.children = children;
        }

        public int getPx() {
            return px;
        }

        public void setPx(int px) {
            this.px = px;
        }

        public String getMenuName() {
            return menuName;
        }

        public void setMenuName(String menuName) {
            this.menuName = menuName;
        }

        @Override
        public String toString() {
            return "Menu{" +
                    "id='" + id + '\'' +
                    ", pid='" + pid + '\'' +
                    ", children=" + children +
                    ", px=" + px +
                    ", menuName='" + menuName + '\'' +
                    '}';
        }

    }
View Code

 

  效果展示

[Menu{id='1', pid='-1', children=[Menu{id='11', pid='1', children=null, px=1, menuName='菜单维护'}, Menu{id='12', pid='1', children=null, px=2, menuName='角色维护'}, Menu{id='13', pid='1', children=[Menu{id='131', pid='13', children=null, px=1, menuName='日志管理'}], px=3, menuName='系统安全'}], px=1, menuName='系统管理'}, Menu{id='12', pid='-1', children=null, px=2, menuName='用户管理'}]
Menu{id='1', pid='-1', children=[Menu{id='13', pid='1', children=[Menu{id='131', pid='13', children=null, px=1, menuName='日志管理'}], px=3, menuName='系统安全'}], px=1, menuName='系统管理'}
select id from lp_sys_menu where 1=1  and pid = '-1' union select id from lp_sys_menu where pid in ( select id from lp_sys_menu where pid = '-1') union select id from lp_sys_menu where pid in ( select id  from lp_sys_menu where pid in ( select id from lp_sys_menu where pid = '-1')) union select id from lp_sys_menu where pid in ( select id  from lp_sys_menu where pid in ( select id  from lp_sys_menu where pid in ( select id from lp_sys_menu where pid = '-1')))

 

  原数据

   根据父id,递归获取所有子节点,转为树结构

   根据子id,递归获取所有父节点,转为树结构

   拼接 union sql脚本,根据查询查询条件、id字段名、pid字段名,拼接出sql

SELECT
    id
FROM
    lp_sys_menu
WHERE
    1 = 1
    AND pid = '-1'
UNION
SELECT
    id
FROM
    lp_sys_menu
WHERE
    pid IN (
        SELECT
            id
        FROM
            lp_sys_menu
        WHERE
            pid = '-1'
    )
UNION
SELECT
    id
FROM
    lp_sys_menu
WHERE
    pid IN (
        SELECT
            id
        FROM
            lp_sys_menu
        WHERE
            pid IN (
                SELECT
                    id
                FROM
                    lp_sys_menu
                WHERE
                    pid = '-1'
            )
    )
UNION
SELECT
    id
FROM
    lp_sys_menu
WHERE
    pid IN (
        SELECT
            id
        FROM
            lp_sys_menu
        WHERE
            pid IN (
                SELECT
                    id
                FROM
                    lp_sys_menu
                WHERE
                    pid IN (
                        SELECT
                            id
                        FROM
                            lp_sys_menu
                        WHERE
                            pid = '-1'
                    )
            )
    )

 

 

  后记

  树形结构工具类暂时先记录到这,后续再进行补充

 

posted @ 2024-09-25 15:48  huanzi-qch  阅读(473)  评论(0编辑  收藏  举报