通过postgresql的Ltree字段类型实现目录结构的基本操作

通过postgresql的Ltree字段类型实现目录结构的基本操作

将这种具有目录结构的excel表存储到数据库中,可以采用树型结构存储[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZLXAzpxj-1691660171264)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20230810172124733.png)]

DROP TABLE IF EXISTS "public"."directory_tree";
CREATE TABLE "public"."directory_tree" ("id" varchar(100) COLLATE "pg_catalog"."default","path" "public"."ltree","name" varchar(100) COLLATE "pg_catalog"."default" NOT NULL,"description" text COLLATE "pg_catalog"."default","updated_at" timestamp(6) DEFAULT now(),"created_at" timestamp(6) DEFAULT now()
)
;-- ----------------------------
-- Records of directory_tree
-- ----------------------------
INSERT INTO "public"."directory_tree" VALUES ('04e19944aa1d3d8bc13971b4488a4e0d', '04e19944aa1d3d8bc13971b4488a4e0d', 'root', 'root', '2023-08-09 02:11:35.145821', '2023-08-09 02:11:35.145821');

上面是建一张表,并且插入一条根节点。这里我们的id是mybatisPuls提供的UUID,并且我们的path字段采用祖id+爷id+父id+子id的结构。这是处理excel表格的工具类

