通过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.rhkb.cn/news/98801.html

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

相关文章

【李沐】3.5、softmax回归的从0开始实现

注意&#xff1a; 把每个像素位置看作⼀个特征 # 导入PyTorch库 import torch # 从IPython库中导入display模块&#xff0c;用于在交互式环境中显示内容 from IPython import display # 从d2l.torch模块中导入torch作为d2l的别名&#xff0c;方便后续使用d2l库中的功能 from d…

rabbitMq安装后无法启动可视化页面http://localhost:15672处理

本次安装环境信息&#xff1a; 系统&#xff1a;win10 64位专业版 erlang&#xff1a;otp_win64_23.0 rabbitMQ&#xff1a;rabbitmq-server-3.8.5 安装rabbitMQ需要依赖erlang语言环境&#xff0c;所以需要我们下载erlang的环境安装程序。 一、下载安装程序 rabbitMQ安装…

UGUI可视化组件Image, RawImage

一.组件Image 1.1 Image的属性 创建的Image对象自带Image组件&#xff0c;用来显示图片&#xff0c;其属性说明如下 属性&#xff1a;功能&#xff1a;Source Image表示要显示的图像的纹理&#xff08;必须作为精灵导入&#xff09;。Color要应用于图像的颜色&#xff0c;会和…

Matlab论文插图绘制模板第108期—特征渲染的标签散点图

在之前的文章中&#xff0c;分享了Matlab标签散点图的绘制模板&#xff1a; 进一步&#xff0c;再来分享一下特征渲染的标签散点图的绘制模板&#xff0c;以便再添加一个维度的信息。 先来看一下成品效果&#xff1a; 特别提示&#xff1a;本期内容『数据代码』已上传资源群中…

Vue-13.创建完整的Vue项目(vue+vue-cli+js)-1

前言 之前写了命令创建Vue项目&#xff0c;但是事实上我们可以直接用编译器直接创建项目&#xff0c;这里我使用webstorm&#xff08;因为我是前后端兼修的所以我习惯使用Idea家族的编译器&#xff09; 只写前端的推荐用VsCode前后端都写的推荐用webstorm 新建项目 项目初始…

框架分析(1)-IT人必须会

框架分析&#xff08;1&#xff09;-IT人必须会 专栏介绍当今主流框架前端框架后端框架移动应用框架数据库框架测试框架 Angular关键特点和功能&#xff1a;组件化架构双向数据绑定依赖注入路由功能强大的模板语法测试友好 优缺点分析优点缺点 总结 专栏介绍 link 主要对目前市…

爱荷华州的一个学区正在使用ChatGPT来决定禁止哪些书籍

为了响应爱荷华州最近颁布的立法&#xff0c;管理员们正在从梅森市学校图书馆移除禁书&#xff0c;官员们正在使用ChatGPT帮助他们挑选书籍&#xff0c;根据公报和大众科学. 由州长金雷诺兹签署的禁令背后的新法律是教育改革浪潮的一部分&#xff0c;共和党立法者认为这是保护…

JVM性能分析-jstat工具观察gc频率

jstat jstat是java自带的工具&#xff0c;在bin目录下 用法 语法&#xff1a;jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]] [kqkyyj-2 bin]$ jstat -help Usage: jstat -help|-optionsjstat -<option> [-t] [-h&l…

Servlet+Jsp+JDBC实现房屋租赁管理系统(源码+数据库+论文+系统详细配置指导+ppt)

一、项目简介 本项目是一套基于ServletJsp房屋租赁管理系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;…

redis-数据类型及样例

一.string 类型数据的基本操作 1.添加/修改数据 set key value2.获取数据 get key3.删除数据 del key4.添加/修改多个数据 mset key1 value1 key2 value25.获取多个数据 mget key1 key2二.list类型的基本操作 数据存储需求&#xff1a;存储多个数据&#xff0c;并对数据…

MySQL8.0.26-Linux版安装

MySQL8.0.26-Linux版安装 1. 准备一台Linux服务器 云服务器或者虚拟机都可以; Linux的版本为 CentOS7; 2. 下载Linux版MySQL安装包 MySQL :: Download MySQL Community Server (Archived Versions) 3. 上传MySQL安装包 4. 创建目录,并解压 mkdir mysql ​ tar -xvf mysql-8…

Django框架简单搭建增删改查页面 Django请求生命周期流程图

在数据库中准备好数据 三、将MySQL的数据展示到页面&#xff08;简单认识HTML模板语法 for循环&#xff09; 在Django项目views.py文件中利用ORM模型语法查找所有的数据 def user_list(request):# 1.获取user表中所有的数据展示到html页面上user_data models.UserInfo.object…

SpringBoot + MyBatis-Plus构建树形结构的几种方式

1. 树形结构 树形结构&#xff0c;是指&#xff1a;数据元素之间的关系像一颗树的数据结构。由树根延伸出多个树杈 它具有以下特点&#xff1a; 每个节点都只有有限个子节点或无子节点&#xff1b;没有父节点的节点称为根节点&#xff1b;每一个非根节点有且只有一个父节点&a…

6个主流的工业3D管道设计软件

3D 管道设计软件是大多数行业工程工作的主要部分&#xff0c;例如&#xff1a; 电力、石油和天然气、石化、炼油厂、纸浆和造纸、化学品和加工业。 全球各工程公司使用了近 50 种工厂或管道设计软件。 每个软件都有优点和缺点&#xff0c;包括价格点。 EPC 和业主部门当前的趋势…

【正点原子STM32连载】第十五章 窗口看门狗实验 摘自【正点原子】APM32F407最小系统板使用指南

1&#xff09;实验平台&#xff1a;正点原子stm32f103战舰开发板V4 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id609294757420 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html# 第十…

使用 Apache Kafka 和 Go 将数据引入 OpenSearch

需要编写自定义集成层来满足数据管道中的特定要求&#xff1f;了解如何使用 Go 通过 Kafka 和 OpenSearch 实现此目的。 可扩展的数据摄取是OpenSearch等大规模分布式搜索和分析引擎的一个关键方面。构建实时数据摄取管道的方法之一是使用Apache Kafka。它是一个开源事件流平台…

无涯教程-Perl - unshift函数

描述 此函数按顺序将LIST中的元素放在ARRAY的开头。这与shift()相反。 语法 以下是此函数的简单语法- unshift ARRAY, LIST返回值 此函数返回ARRAY中新元素的数量。 例 以下是显示其基本用法的示例代码- #!/usr/bin/perl -warray ( 1, 2, 3, 4);print "Value of a…

30.Netty源码服务端启动主要流程

highlight: arduino-light 服务端启动主要流程 •创建 selector •创建 server socket channel •初始化 server socket channel •给 server socket channel 从 boss group 中选择一个 NioEventLoop •将 server socket channel 注册到选择的 NioEventLoop 的 selector •…

SSH远程直连--------------Docker容器

文章目录 1. 下载docker镜像2. 安装ssh服务3. 本地局域网测试4. 安装cpolar5. 配置公网访问地址6. SSH公网远程连接测试7.固定连接公网地址8. SSH固定地址连接测试 在某些特殊需求下,我们想ssh直接远程连接docker 容器,下面我们介绍结合cpolar工具实现ssh远程直接连接docker容器…

SpringBoot复习:(56)使用@Transactional注解标记的方法的执行流程

首先&#xff0c;如果在某个类或某个方法被标记为Transactional时&#xff0c;Spring boot底层会在创建这个bean时生成代理对象&#xff08;默认使用cglib) 示例&#xff1a; 当调用studentService的addStudent方法时&#xff0c;会直接跳到CglibAopProxy类去执行intercept方…