工作日志,Excel导入树结构数据
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导入树结构数据的更多相关文章
- [办公自动化] 再读《让EXCEL飞》(从excel导入access数据时,union联合查询,数据源中没有包含可见的表格)
一年多以前就买了@Mrexcel的<让excel飞>这本书.整体思路是利用access结合excel,大幅度提高数据分析效率. 最近又拿出来看了看.第十五章,比高级筛选更“高级”,P241 ...
- PHP 清除 Excel 导入的数据空格
处理excel中的数据时,遇到了页面中显示为空格,审查元素时却显示为换行,使用replace函数也不管用,反正就是不知道是什么东西,看起来像空格 中文空格这里面有好几种:没有简单的解决问题的方式,比如 ...
- Talend 从Excel导入Saleforce数据(二) TMAP是精髓
TMap LookUp 经过测试的结果: ------------------------------------------ LookUp最好从CSV读数据,这样是最快了(20万记录1s).从Sal ...
- Talend 从Excel导入Saleforce数据(一) 直接从salesforce lookup 性能的噩梦
速度的瓶颈是在查询Sales force是否有该电话号码的联系人资料. TMap属性的 lookup Model, 如果用Load Once, 则会把SaleForce的contact全部load下来 ...
- excel导入mysql数据
excel加载mysql数据 1.第一步,选择从mysql导入数据 2.单击会出现弹框: 3.可能有的同学的,这里缺少插件,例如: 4.去下载 这个 插件安装即可.https://dev.mysql. ...
- 将Excel导入到数据中
常用的方式的有两种: 1. 通过 Microsoft.Jet.OLEDB.4.0 或 Microsoft.ACE.OLEDB.12.0 Microsoft.ACE.OLEDB.12.0 需要安装 A ...
- JeeSite中Excel导入导出
在各种管理系统中,数据的导入导出是经常用到的功能,通常导入导出以Excel.CSV格式居多.如果是学习的过程中,最好是自己实现数据导入与导出的功能,然而在项目中,还是调用现成的功能比较好.近期一直使用 ...
- java利用jxl实现Excel导入功能
本次项目实践基于Spring+SpringMvc+MyBatis框架,简单实现了Excel模板导出.和Excel批量导入的功能.实现过程如下:. 1.maven导入所需jar包 <depende ...
- 将datagrid中数据导出到excel中 -------<<工作日志2014-6-6>>
前台datagrid数据绑定 #region 导出到excel中 /// <summary> /// 2014-6-6 /// </summary> / ...
随机推荐
- Javascript学习笔记-基本概念-数据类型
1.typeof 操作符的返回值: "undefined"——如果这个值未定义: "boolean"——如果这个值是布尔值: "string" ...
- 网站提权之MSF骚操作
当我们在进行web渗透测试的时候,拿到了webshell,但是在执行net user.whoami.类似的命令会发现怎么好像用不了,没有回显,权限不够,这可怎么办呐? 测试环境: 内网,没钱买服务器, ...
- iOS技术提升方向
技术维度: 认知维度:
- html/css系列 BFC
本文详情:https://www.cnblogs.com/chen-... 第一种 BFC中的盒子对齐 <div class="container"> <div ...
- 浅谈Java开发三层架构
三层架构,一般来说就是将整个业务应用划分为:表现层(UI).业务逻辑层(BLL).数据访问层(DAL).区分层次的目的即为了“高内聚,低耦合”的思想. 概念简介 1.表现层(UI):简单来说,就是展现 ...
- element多层导航菜单
很久没写博客了原因就是懒, 刚下班今天写了个基于element导航菜单实现多层菜单(可以无限多层) 组件核心思想就是组件递归(这个有时候面试会问到) 然后就判断children有没有内容,没内容就是选 ...
- python学习-练习题4巩固
一个数加100后是一个整数的平方,加268后也是一个数的平方,求这个数 分析:这个数加100之后开方是整数,说明int(math.sqrt(a+100))转为整数应该没有小数部分 so:b*b = i ...
- flask 对于邮件url进行一个加密防止爆破
注册表单 from app.modles import User class registerForm(FlaskForm): nicheng = StringField('昵称',validator ...
- MySQL字符集不一致导致性能下降25%,你敢信?
故事是这样的: 我在对MySQL进行性能测试时,发现CPU使用率接近100%,其中80%us, 16%sys,3%wa,iostat发现磁盘iops2000以下,avgqu-sz不超过3,%util最 ...
- selenium基本对象之——数值型
python的数值类型,除了魔法方法以为,只有下面的这些方法: 整形的方法有:as_integer_ratio.bit_length.from_bytes.to_bytes.conjugate.ima ...