package com.cdcas.utils;import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;import java.io.File;
import java.io.IOException;
import java.util.*;/*** @author jiao xn* @date 2023/4/20 22:44* @description*/
@Component
public class ExcelUtil {/*** 根据文件地址,读取指定 Excel 文件的内容,并以对象数组的方式返回** @param excelFilePath Excel 文件地址* @param sheetIndex 指定 Sheet 的索引值,从 0 开始* @param startLine 开始读取的行:从0开始* @param tailLine 去除最后读取的行* @return Excel 文件内容,对象数组*/public List<Map<String, String>> readExcelFile(String excelFilePath, Integer sheetIndex, Integer startLine, Integer tailLine) {Workbook workbook = this.generateWorkbook(excelFilePath);return this.readExcelSheetToObject(workbook, sheetIndex, startLine, tailLine);}/*** 从 MultipartFile 中读取 Excel 文件的内容,并以对象数组的方式返回** @param multipartFile MultipartFile 对象,一般是从前端接收* @param sheetIndex 指定 Sheet 的索引值,从 0 开始* @param startLine 开始读取的行:从0开始* @param tailLine 去除最后读取的行* @return Excel 文件内容,对象数组*/public List<Map<String, String>> readExcelFile(MultipartFile multipartFile, Integer sheetIndex, Integer startLine, Integer tailLine) {Workbook workbook = this.generateWorkbook(multipartFile);return this.readExcelSheetToObject(workbook, sheetIndex, startLine, tailLine);}/*** 生成 Workbook 对象** @param excelFilePath Excel 文件路径* @return Workbook 对象,允许为空*/private Workbook generateWorkbook(String excelFilePath) {Workbook workbook;try {File excelFile = new File(excelFilePath);workbook = WorkbookFactory.create(excelFile);} catch (IOException | InvalidFormatException e) {e.printStackTrace();throw new RuntimeException(e);}return workbook;}/*** 生成 Workbook 对象** @param multipartFile MultipartFile 对象* @return Workbook 对象*/private Workbook generateWorkbook(MultipartFile multipartFile) {Workbook workbook;try {workbook = WorkbookFactory.create(multipartFile.getInputStream());} catch (IOException | InvalidFormatException e) {e.printStackTrace();throw new RuntimeException(e);}return workbook;}/*** 读取指定 Sheet 中的数据** @param workbook Workbook 对象* @param sheetIndex 指定 Sheet 的索引值,从 0 开始* @param startLine 开始读取的行:从0开始* @param tailLine 去除最后读取的行* @return 指定 Sheet 的内容*/private List<Map<String, String>> readExcelSheetToObject(Workbook workbook,Integer sheetIndex, Integer startLine, Integer tailLine) {List<Map<String, String>> result = new ArrayList<>();Sheet sheet = workbook.getSheetAt(sheetIndex);// 获取第一行内容,作为标题内容Row titileRow = sheet.getRow(0);Map<String, String> titleContent = new LinkedHashMap<>();for (int i = 0; i < titileRow.getLastCellNum(); i++) {Cell cell = titileRow.getCell(i);titleContent.put(cell.getStringCellValue(), cell.getStringCellValue());}result.add(titleContent);// 获取正文内容Row row;for (Integer i = startLine; i < sheet.getLastRowNum() - tailLine + 1; i++) {row = sheet.getRow(i);Map<String, String> rowContent = new HashMap<>();for (Cell cell : row) {String returnStr;boolean isMergedCell  = this.isMergedCell(sheet, i, cell.getColumnIndex());if (isMergedCell) {returnStr = this.getMergedRegionValue(sheet, row.getRowNum(), cell.getColumnIndex());} else {returnStr = cell.getRichStringCellValue().getString();}rowContent.put(titileRow.getCell(cell.getColumnIndex()).getStringCellValue(), returnStr);}result.add(rowContent);}return result;}/*** 判断指定的单元格是否是合并单元格** @param sheet Excel 指定的 Sheet 表* @param row 行下标* @param column 列下标* @return 是否为合并的单元格*/private boolean isMergedCell(Sheet sheet, int row, int column) {int sheetMergeCount = sheet.getNumMergedRegions();for (int i = 0; i < sheetMergeCount; i++) {CellRangeAddress range = sheet.getMergedRegion(i);int firstColumn = range.getFirstColumn();int lastColumn = range.getLastColumn();int firstRow = range.getFirstRow();int lastRow = range.getLastRow();if(row >= firstRow && row <= lastRow && (column >= firstColumn && column <= lastColumn)){return true;}}return false;}/*** 获取合并单元格的值** @param sheet 指定的值* @param row 行号* @param column 列好* @return 合并单元格的值*/private String getMergedRegionValue(Sheet sheet, int row, int column){int sheetMergeCount = sheet.getNumMergedRegions();for(int i = 0 ; i < sheetMergeCount ; i++){CellRangeAddress ca = sheet.getMergedRegion(i);int firstColumn = ca.getFirstColumn();int lastColumn = ca.getLastColumn();int firstRow = ca.getFirstRow();int lastRow = ca.getLastRow();if(row >= firstRow && row <= lastRow && (column >= firstColumn && column <= lastColumn)) {Row fRow = sheet.getRow(firstRow);Cell fCell = fRow.getCell(firstColumn);return this.getCellValue(fCell) ;}}return null ;}/*** 获取单元格的值** @param cell Cell 对象* @return 单元格的值*/private String getCellValue(Cell cell){if(cell == null) {return "";}if(cell.getCellTypeEnum() == CellType.STRING){return cell.getStringCellValue();} else if(cell.getCellTypeEnum() == CellType.BOOLEAN){return String.valueOf(cell.getBooleanCellValue());} else if(cell.getCellTypeEnum() == CellType.FORMULA){return cell.getCellFormula() ;} else if(cell.getCellTypeEnum() == CellType.NUMERIC){return String.valueOf(cell.getNumericCellValue());}return "";}
}

下面是将生成的List<Map<String, String>> excel数据插入到excel表中的工具类

package com.cdcas;import com.cdcas.mapper.DirectoryTreeMapper;
import com.cdcas.pojo.DirectoryTree;
import com.cdcas.utils.ExcelDataUtil;
import com.cdcas.utils.ExcelUtil;
import org.junit.jupiter.api.Test;
import org.junit.platform.commons.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.CollectionUtils;import java.io.FileInputStream;
import java.util.HashSet;
import java.util.List;
import java.util.Map;/*** @version 1.0* @Author zhaozhixin* @Date 2023/8/7 15:01* @注释*/   //983
@SpringBootTest
public class Test2 {/*** 替换和插入** @param parentPath* @param directoryTree*/private String Insert(String parentPath, DirectoryTree directoryTree, String name) {directoryTree.setName(name);directoryTree.setDescription(parentPath);directoryTreeMapper.insert(directoryTree);directoryTree.setPath(parentPath + "." + directoryTree.getId());directoryTreeMapper.updateDirectoryTree(directoryTree);return directoryTree.getId();}/*** 通过名称查询父路径** @param name* @return*/private String getParentPathByName(String name) {DirectoryTree parent = directoryTreeMapper.getOneByName(name);return parent.getPath();}@Testpublic void get() {String path = directoryTreeMapper.getPath(1);System.out.println(path);}@Autowiredprivate ExcelUtil excelUtil;@Autowiredprivate ExcelDataUtil excelDataUtil;@Autowiredprivate DirectoryTreeMapper directoryTreeMapper;@Testpublic void insert() throws Exception {//读取一个excelList<Map<String, String>> maps = excelUtil.readExcelFile("C:\\Users\\20745\\Desktop\\git库\\gitee\\pg-demo-itree\\src\\main\\resources\\国土规划目录树.xlsx", 0, 1, 0);maps.remove(0);System.out.println(maps);for (Map<String, String> map : maps) {String A1 = map.get("A1");String A2 = map.get("A2");String A3 = map.get("A3");String A4 = map.get("A4");String A5 = map.get("A5");String A6 = map.get("A6");String A7 = map.get("A7");String A8 = map.get("A8");String A9 = map.get("A9");StringBuilder parentPath = new StringBuilder();//用来拼接父节点idparentPath.append("04e19944aa1d3d8bc13971b4488a4e0d");//这是根节点idif (A1 != null && !"".equals(A1)) {//二级节点  根节点为rootDirectoryTree directoryTree = new DirectoryTree();String name = A1;String id = isExtis(name, directoryTree, parentPath.toString());if (id==null){}else {parentPath.append(".").append(id);}}if (A2 != null && !"".equals(A2)) {//拿到所有同名的行DirectoryTree directoryTree = new DirectoryTree();String name = A2;String id = isExtis(name, directoryTree, parentPath.toString());if (id==null){}else {parentPath.append(".").append(id);}}if (A3 != null && !"".equals(A3)) {DirectoryTree directoryTree = new DirectoryTree();String name = A3;String id = isExtis(name, directoryTree, parentPath.toString());if (id==null){}else {parentPath.append(".").append(id);}}if (A4 != null && !"".equals(A4)) {DirectoryTree directoryTree = new DirectoryTree();String name = A4;String id = isExtis(name, directoryTree, parentPath.toString());if (id==null){}else {parentPath.append(".").append(id);}}if (A5 != null && !"".equals(A5)) {DirectoryTree directoryTree = new DirectoryTree();String name = A5;String id = isExtis(name, directoryTree, parentPath.toString());if (id==null){}else {parentPath.append(".").append(id);}}if (A6 != null && !"".equals(A6)) {DirectoryTree directoryTree = new DirectoryTree();String name = A6;String id = isExtis(name, directoryTree, parentPath.toString());if (id==null){}else {parentPath.append(".").append(id);}}if (A7 != null && !"".equals(A7)) {DirectoryTree directoryTree = new DirectoryTree();String name = A7;String id = isExtis(name, directoryTree, parentPath.toString());if (id==null){}else {parentPath.append(".").append(id);}}if (A8 != null && !"".equals(A8)) {DirectoryTree directoryTree = new DirectoryTree();String name = A8;String id = isExtis(name, directoryTree, parentPath.toString());if (id==null){}else {parentPath.append(".").append(id);}}if (A9 != null && !"".equals(A9)) {DirectoryTree directoryTree = new DirectoryTree();String name = A9;String id = isExtis(name, directoryTree, parentPath.toString());if (id==null){}else {parentPath.append(".").append(id);}}}}/*** 判断一个同名的是否存在在其它数 返回当前节点id* @param name* @param directoryTree* @param parentPath*/private String isExtis(String name,DirectoryTree directoryTree,String parentPath){//最终标识  开始表明不存在boolean isExtis = false;//获取到所有的和name同名的行List<DirectoryTree> oneByNames = directoryTreeMapper.getListByName(name);//如果没有同名的直接插入if (CollectionUtils.isEmpty(oneByNames)){directoryTree.setName(name);directoryTree.setDescription(parentPath);directoryTreeMapper.insert(directoryTree);directoryTree.setPath(parentPath+"."+directoryTree.getId());directoryTreeMapper.updateDirectoryTree(directoryTree);return directoryTree.getId();}//如果有同名的,需要判断父路径是否相同String path = "";int lastIndexOf = 0;for (DirectoryTree oneByName : oneByNames) {if (oneByName!=null) {path = oneByName.getPath();lastIndexOf = path.lastIndexOf(".");}//重复的数据应该也要被插入进去  查出的父路径传入的父路径进行对比if (path.substring(0,lastIndexOf).equals(parentPath)){isExtis = true;}}//最后如果同名的数据但是父路径不相同,就需要插入进去if (!isExtis){directoryTree.setName(name);directoryTree.setDescription(parentPath);directoryTreeMapper.insert(directoryTree);directoryTree.setPath(parentPath+"."+directoryTree.getId());directoryTreeMapper.updateDirectoryTree(directoryTree);return directoryTree.getId();}return oneByNames.get(oneByNames.size()-1).getId();}//查看重复条数和 总条数@Testpublic void look() throws Exception {FileInputStream fileInputStream = new FileInputStream("C:\\Users\\20745\\Desktop\\git库\\gitee\\pg-demo-itree\\src\\main\\resources\\国土规划目录树.xlsx");List<Map<String, String>> maps = excelDataUtil.readExcel(fileInputStream);int count = 0;HashSet<String> set = new HashSet();for (Map<String, String> map : maps) {if (StringUtils.isNotBlank(map.get("A1"))) {set.add(map.get("A1"));count++;}if (StringUtils.isNotBlank(map.get("A2"))) {set.add(map.get("A2"));count++;}if (StringUtils.isNotBlank(map.get("A3"))) {set.add(map.get("A3"));count++;}if (StringUtils.isNotBlank(map.get("A4"))) {set.add(map.get("A4"));count++;}if (StringUtils.isNotBlank(map.get("A5"))) {set.add(map.get("A5"));count++;}if (StringUtils.isNotBlank(map.get("A6"))) {set.add(map.get("A6"));count++;}if (StringUtils.isNotBlank(map.get("A7"))) {set.add(map.get("A7"));count++;}if (StringUtils.isNotBlank(map.get("A8"))) {set.add(map.get("A8"));count++;}if (StringUtils.isNotBlank(map.get("A9"))) {set.add(map.get("A9"));count++;}}System.out.println(count);System.out.println(set.size());}
}

最后插入的数据大概是这样[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-449koFvW-1691660171266)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20230810172712617.png)]

