1. 前言

最近做了一个比较有趣的需求。需要把树结构的目录通过Excel的方式导入到系统中,并且该目录层级可以是多级且不确定的。这可能是一个常见又不太常见的需求,一般目录都是在界面上操作创建,或者是系统初始化生成。很少在系统使用一段时间后还有导入新目录的需求。

2. 需求分析

2.1 需求难点

这个需求最大的难点就是如何找到父级节点。包括

1)如何让一个Excel表格实现不确定目录层级功能?

2)如何让子个节点能正确找到其父级节点?

3)如何在遍历完一个分枝后,还能从根节点继续遍历另外一个分枝?

2.2 解决难点

1)我们可以将目录层级作为用户输入项,由用户决定该数据处于第几层目录。解决目录层级不确定的需求。

2)我们可以用树节点深度遍历的思想,遍历一个个节点,使其找到其父节点。

3)我们同样可以用深度遍历的思想再结合先进后出操作,重新找回之前的根节点。

2.3 表格设计

我们可以用Level作为目录所在层级,一级目录的Level就是1,同理N级目录的Level就是N。且数据从上至下可以形成一个完整树分枝。

表格设计如下:

分类名称 级别Level 其他字段
A栋 1
A栋-1楼 2
B栋 1
B栋-1楼 2
B栋-1楼-A区 3
B栋-2楼 2
B栋-2楼-A区 3
B栋-2楼-B区 3

从表格中,我们应该可以得出以下结论:

1)A栋和B栋属于一级目录

2)A栋有一个子目录,A栋-1楼

3)B栋有两个子目录,分别是:B栋-1楼、B栋-2楼

4)B栋-1楼有一个子目录,B栋-1楼-A区

5)B栋-2楼有两个子目录,分别是:B栋-2楼-A区、B栋-2楼-B区

3. 功能实现

我们对需求做了简单的分析,现在就用代码来实现。从易到难,从一个分枝再到多个分枝来实现。

3.1 一个分枝

一个分枝的Level排序应该是:1-2-3-N

这种情况是最简单的,孤零零的一条直线。其父节点就是当前节点的上一个元素。

伪代码如下:

var categoryPathStack = mutableListOf<EquipmentCategory>()
for (i in sheet.firstRowNum..sheet.lastRowNum) {
val categoryName = row.getCell(0).stringCellValue
val categoryLevel = row.getCell(1).stringCellValue.toInt()
var parentCategory: EquipmentCategory? = null
if (categoryLevel > 1) {
parentCategory = categoryPathStack.last()
}
// todo save or update
categoryPathStack.add(equipmentCategory)
}

3.2 一个分枝多个树叶

一个分支多个树叶的Level排序应该是:1-2-3-3-3-3

这种情况稍微复杂了一点,如果只是获取当前节点的上一个元素是很难找到其父级节点的。我们需要把同一层的兄弟节点都剔除掉。

伪代码如下:

var categoryPathStack = mutableListOf<EquipmentCategory>()
for (i in sheet.firstRowNum..sheet.lastRowNum) {
val categoryName = row.getCell(0).stringCellValue
val categoryLevel = row.getCell(1).stringCellValue.toInt()
var parentCategory: EquipmentCategory? = null
// 将集合中大于或等于当前层级的数据剔除掉
while (categoryPathStack.isNotEmpty() && categoryPathStack.last().level >= categoryLevel) {
categoryPathStack = categoryPathStack.subList(0, categoryPathStack.size-1).toMutableList()
}
if (categoryLevel > 1) {
parentCategory = categoryPathStack.last()
}
// todo save or update
categoryPathStack.add(equipmentCategory)
}

3.3 多个分枝多个树叶

多个分支多个树叶的Level排序应该是:1-2-3-3-3-3-2-3-1-2-3

这种场景依然可以用一个分支多个树叶的代码实现,而后面来的1就像一个分割线,将前面先进来的数据隔离开。

4. 代码事例

