工作日志,Excel导入树结构数据
[TOC]
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博客
- PHPExcel 导入数据导多个工作区(sheet)
- Sql2012如何将远程服务器数据库及表、表结构、表数据导入本地数据库 自定义日志记录功能,按日记录,很方便 C#常量和字段以及各种方法的语法总结 类型,对象,线程栈,托管堆在运行时的关系,以及clr如何调用静态方法,实例方法,和虚方法 asp.net webapi 自定义身份验证
- 留个记念,第一次写这么长的存储过程。关于台湾地址excel中数据从一张表中导入另一张不同结构的表!
- ThinkPHP第二十三天(Category表结构、PHPExcel导入数据函数)
- 将Excel文件中树状结构的数据导入到MSSQL数据库的表中
- 将excel文件中数据导入数据库中,表结构中已存有20W笔数据,需将数据排重后存入数据库
- Linux下把excel数据导入到mssql数据库中,今天完成的太有挑战性的工作!
- 如果通过toad得到表结构的sql语句?通过toad可以导入导出excel数据文件吗?
- (运维日志)对Excel进行数据导入,函数拼写Sql脚本,动态生成一列单元格函数
- 工作记录:使用POI从excel导入数据(2003版本、2007以及以上版本)
- 将datagrid中数据导出到excel中 -------<<工作日志2014-6-6>>
- 如何将记事本里面的数据按照原先的行列结构导入excel
- excel数据导入到sqlserver中---------工作笔记
- 利用Excel导入数据到SAP C4C
- Mysql导入Excel数据
- 在ASP.NET中将Excel文件中数据导入数据库并显示进度条
- 如何使用免费控件将Word表格中的数据导入到Excel中
- java 实现excel中的数据导入到数据库的功能
- 从excel导入数据时显现进度条-用java导入Excel数据到数据库(实时进度条)
- java上传Excel实现数据的导入