注意这里的path!!!!!是id拼起来的具有目录层次的!![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UHQCBxy4-1691660171267)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20230810173241809.png)]

这些关于目录树的基本操作,楼主写了一个小demo放在gitee上面了。本人不会算法,里面写的很菜见谅哈哈。喜欢的点个赞谢谢<.>! gitee地址:pg-demo-itree: 基于postgresql的Itree功能实现目录树的操作 (gitee.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/48662.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

产品流程图是什么?怎么做?

产品流程图是什么&#xff1f; 产品流程图是一种图形化的表达方式&#xff0c;用于描述产品开发、制造、销售、使用等各个阶段中涉及的流程、步骤和关系。它通过图形符号、箭头、文本等元素&#xff0c;展示了产品的各个环节之间的关联和顺序&#xff0c;通常被用于可视化产…

lwIP更新记10:IP 冲突检测

lwip-2.2.0-rc1 版本于 2023 年 6 月 29 日发布&#xff0c;带来了我期盼已久的 IPv4 冲突检测 功能。 lwip-2.2.0-rc1 版本重新回归了 master 分支&#xff08;主分支&#xff09;&#xff0c;不再使用单独的稳定分支。 master 分支 是一个 Git&#xff08;版本控制程序&…

[保研/考研机试] KY196 复数集合 北京邮电大学复试上机题 C++实现

题目链接&#xff1a; 复数集合_牛客题霸_牛客网 一个复数&#xff08;xiy&#xff09;集合&#xff0c;两种操作作用在该集合上&#xff1a; 1、Pop 表示读出集。题目来自【牛客题霸】https://www.nowcoder.com/share/jump/437195121692724009060 描述 一个复数&#xff08;…

如何做好流量经营?数字化系统如何加速流量增长

​在用户转化策略上&#xff0c;从“公域流量”到“私域流量”的来源转变&#xff0c;充分说明企业已经意识到公域流量存在成本高、粘度差、稳定性差等问题&#xff0c;开始寻求拥有更低成本、更容易培养忠实度、更容易精准触达的私域流量。但由于企业缺少整体、系统化的私域经…

深入浅出 TCP/IP 协议栈

TCP/IP 协议栈是一系列网络协议的总和&#xff0c;是构成网络通信的核心骨架&#xff0c;它定义了电子设备如何连入因特网&#xff0c;以及数据如何在它们之间进行传输。TCP/IP 协议采用4层结构&#xff0c;分别是应用层、传输层、网络层和链路层&#xff0c;每一层都呼叫它的下…

SSD基本工作原理了解

SSD与RAM的原理有些类似&#xff0c;RAM使用晶体管和电容来表示0或1&#xff0c;晶体管用于将电荷转移到电容器或从电容器中吸取电荷&#xff0c;并且电荷必须每几微秒刷新一次。 而SSD相比于RAM的非易失性来自于其使用的浮栅晶体管。其创造了一个小笼子&#xff0c;不需要外界…

适配器模式实现stack和queue

适配器模式实现stack和queue 什么是适配器模式&#xff1f;STL标准库中stack和queue的底层结构stack的模拟实现queue的模拟实现 什么是适配器模式&#xff1f; 适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结)&#xff…

MPP 还是主流架构吗

MPP 架构&#xff1a; MPP 架构的产品&#xff1a; Impala ClickHouse Druid Doris 很多 OLAP 引擎都采用了 MPP 架构 批处理系统 - 使用场景分钟级、小时级以上的任务&#xff0c;目前很多大型互联网公司都大规模运行这样的系统&#xff0c;稳定可靠&#xff0c;低成本。…

<深度学习基础> 激活函数

为什么需要激活函数&#xff1f;激活函数的作用&#xff1f; 激活函数可以引入非线性因素&#xff0c;可以学习到复杂的任务或函数。如果不使用激活函数&#xff0c;则输出信号仅是一个简单的线性函数。线性函数一个一级多项式&#xff0c;线性方程的复杂度有限&#xff0c;从…

如何在服务器上用kaggle下载数据集

S1 服务器上安装kaggle cli工具 pip install --user kaggleS2 服务器上创建kaggle目录 mkdir ~/.kaggleS3 进入kaggle账户创建token 生成token 点击右上角头像&#xff0c;选择setting 点击create new token 进入你的浏览器下载页&#xff0c;可以看到有了一个kaggle.jso…

【Linux操作系统】Linux系统编程中信号捕捉的实现

在Linux系统编程中&#xff0c;信号是一种重要的机制&#xff0c;用于实现进程间通信和控制。当某个事件发生时&#xff0c;如用户按下CtrlC键&#xff0c;操作系统会向进程发送一个信号&#xff0c;进程可以捕获并相应地处理该信号。本篇博客将介绍信号的分类、捕获与处理方式…

ImportError: cannot import name ‘SQLDatabaseChain‘ from ‘langchain‘解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

深度学习基本理论下篇:(梯度下降/卷积/池化/归一化/AlexNet/归一化/Dropout/卷积核)、深度学习面试

深度学习基本理论上篇&#xff1a;&#xff08;MLP/激活函数/softmax/损失函数/梯度/梯度下降/学习率/反向传播&#xff09; 深度学习基本理论上篇&#xff1a;&#xff08;MLP/激活函数/softmax/损失函数/梯度/梯度下降/学习率/反向传播&#xff09;、深度学习面试_会害羞的杨…

全国城市内涝排涝模拟技术及在市政、规划设计中应用教程

详情点击链接&#xff1a;全国城市内涝排涝模拟技术及在市政、规划设计中应用教程 一&#xff0c;数据准备 通过标准化的步骤&#xff0c;利用CAD数据、GIS数据&#xff0c;在建模的不同阶段发挥不同软件的优势&#xff0c;实现高效的数据处理、准确的参数赋值、模型的快速建…

Maven 配置文件修改及导入第三方jar包

设置java和maven的环境变量 修改maven配置文件 &#xff08;D:\app\apache-maven-3.5.0\conf\settings.xml&#xff0c;1中环境变量对应的maven包下的conf&#xff09; 修改131行左右的mirror&#xff0c;设置阿里云的仓库地址 <mirror> <id>alimaven</id&g…

无涯教程-PHP - sql_regcase()函数

sql_regcase() - 语法 string sql_regcase (string string) 可以将sql_regcase()函数视为实用程序函数&#xff0c;它将输入参数字符串中的每个字符转换为包含两个字符的带括号的表达式。 sql_regcase() - 返回值 返回带括号的表达式字符串以及转换后的字符。 sql_regcase…

[Mac软件]MacCleaner 3 PRO 3.2.1应用程序清理和卸载

应用介绍 MacCleaner PRO是一个应用程序包&#xff0c;将帮助您清除磁盘空间并加快Mac的速度&#xff01; MacCleaner PRO - 让您的Mac始终快速、干净和有条理。 App Cleaner & Uninstaller PRO - 完全删除未使用的应用程序并管理Mac扩展。 磁盘空间分析仪PRO-分析磁盘空…

PHP求职招聘系统Dreamweaver开发mysql数据库web结构php编程计算机网页

一、源码特点 PHP 求职招聘系统是一套完善的web设计系统&#xff0c;对理解php编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 源码 https://download.csdn.net/download/qq_41221322/88240283 论文 https://down…

(一)idea连接GitHub的全部流程(注册GitHub、idea集成GitHub、增加合作伙伴、跨团队合作、分支操作)

&#xff08;二&#xff09;Git在公司中团队内合作和跨团队合作和分支操作的全部流程&#xff08;一篇就够&#xff09;https://blog.csdn.net/m0_65992672/article/details/132336481 4.1、简介 Git是一个免费的、开源的*分布式**版本控制**系统*&#xff0c;可以快速高效地…

华为云零代码新手教学-体验通过Astro Zero快速搭建微信小程序

您将会学到 您将学会如何基于Astro零代码能力&#xff0c;DIY开发&#xff0c;完成问卷、投票、信息收集、流程处理等工作&#xff0c;还能够在线筛选、分析数据。实现一站式快速开发个性化应用&#xff0c;体验轻松拖拽开发的乐趣。 您需要什么 环境准备 注册华为云账号、实…