4.1 目录实体结构

目录实体添加临时字段level方便逻辑判断。字段code是方便后期通过code作为StartingWith的查询条件,从而减少递归查询所有子级目录带来的性能损耗。code的生成规则是:父节点code拼接当前节点id,

class Category: AuditModel() {

    var name: String? = null
var description: String? = null
var isLeaf: Boolean = true
var parentId: String? = null
@Column(columnDefinition = "TEXT")
var code: String? = null @Transient
var level: Int = 0
}

4.2 Excel导入代码

以下只是删减过后的代码,具体业务场景会有具体的逻辑代码。

@Transactional
fun importCategoryData(file: MultipartFile, request: HttpServletRequest): OperateStatus {
// fileUtil.getExcelWorkbook 只是简单封装的读取excel方法
val work = fileUtil.getExcelWorkbook(file.inputStream, file.originalFilename!!)
// todo 清空旧数据 val sheet: Sheet = work.getSheetAt(0)
var categoryPathStack = mutableListOf<Category>()
for (i in sheet.firstRowNum..sheet.lastRowNum) {
val row = sheet.getRow(i)
if (row == null || row.rowNum == 0) {
continue
}
// todo 数据校验 val categoryName = row.getCell(0).stringCellValue
val categoryLevel = row.getCell(1).stringCellValue.toInt()
var parentCategory: Category? = null
while (categoryPathStack.isNotEmpty() && categoryPathStack.last().level >= categoryLevel) {
categoryPathStack = categoryPathStack.subList(0, categoryPathStack.size-1).toMutableList()
}
if (categoryLevel > 1) {
parentCategory = categoryPathStack.last()
}
var category = Category()
category.name = categoryName
category.parentId = parentCategory?.id
category = categoryRepository.save(category) if (parentCategory == null) {
category.code = category.id
} else {
category.code = "${parentCategory.code}-${category.id}"
category.isLeaf = true
parentCategory.isLeaf = false
categoryRepository.save(parentCategory)
}
categoryRepository.save(category)
category.level = categoryLevel
categoryPathStack.add(category)
}
work.close()
return OperateStatus("Import Category Success")
}

文章到这里就结束了,感谢观看。ITDragon博客

工作日志,Excel导入树结构数据的更多相关文章

  1. [办公自动化] 再读《让EXCEL飞》(从excel导入access数据时,union联合查询,数据源中没有包含可见的表格)

    一年多以前就买了@Mrexcel的<让excel飞>这本书.整体思路是利用access结合excel,大幅度提高数据分析效率. 最近又拿出来看了看.第十五章,比高级筛选更“高级”,P241 ...

  2. PHP 清除 Excel 导入的数据空格

    处理excel中的数据时,遇到了页面中显示为空格,审查元素时却显示为换行,使用replace函数也不管用,反正就是不知道是什么东西,看起来像空格 中文空格这里面有好几种:没有简单的解决问题的方式,比如 ...

  3. Talend 从Excel导入Saleforce数据(二) TMAP是精髓

    TMap LookUp 经过测试的结果: ------------------------------------------ LookUp最好从CSV读数据,这样是最快了(20万记录1s).从Sal ...

  4. Talend 从Excel导入Saleforce数据(一) 直接从salesforce lookup 性能的噩梦

    速度的瓶颈是在查询Sales force是否有该电话号码的联系人资料. TMap属性的 lookup Model, 如果用Load Once, 则会把SaleForce的contact全部load下来 ...

  5. excel导入mysql数据

    excel加载mysql数据 1.第一步,选择从mysql导入数据 2.单击会出现弹框: 3.可能有的同学的,这里缺少插件,例如: 4.去下载 这个 插件安装即可.https://dev.mysql. ...

  6. 将Excel导入到数据中

    常用的方式的有两种: 1. 通过 Microsoft.Jet.OLEDB.4.0 或  Microsoft.ACE.OLEDB.12.0 Microsoft.ACE.OLEDB.12.0 需要安装 A ...

  7. JeeSite中Excel导入导出

    在各种管理系统中,数据的导入导出是经常用到的功能,通常导入导出以Excel.CSV格式居多.如果是学习的过程中,最好是自己实现数据导入与导出的功能,然而在项目中,还是调用现成的功能比较好.近期一直使用 ...

  8. java利用jxl实现Excel导入功能

    本次项目实践基于Spring+SpringMvc+MyBatis框架,简单实现了Excel模板导出.和Excel批量导入的功能.实现过程如下:. 1.maven导入所需jar包 <depende ...

  9. 将datagrid中数据导出到excel中 -------<<工作日志2014-6-6>>

    前台datagrid数据绑定 #region 导出到excel中    /// <summary>    /// 2014-6-6    /// </summary>    / ...

