工作中设计树结构增删改查,导入,导出操作,搜索 POI导入导出树结构Excle 相关博客较少,故写博客用以记录分享。
文章目录
一、表结构设计,导入导出模板。
注: 博主树结构为8级结构,因业务关系,故分为两张表。此表分为4级结构如下:
楼栋 — 单元 — 楼层 — 房间
导入导出Excel表格第一列项目名不在此表范围内,忽略此列即可
表仅截取几个树结构相关字段,业务字段根据需求自己添加即可。
导出Excel样式如下。
导入Excel模板样式如下:
二、递归查询树结构
1.思路如下
- 因博主结构层级较多分为两级处理,所以副表可理解为详情表,详情表树结构数据均对应有主表项目id。
- 入参:项目id 根据入参先获取此项目下所有子级数据集合(包含楼栋、单元、楼层、房间)。
- 返回实体需包含子级List字段 与 自定义获取所有叶子节点方法
private List<****> children = new ArrayList<SysDeptInfoTreeVo>();
public int getLeftNum(){
int count = 0 ;
if (children.isEmpty()){
return 1;
}else{
for (**** s : children){
count+= s.getLeftNum();
}
}
return count;
}
2.代码示例
//根据项目id获取此项目下全部数据
List<SysDeptInfoTreeVo> sysDeptInfoTreeVoList = deptInfoMapper.selectListByDeptId(sysDeptInfoDto.getDeptId());
//获取父节点
List<SysDeptInfoTreeVo> collect = sysDeptInfoTreeVoList.stream().filter(sysDeptInfoTreeVo -> sysDeptInfoTreeVo.getParentId() == 0).map(s -> {
s.setChildren(getChildren(s, sysDeptInfoTreeVoList));
return s;
}).sorted(Comparator.comparing(SysDeptInfoTreeVo::getId)).collect(Collectors.toList());
- 代码解读:
- 从副表即详情表中先获取此项目下所有数据集合。
- 过滤获取第一级节点,第一级节点无父节点,获取各个第一级节点后设置子节点调用递归方法即可。
/**
* 递归查询子节点
* @param sysDeptInfoTreeVo 根节点
* @param sysDeptInfos 所有节点
* @return 根节点信息
*/
public static List<SysDeptInfoTreeVo> getChildren(SysDeptInfoTreeVo sysDeptInfoTreeVo, List<SysDeptInfoTreeVo> sysDeptInfos) {
List<SysDeptInfoTreeVo> children = sysDeptInfos.stream().filter(s -> sysDeptInfoTreeVo.getId().equals(s.getParentId())).map(sysDeptInfo -> {
System.out.println("'sysDeptInfo' = " + sysDeptInfo.toString());
sysDeptInfo.setChildren(getChildren(sysDeptInfo,sysDeptInfos));
return sysDeptInfo;
}).sorted(Comparator.comparing(SysDeptInfoTreeVo::getId)).collect(Collectors.toList());
return children;
}
二、新增树结构
思路如下
-
新增入参实体需有 parentId 父节点id
-
新增根据具体业务进行校验
-
新增需判断是否为第一级节点,
若是第一级节点,则 父节点id为0 祖级列表为 0 , 等级为 1
否则 根据 父节点id 先查询父节点,从父节点获取id作为 parentId ,父节点祖级列表+父节点id 作为祖级列表参数,等级 为 父节点等级+1 -
新增操作分级处理即可,无示例代码
三.树结构修改。
思路如下
- 修改操作,需根据实际业务修改,博主修改无修改级别操作,故仅为一些校验后直接update。
四、递归删除树结构
1.思路如下
- 入参:某根节点id,只要获取到此根节点所有子孙级数据,全部删除即可。
2.代码示例
//根据id获取此项目
SysDeptInfo sysDeptInfo = deptInfoMapper.selectById(id);
Assert.isNull(sysDeptInfo,"未查询到项目信息");
//获取此项目子级集合
List<SysDeptInfo> sysDeptInfoList = deptInfoMapper.selectList(new QueryWrapper<SysDeptInfo>().eq("dept_id",sysDeptInfo.getDeptId()));
//此项目下所有子孙级list
List<SysDeptInfo> list = new ArrayList();
list.add(sysDeptInfo);
//调用递归方法
list = getChildrenList(sysDeptInfo, sysDeptInfoList,list);
- 代码解读:
- 首先根据某根节点id获取此条数据,然后根据此数据所属项目id即可获取和此根节点所属同一项目下各个级别数据集合,此集合必包含此条数据下的子孙数据。
- 创建返回list ,此list包含所有要删除的数据集合,包含此节点。
- 调用递归方法
/**
* 递归查询子节点
* @param sysDeptInfo 根节点
* @param sysDeptInfoList 与根节点同属一个项目下的所有数据list
* @param list 返回list
* @return sysDeptInfoList 所有子节点的list集合
*/
public static List<SysDeptInfo> getChildrenList(SysDeptInfo sysDeptInfo, List<SysDeptInfo> sysDeptInfoList,List<SysDeptInfo> list) {
List<SysDeptInfo> resList = sysDeptInfoList.stream().filter(s -> sysDeptInfo.getId().equals(s.getParentId())).map(s -> {
list.add(s);
List<SysDeptInfo> childrenList = getChildrenList(s, sysDeptInfoList, list);
return s;
}).collect(Collectors.toList());
return list;
}
- 获取所有需删除的list集合后,从表中删除即可。
Excel导入导出参考下方链接根据需求修改完善即可: Java POI完成Excel导入导出示例
参考博主poi版本较低,若用4.开头poi需修改版本不一致某些方法修改的问题,下方代码已修正。
五、树结构递归导出Excel
1.思路如下
导出Excel尚未进行单元格合并操作,哪个大佬写好的话,不介意的话分享至评论区,感谢。- 已订正,导出Excel已添加合并单元格功能
- 已订正,导出Excel层级多表格样式错乱问题
- 入参:递归查询树结构的 结果Vo
2.代码示例
- 代码解读:
- 只需关注 exportTaskSumPoi 和 ExcelChildrenNew 这个方法即可。
- exportTaskSumPoi 方法从第三行开始创建行、单元格目的:因为博主结构分为主副表,主表叶子节点为副表所属项目,例如 主表叶子精确到—爱情公寓,那么副表 第一级节点 从 楼栋开始,即爱情公寓第一栋楼,只不过博主多加一个字段 项目id,指定这些楼栋,单元,楼层,房间属于某一项目,例:
- 主表中 爱情公寓id为 105
- 副表中加上dept_id字段用来指定属于哪个项目
- 回归第三行开始创建行、单元格目的从主表中获取项目信息并将项目信息放在第三行第一个单元格中,关注点为下面递归方法ExcelChildrenNew
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.List;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.zensun.system.domain.vo.SysDeptInfoTreeVo;
import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Component;
/**
* @program: family-file-pc-b
* @description: Excel树结构导出Util
* @author: LYK
* @create: 2021-05-20 08:37
**/
@Component
public class ExcelExportUtil {
private int cols = 9;//excel里表格列
private String sheetTitle = "项目房间信息";
public void setCols(int cols) {
this.cols = cols;
}
public void setSheetTitle(String sheetTitle) {
this.sheetTitle = sheetTitle;
}
/**
* POI : 导出数据,存放于Excel中
*
* @param os
* 输出流 (action: OutputStream os = response.getOutputStream();)
* @param sysDeptInfoTreeVo
* 要导出的数据(根数据)
*/
public void exportTaskSumPoi(OutputStream os, SysDeptInfoTreeVo sysDeptInfoTreeVo) {
//取出数据源;
String headTitle = sheetTitle;//标题;
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
try {
// 创建Excel工作薄
Workbook book = null;
try {
book = new XSSFWorkbook();//excell2007
} catch (Exception ex) {
book = new HSSFWorkbook();//excell2003
}
// 在Excel工作薄中建一张工作表;
Sheet sheet = book.createSheet(sheetTitle);
//设置标题和表头;
createTitle(book, sheet, headTitle);
createHead(book, sheet);
int startCellCount = -1 ; //控制第几个单元格开始字段
int endCellCount = 0;
int rowCount = 2 ; //控制创建行数字段
//从第三行开始创建一行 --- 项目信息
Row row = sheet.createRow(rowCount);
//创建一个单元格 ---- 项目信息
Cell cell0 = row.createCell(0);
cell0.setCellValue(sysDeptInfoTreeVo.getDeptName()==null?"null":sysDeptInfoTreeVo.getDeptName());
//获取第一级所有子节点数量,+1是单元格 是从第三行开始
int i = sysDeptInfoTreeVo.getLeftNum()+ 1;
//合并项目单元格 四个参数分别为 第一个单元格的行号,第二个单元格行号,第一个单元格列号,第二个单元格的列号
sheet.addMergedRegion(new CellRangeAddress(2,sysDeptInfoTreeVo.getLeftNum()+1,0,0));
// 获取楼栋信息
ExcelChildrenNew(row,sysDeptInfoTreeVo, sheet, startCellCount,endCellCount,rowCount);
// 写入数据 把相应的Excel 工作簿存盘
book.write(os);
book.close();
os.