Java开发项目设计树形表结构和数据处理

目录

1、添加依赖

2、创建表和插入数据

3、返回的树形结构实体

4、treeUtil工具类

5、查询数据

5.1 一次性查出所有

5.2 根据父类ID查询

5.3 调用方法返回树形结构数据

5.4 根据ID查询所有父节点


1、添加依赖

<dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
            <exclusions>
                <exclusion>
                    <groupId>io.swagger</groupId>
                    <artifactId>swagger-annotations</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>io.swagger</groupId>
                    <artifactId>swagger-models</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>1.5.21</version>
        </dependency>

        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
            <version>1.5.21</version>
        </dependency>
<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

2、创建表和插入数据

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for data_folder
-- ----------------------------
DROP TABLE IF EXISTS `data_folder`;
CREATE TABLE `data_folder`  (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `parent_id` bigint(20) NULL DEFAULT NULL COMMENT '父类ID',
  `type` int(3) NULL DEFAULT NULL COMMENT '文件夹类型(0-数据模型,1-数据连接)',
  `data_id` bigint(20) NULL DEFAULT NULL COMMENT '关联的数据ID',
  `folder_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NULL DEFAULT NULL COMMENT '文件夹名称',
  `data_type` int(3) NULL DEFAULT NULL COMMENT '文件类型(0-文件夹,1-数据模型,2-数据连接_EXCEL,3-数据连接_CSV,4-数据连接_ORACLE,5-数据连接_MYSQL)',
  `user_id` bigint(20) NULL DEFAULT NULL COMMENT '创建用户id',
  `update_user_id` bigint(20) NULL DEFAULT NULL COMMENT '修改用户id',
  `create_time` datetime(0) NULL DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime(0) NULL DEFAULT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 22 CHARACTER SET = utf8 COLLATE = utf8_bin COMMENT = '数据准备' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of data_folder
-- ----------------------------
INSERT INTO `data_folder` VALUES (1, 0, 0, NULL, '数据模型-查询平台', 0, 1, NULL, '2022-05-05 15:26:51', '2022-05-10 16:48:52');
INSERT INTO `data_folder` VALUES (2, 0, 0, NULL, '数据模型-共享平台', 0, 1, NULL, '2022-05-05 15:27:02', NULL);
INSERT INTO `data_folder` VALUES (3, 0, 1, NULL, '数据连接-交换平台', 0, 1, NULL, '2022-05-05 15:27:19', NULL);
INSERT INTO `data_folder` VALUES (4, 0, 1, NULL, '数据连接-查询平台', 0, 1, NULL, '2022-05-05 15:26:51', NULL);
INSERT INTO `data_folder` VALUES (5, 0, 1, NULL, '数据连接-共享平台', 0, 1, NULL, '2022-05-05 15:27:02', NULL);
INSERT INTO `data_folder` VALUES (6, 0, 0, NULL, '数据模型-交换平台', 0, 1, NULL, '2022-05-05 15:27:19', NULL);
INSERT INTO `data_folder` VALUES (9, 3, 1, 1, '登录日志表', 2, 1, NULL, '2022-05-06 10:46:12', NULL);
INSERT INTO `data_folder` VALUES (10, 3, 1, 2, '22服务器mysql测试连接', 5, 1, NULL, '2022-05-06 10:46:12', NULL);
INSERT INTO `data_folder` VALUES (11, 3, 1, 3, 'CSV上传文件', 3, 1, 2, '2022-05-06 10:46:12', '2022-05-09 15:41:16');
INSERT INTO `data_folder` VALUES (12, 3, 1, 4, '22服务器oracle数据库测试连接', 4, 1, NULL, '2022-05-06 10:46:12', NULL);
INSERT INTO `data_folder` VALUES (13, 3, 1, NULL, '文件夹1', 0, 1, NULL, '2022-05-06 11:51:37', NULL);
INSERT INTO `data_folder` VALUES (14, 13, 1, 10, '用户信息表', 2, 1, NULL, '2022-05-06 11:52:29', NULL);
INSERT INTO `data_folder` VALUES (15, 0, 0, 1, '数据模型1', 1, 1, NULL, '2022-05-09 15:57:54', NULL);
INSERT INTO `data_folder` VALUES (16, 0, 0, NULL, '新建文件夹-1', 0, NULL, NULL, '2022-05-10 16:33:55', '2022-05-10 16:34:09');
INSERT INTO `data_folder` VALUES (17, 16, 0, NULL, '子文件夹', 0, NULL, NULL, '2022-05-10 16:34:27', '2022-05-10 16:34:27');
INSERT INTO `data_folder` VALUES (18, 17, 0, NULL, '1', 0, NULL, NULL, '2022-05-10 16:35:15', '2022-05-10 16:35:15');
INSERT INTO `data_folder` VALUES (19, 1, 0, NULL, '子文件', 0, NULL, NULL, '2022-05-10 16:43:40', '2022-05-10 16:43:40');
INSERT INTO `data_folder` VALUES (21, 1, 0, NULL, '112', 0, NULL, NULL, '2022-05-10 16:53:39', '2022-05-10 16:53:56');

SET FOREIGN_KEY_CHECKS = 1;

3、返回的树形结构实体

package com.shucha.smartreport.biz.dto;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.List;

/**
 * @author tqf
 * @Description
 * @Version 1.0
 * @since 2022-05-06 11:00
 */
@Data
@ApiModel(value = "模型、数据连接树形结构数据")
public class DataFolderDTO {

    @ApiModelProperty(value = "主键id")
    private Long id;

    @ApiModelProperty(value = "父id")
    private Long parentId;

    @ApiModelProperty(value = "文件夹类型(0-数据模型,1-数据连接)")
    private Integer type;

    @ApiModelProperty(value = "文件类型(0-文件夹,1-数据模型,2-数据连接_EXCEL,3-数据连接_CSV,4-数据连接_ORACLE,5-数据连接_MYSQL)")
    private Integer dataType;

    @ApiModelProperty(value = "名称")
    private String folderName;

    @ApiModelProperty(value = "关联的数据ID")
    private Long dataId;

    @ApiModelProperty(value = "子类数据")
    private List<DataFolderDTO> children;
}

4、treeUtil工具类

package com.shucha.smartreport.util;

import org.springframework.util.StringUtils;

import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * list -> tree
 * 将普通list数据转换为树形数据结构
 */
public class TreeUtil {

    /**
     * 集合转树结构
     *
     * @param list  目标集合
     * @param clazz 集合元素类型
     * @return 转换后的树形结构
     */
    public static <T> List<T> toTree(@NotNull List<T> list, @NotNull Class<T> clazz) {
        return toTree(list, null, null, null, clazz);
    }

    /**
     * 集合转树结构
     *
     * @param list     目标集合列表
     * @param id       节点编号字段名称
     * @param parent   父节点编号字段名称
     * @param children 子节点集合属性名称
     * @param clazz    集合元素类型
     * @return 转换后的树形结构
     */
    public static <T> List<T> toTree(@NotNull List<T> list, String id, String parent, String children, @NotNull Class<T> clazz) {
        try {
            // 如果目标集合为空,直接返回一个空树
            if (list == null || list.isEmpty()) return null;
            // 如果被依赖字段名称为空则默认为id
            if (StringUtils.isEmpty(id)) id = "id";
            // 如果依赖字段为空则默认为parent
            if (StringUtils.isEmpty(parent)) parent = "pid";
            // 如果子节点集合属性名称为空则默认为children
            if (StringUtils.isEmpty(children)) children = "children";

            // 初始化根节点集合, 支持 Set 和 List
            List<T> roots = new ArrayList<>();

            // 获取 id 字段, 从当前对象或其父类
            Field idField;
            try {
                idField = clazz.getDeclaredField(id);
            } catch (NoSuchFieldException e1) {
                idField = clazz.getSuperclass().getDeclaredField(id);
            }

            // 获取 parentId 字段, 从当前对象或其父类
            Field parentField;
            try {
                parentField = clazz.getDeclaredField(parent);
            } catch (NoSuchFieldException e1) {
                parentField = clazz.getSuperclass().getDeclaredField(parent);
            }

            // 获取 children 字段, 从当前对象或其父类
            Field childrenField;
            try {
                childrenField = clazz.getDeclaredField(children);
            } catch (NoSuchFieldException e1) {
                childrenField = clazz.getSuperclass().getDeclaredField(children);
            }

            // 设置为可访问
            idField.setAccessible(true);
            parentField.setAccessible(true);
            childrenField.setAccessible(true);

            // 找出所有的根节点
            for (T c : list) {
                Object parentId = parentField.get(c);
                if (isRootNode(parentId)) {
                    roots.add(c);
                }
            }

            // 从目标集合移除所有根节点
            list.removeAll(roots);

            // 遍历根节点, 依次添加子节点
            for (T root : roots) {
                addChild(root, list, idField, parentField, childrenField);
            }

            // 关闭可访问
            idField.setAccessible(false);
            parentField.setAccessible(false);
            childrenField.setAccessible(false);

            return roots;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 为目标节点添加孩子节点
     *
     * @param node          目标节点
     * @param list          目标集合
     * @param idField       ID 字段
     * @param parentField   父节点字段
     * @param childrenField 字节点字段
     */
    private static <T> void addChild(@NotNull T node, @NotNull List<T> list, @NotNull Field idField, @NotNull Field parentField, @NotNull Field childrenField) throws IllegalAccessException {
        Object id = idField.get(node);
        List<T> children = (List<T>) childrenField.get(node);
        // 如果子节点的集合为 null, 初始化孩子集合
        if (children == null) {
            children = new ArrayList<>();
        }

        for (T t : list) {
            Object o = parentField.get(t);
            if (id.equals(o)) {
                // 将当前节点添加到目标节点的孩子节点
                children.add(t);
                // 重设目标节点的孩子节点集合,这里必须重设,因为如果目标节点的孩子节点是null的话,这样是没有地址的,就会造成数据丢失,所以必须重设,如果目标节点所在类的孩子节点初始化为一个空集合,而不是null,则可以不需要这一步,因为java一切皆指针
                childrenField.set(node, children);
                // 递归添加孩子节点
                addChild(t, list, idField, parentField, childrenField);
            }
        }
    }

    /**
     * 判断是否是根节点, 判断方式为: 父节点编号为空或为 0, 则认为是根节点. 此处的判断应根据自己的业务数据而定.
     *
     * @param parentId 父节点编号
     * @return 是否是根节点
     */
    private static boolean isRootNode(Object parentId) {
        boolean flag = false;
        if (parentId == null) {
            flag = true;
        } else if (parentId instanceof String && (StringUtils.isEmpty(parentId) || parentId.equals("0"))) {
            flag = true;
        } else if (parentId instanceof Integer && Integer.valueOf(0).equals(parentId)) {
            flag = true;
        } else if (parentId instanceof Long && Long.valueOf("0").equals(parentId)) {
            flag = true;
        }
        return flag;
    }

}

5、查询数据

5.1 一次性查出所有

SELECT * FROM `data_folder` WHERE type = 1

  

5.2 根据父类ID查询

-- 开发使用
SELECT
	id.LEVEL,
	data.* 
FROM
	(
	SELECT
		@ids AS _ids,
		( SELECT @ids := GROUP_CONCAT( id ) FROM data_folder WHERE FIND_IN_SET(parent_id, @ids ) ) AS cids,
		@l := @l + 1 AS LEVEL 
	FROM
		data_folder,
		( SELECT @ids := 3, @l := 0 ) b 
	WHERE
		@ids IS NOT NULL 
	) id,
	data_folder data 
WHERE
	FIND_IN_SET( data.id, id._ids ) 
ORDER BY
	LEVEL,
	id;
	
-- 原始的sql
	
	SELECT
	ID.LEVEL,
	DATA.* 
FROM
	(
	SELECT
		@ids AS _ids,
		( SELECT @ids := GROUP_CONCAT( id ) FROM 表名 WHERE FIND_IN_SET( 父级 id字段, @ids ) ) AS cids,
		@l := @l + 1 AS LEVEL 
	FROM
		表名,
		( SELECT @ids := '条件id', @l := 0 ) b 
	WHERE
		@ids IS NOT NULL 
	) ID,
	表名 DATA 
WHERE
	FIND_IN_SET( DATA.id, ID._ids ) 
ORDER BY
	LEVEL,
	id

5.3 调用方法返回树形结构数据

通过5.1和5.2查询得到的数据列表之后,然后调用下面的方法得到树形结构数

// 1、folderName不为空模糊查询时候,需要查询数据的父ID
        List<Long> ids = new ArrayList<>();
        if (StringUtil.isNotBlank(folderName)) {
            ids = getParentIdList(folderName, type);
        }

// 5.1的方法
List<DataFolder> list = dataFolderService.list(Wrappers.<DataFolder>lambdaQuery()
                .in(ids.size() > 0, DataFolder::getId, ids)
                .eq(DataFolder::getType,1));
        ArrayList<DataFolderDTO> folderDTOS = new ArrayList<>();
        if (list.size() > 0) {
            for (DataFolder folder : list) {
                DataFolderDTO dto = new DataFolderDTO();
                dto.setId(folder.getId());
                dto.setParentId(folder.getParentId());
                dto.setDataId(folder.getDataId());
                dto.setFolderName(folder.getFolderName());
                dto.setDataType(folder.getDataType());
                dto.setType(folder.getType());
                folderDTOS.add(dto);
            }
        }
List<DataFolderDTO> powerListDTOSs = TreeUtil.toTree(folderDTOS, "id", "parentId", "children", DataFolderDTO.class);


/**
     * 数据准备 树形结构模糊查询获取父ID
     *
     * @param folderName
     * @param type
     * @return
     */
    List<Long> getParentIdList(String folderName, Integer type) {
        List<Long> ids = new ArrayList<>();
        List<DataFolder> likeList = dataFolderService.list(Wrappers.<DataFolder>lambdaQuery()
                .select(DataFolder::getParentId)
                .eq(DataFolder::getType, type)
                .like(DataFolder::getFolderName, folderName)
                .groupBy(DataFolder::getParentId));
        if (likeList.size() > 0) {
            ids = likeList.stream().map(DataFolder::getParentId).collect(Collectors.toList());
        }
        return ids;
    }

得到的数据格式如下:

{
  "code": 0,
  "success": true,
  "message": "ok",
  "data": [
    {
      "id": 3,
      "parentId": 0,
      "type": 1,
      "dataType": 0,
      "folderName": "数据连接-交换平台",
      "dataId": null,
      "children": [
        {
          "id": 13,
          "parentId": 3,
          "type": 1,
          "dataType": 0,
          "folderName": "文件夹1",
          "dataId": null,
          "children": [
            {
              "id": 14,
              "parentId": 13,
              "type": 1,
              "dataType": 2,
              "folderName": "用户信息表",
              "dataId": 10,
              "children": null
            }
          ]
        },
        {
          "id": 9,
          "parentId": 3,
          "type": 1,
          "dataType": 2,
          "folderName": "登录日志表",
          "dataId": 1,
          "children": null
        },
        {
          "id": 10,
          "parentId": 3,
          "type": 1,
          "dataType": 5,
          "folderName": "22服务器mysql测试连接",
          "dataId": 2,
          "children": null
        },
        {
          "id": 11,
          "parentId": 3,
          "type": 1,
          "dataType": 3,
          "folderName": "CSV上传文件",
          "dataId": 3,
          "children": null
        },
        {
          "id": 12,
          "parentId": 3,
          "type": 1,
          "dataType": 4,
          "folderName": "22服务器oracle数据库测试连接",
          "dataId": 4,
          "children": null
        }
      ]
    },
    {
      "id": 5,
      "parentId": 0,
      "type": 1,
      "dataType": 0,
      "folderName": "数据连接-共享平台",
      "dataId": null,
      "children": null
    },
    {
      "id": 4,
      "parentId": 0,
      "type": 1,
      "dataType": 0,
      "folderName": "数据连接-查询平台",
      "dataId": null,
      "children": null
    }
  ]
}

5.4 根据ID查询所有父节点

SELECT t2.id, t2.`folder_name`
FROM
    (
        SELECT 
        @r AS _id,
        (SELECT @r := parent_id FROM data_folder WHERE id = _id) AS parent_id,
        @l := @l + 1 AS lvl
        FROM
        (SELECT @r := 13, @l := 0) vars, data_folder AS h
        WHERE @r <> 0 
    ) t1
JOIN data_folder t2
ON t1._id = t2.Id
ORDER BY id asc;




SELECT GROUP_CONCAT(folder_name ORDER BY t2.id asc SEPARATOR ' / ')
FROM
    (
        SELECT 
        @r AS _id,
        (SELECT @r := parent_id FROM data_folder WHERE id = _id) AS parent_id,
        @l := @l + 1 AS lvl
        FROM
        (SELECT @r := 13, @l := 0) vars, data_folder AS h
        WHERE @r <> 0 
    ) t1
JOIN data_folder t2
ON t1._id = t2.Id
ORDER BY id asc;


SELECT IFNULL(GROUP_CONCAT(folder_name ORDER BY t2.id asc SEPARATOR ' / '),'主目录')  as file_path
FROM
    (
        SELECT 
        @r AS _id,
        (SELECT @r := parent_id FROM data_folder WHERE id = _id) AS parent_id,
        @l := @l + 1 AS lvl
        FROM
        (SELECT @r := 13, @l := 0) vars, data_folder AS h
        WHERE @r <> 0 
    ) t1
JOIN data_folder t2 ON t1._id = t2.Id
ORDER BY id asc;

posted @ 2022-08-04 19:11  码奴生来只知道前进~  阅读(37)  评论(0编辑  收藏  举报