随机推荐

  1. 第六周学习笔记,vc各类控件的输入输出

    6w学习笔记 vc控件的输入输出 单选按钮 当单击 RadioButton 控件时,其 Checked 属性设置为 true,并且调用 Click 事件处理程序.当 Checked 属性的值更改时,将 ...

  2. 浅谈.NET Framework 与CLR

            承载公共语言运行时 (CLR) 的所有应用程序均需启动(或称“激活”)CLR 以运行托管代码.             通常,.NET Framework 应用程序在生成它的 CLR ...

  3. NLP(二十二)利用ALBERT实现文本二分类

      在文章NLP(二十)利用BERT实现文本二分类中,笔者介绍了如何使用BERT来实现文本二分类功能,以判别是否属于出访类事件为例子.但是呢,利用BERT在做模型预测的时候存在预测时间较长的问题.因此 ...

  4. (Win10)Java,Maven,Tomcat8.0,Mysql8.0.15安装与环境配置,以及IDEA2019.3使用JDBC连接MySQL、创建JavaEE项目

    之前用windows+linux的双系统,最近不怎么舒服就把双系统给卸了,没想到除了问题,导致有linux残余,于是就一狠心重装了电脑,又把Java及其相关的一些东西重新装了回来,还好当初存了网盘链接 ...

  5. Mysql8以上需要指定时区serverTimezone

    JDBC连接Mysql8以下 com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/test?useUnicode=true&charac ...

  6. win10查看本机mac地址的详细操作

    今天和大家分享win10查看本机mac地址的方法,mac地址是什么东西?MAC地址实际上就是网卡的一个标识,和身份证号码类似,大多数情况下是不需要关心MAC地址是多少的,一般不能改动,所以也不会重复. ...

  7. Typora[MarkDown编辑器]+(PicGo+Github+JsDelivr)[个人图床] ,开启你的高效创作

    使用Typora搭配Picgo开启你的高效创作 0x00 一切都要从MarkDown说起 富文本语言的弊端 平常我们最常用的写作工具,无非是富文本编辑器中的代表--微软家的Office Word.这种 ...

  8. DirectX11--深入理解Effects11、使用着色器反射机制(Shader Reflection)实现一个复杂Effects框架

    前言 如果之前你是跟随本教程系列学习的话,应该能够初步了解Effects11(现FX11)的实现机制,并且可以编写一个简易的特效管理框架,但是随着特效种类的增多,要管理的着色器.资源等也随之变多.如果 ...

  9. SpringBoot1.5.10.RELEASE整合druid时,在druid monitor界面出现(*) property for user to setup

    接上篇随笔,配置druid之后出现的错误,解决方法如下: @Bean(destroyMethod = "close",initMethod = "init") ...

  10. 【WPF学习】第五十八章 理解逻辑树和可视化树

    在前面章节中,花费大量时间分析了窗口的内容模型——换句话说,研究了如何在其他元素中嵌套元素,进而构建完整的窗口. 例如,考虑下图中显示的一个非常简单的窗口,该窗口包含两个按钮.为创建该按钮,在窗口中嵌 ...