# easyExcel-list-solve **Repository Path**: javaxiaobear/easyExcel-list-solve ## Basic Information - **Project Name**: easyExcel-list-solve - **Description**: 解决EasyExcel不支持解析List以及实体类对象问题 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2021-09-09 - **Last Updated**: 2025-01-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # easyExcel-list-solve #### 介绍 > 解决EasyExcel不支持解析List以及实体类对象问题 #### 需求说明 > 连表查询一对多的关系,fir_rating_material表与fir_ratingcard_template和fir_rating_card是一对多的关系 #### 解决方法 ##### 方法一:采用Mybatis的继承关系 一对多的关系,就相当于一个`list`里面又包含另一个list集合,这时候我们可以将`list`集合进行解析,也就是说,easyExcel是不支持数据集合里面嵌套对象、`list`、`map`等集合数据的 1. 新建一个实体类来接收数据,当然,你用原来对象来继承也是可以的,这里我是把两个类的属性放在一起了,只继承一个类,主要是看起来方便一点,toString和set、get方法省略 ```java public class FirRatingcardTemplateVo extends FirRatingCard{ private static final long serialVersionUID = 1L; /** 评分模板ID */ @ExcelIgnore private Long ratingTemplateId; /** 模板ID */ @ExcelIgnore private Long templateId; /** 模块 */ @ExcelProperty(value = "模块") private String ratingModule; /** 一级指标 */ @ExcelProperty(value = "一级指标") private String ratingLevelFirst; /** 二级指标 */ @ExcelProperty(value = "二级指标") private String ratingLevelSecond; /** 文件编号 */ @ExcelProperty(value = "对应资料调阅清单编号") private Long ratingNum; /** 评分内容ID */ @ExcelProperty(value = "评分内容ID") private Integer ratingContentId; /** 评价内容 */ @ExcelProperty(value = "评分内容") private String ratingContent; /** 评分方式 */ @ExcelProperty(value = "评分方式") private String ratingType; /** 评分资料ID */ @ExcelIgnore private Long rmId; /** 任务ID */ @ExcelIgnore private String taskId; /** 机构名称 */ @ExcelIgnore private String deptName; /** 机构类型 */ @ExcelIgnore private String deptType; /** 评级期数 */ @ExcelIgnore private String ratingPhase; /** 评级年份 */ @ExcelIgnore private String ratingYear; /** 所在地 */ @ExcelIgnore private String location; /** 删除标志 */ @ExcelIgnore private String delFlag; @ExcelIgnore private FirRatingCard firRatingCard; ``` 2. 对应的mapper.xml配置 ```xml select rm_id, task_id, template_id, dept_name, dept_type, rating_phase, rating_year, location, create_by, create_time, update_by, update_time from fir_rating_material select DISTINCT frm.rm_id, frm.task_id, frm.dept_name, frm.dept_type, frm.rating_phase, frm.rating_year,frm.location, frm.template_id ,frt.rating_template_id, frt.rating_module, frt.rating_level_first, frt.rating_level_second,frt.rating_num, frt.rating_content_id, frt.rating_content, frt.rating_type, frt.remark,frc.verification_type,frc.rating,frc.rating_type,frc.rating_guide_list,frc.rating_doc_list,frc.ask_id from fir_rating_material As frm left join fir_ratingcard_template frt on frm.template_id = frt.template_id left join fir_rating_card frc ON frc.rm_id = frm.rm_id and frc.template_id = frm.template_id and frc.rating_type_id = frt.rating_type_id ``` 3. 这里的service,我是没有做任何数据处理的,代码我就不放了,具体看源码 4. 调用接口 ```java @Controller public class ExcelController { @Resource private IFirRatingMaterialService iFirRatingMaterialService; /** * * @param response * @throws IOException */ @RequestMapping(value = "/getExcel", method = RequestMethod.GET) public void getExcel(HttpServletResponse response) throws IOException { //获取数据源 List list = iFirRatingMaterialService.selectFirRatingMaterialByRmId(2L); //设置输入流,设置响应域 response.setContentType("application/ms-excel"); response.setCharacterEncoding("utf-8"); String fileName = URLEncoder.encode(list.get(0).getRatingYear()+"年第"+list.get(0).getRatingPhase()+"期"+list.get(0).getDeptName()+"评级打分表.xlsx","utf-8"); response.setHeader("Content-disposition","attachment;filename="+fileName); //需要合并的列 int[] mergeColumeIndex = {0, 1, 2, 3, 4, 5,6,7,8,9,10,11,12,13,14,16,17,18}; //需要从第一行开始,列头第一行 int mergeRowIndex = 1; EasyExcel//将数据映射到DownloadDTO实体类并响应到浏览器 .write(new BufferedOutputStream(response.getOutputStream()), FirRatingcardTemplateVo.class) //07的excel版本,节省内存 .excelType(ExcelTypeEnum.XLSX) .head(head("评级打分",list.get(0).getDeptName(),list.get(0).getDeptType(),list.get(0).getRatingYear(),list.get(0).getRatingPhase(),list.get(0).getLocation())) //是否自动关闭输入流 .autoCloseStream(Boolean.TRUE) .registerWriteHandler(new CustomCellWriteHandler()) .registerWriteHandler(new ExcelFillCellMergeStrategy(mergeRowIndex, mergeColumeIndex)) // // 自定义列宽度,有数字会 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) //设置excel保护密码 // .password("123456") .sheet().doWrite(list); } /** * 自定义头 * @param headTitle 统一头 * * @return 返回整个头list。 头部相同连续的单元格会自动合并。 */ private static List> head(String headTitle, String deptName,String deptType,String ratingYear,String ratingPhase,String location){ List> list = new ArrayList>(); /* List head1 = new ArrayList(); head1.add(headTitle); head1.add("金融机构名称:"+deptName); head1.add("评分模板ID"); List head2 = new ArrayList(); head2.add(headTitle); head2.add(""); head2.add("模板号");*/ List head1 = new ArrayList(); head1.add(headTitle); head1.add("金融机构名称:"+deptName); head1.add("模块"); List head2 = new ArrayList(); head2.add(headTitle); head2.add(""); head2.add("一级指标"); List head3 = new ArrayList(); head3.add(headTitle); head3.add("机构类型:"+deptType); head3.add("二级指标"); List head4 = new ArrayList(); head4.add(headTitle); head4.add(""); head4.add("对应资料调阅清单编号"); List head5 = new ArrayList(); head5.add(headTitle); head5.add("评级年份:"+ratingYear); head5.add("评分内容ID"); List head6 = new ArrayList(); head6.add(headTitle); head6.add(""); head6.add("评分内容"); List head7 = new ArrayList(); head7.add(headTitle); head7.add("评级期数:"+ratingPhase); head7.add("评分方式"); List head8 = new ArrayList(); head8.add(headTitle); head8.add(""); head8.add("问询"); List head9 = new ArrayList(); head9.add(headTitle); head9.add("所在地:"+location); head9.add("现场核查"); List head10 = new ArrayList(); head10.add(headTitle); head10.add(""); head10.add("打分"); List head11 = new ArrayList(); head11.add(headTitle); head11.add(""); head11.add("打分指引依据"); List head12 = new ArrayList(); head12.add(headTitle); head12.add(""); head12.add("资料调阅依据"); list.add(head1); list.add(head2); list.add(head3); list.add(head4); list.add(head5); list.add(head6); list.add(head7); list.add(head8); list.add(head9); list.add(head10); list.add(head11); list.add(head12); return list; } } ``` 5. 访问下载:http://localhost:8989/getExcel ##### 2、采用Vo的方式 1. 新建一个Vo类,把三个类的属性全部放进去 ```java public class EasyExcelDemoVo { private static final long serialVersionUID = 1L; /** 评分模板ID */ @ExcelIgnore private Long ratingTemplateId; /** 模板ID */ @ExcelIgnore private Long templateId; /** 模块 */ @ExcelProperty(value = "模块") private String ratingModule; /** 一级指标 */ @ExcelProperty(value = "一级指标") private String ratingLevelFirst; /** 二级指标 */ @ExcelProperty(value = "二级指标") private String ratingLevelSecond; /** 文件编号 */ @ExcelProperty(value = "对应资料调阅清单编号") private Long ratingNum; /** 评分内容ID */ @ExcelProperty(value = "评分内容ID") private Integer ratingContentId; /** 评价内容 */ @ExcelProperty(value = "评分内容") private String ratingContent; /** 评分方式 */ @ExcelProperty(value = "评分方式") private String ratingType; /*----------------------第二个类---------------------- */ /** 评分资料ID */ @ExcelIgnore private Long rmId; /** 任务ID */ @ExcelIgnore private String taskId; /** 机构名称 */ @ExcelIgnore private String deptName; /** 机构类型 */ @ExcelIgnore private String deptType; /** 评级期数 */ @ExcelIgnore private String ratingPhase; /** 评级年份 */ @ExcelIgnore private String ratingYear; /** 所在地 */ @ExcelIgnore private String location; /** 删除标志 */ @ExcelIgnore private String delFlag; @ExcelIgnore private FirRatingCard firRatingCard; /*----------------------第三个类---------------------- */ /** 评分卡ID */ @ExcelIgnore private Long ratingCardId; /** 问询ID */ @ExcelProperty("问询") private Long askId; /** 现场核查 */ @ExcelProperty("现场核查") private String verificationType; /** 打分 */ @ExcelProperty("打分") private String rating; /** 打分指引依据 */ @ExcelProperty("打分指引依据") private String ratingGuideList; /** 资料调阅依据 */ @ExcelProperty("资料调阅依据") private String ratingDocList; /** 搜索值 */ @ExcelIgnore private String searchValue; /** 创建者 */ @ExcelIgnore private String createBy; /** 创建时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @ExcelIgnore private Date createTime; /** 更新者 */ @ExcelIgnore private String updateBy; /** 更新时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @ExcelIgnore private Date updateTime; /** 备注 */ @ExcelIgnore private String remark; /** 开始时间 */ @JsonIgnore @ExcelIgnore private String beginTime; /** 结束时间 */ @JsonIgnore @ExcelIgnore private String endTime; /** 请求参数 */ @ExcelIgnore private Map params; } ``` 2. 对应的mapper.xml配置 ```java select rm_id, task_id, template_id, dept_name, dept_type, rating_phase, rating_year, location, create_by, create_time, update_by, update_time from fir_rating_material select DISTINCT frm.rm_id, frm.task_id, frm.dept_name, frm.dept_type, frm.rating_phase, frm.rating_year,frm.location, frm.template_id ,frt.rating_template_id, frt.rating_module, frt.rating_level_first, frt.rating_level_second,frt.rating_num, frt.rating_content_id, frt.rating_content, frt.rating_type, frt.remark,frc.verification_type,frc.rating,frc.rating_type,frc.rating_guide_list,frc.rating_doc_list,frc.ask_id from fir_rating_material As frm left join fir_ratingcard_template frt on frm.template_id = frt.template_id left join fir_rating_card frc ON frc.rm_id = frm.rm_id and frc.template_id = frm.template_id and frc.rating_type_id = frt.rating_type_id ``` ```java public interface FirRatingMaterialMapper { /** * 第一种:采用继承的方式 * @param rmId * @return 结果 */ public List selectFirRatingMaterialByRmId(Long rmId); /** * 第二种:采用vo的方式来接收 * @param rmId * @return */ public List selectDataByRmId(Long rmId); } ``` 3. 同样的,servcice我没有做任何的数据处理,你要处理也是可以的 4. 调用接口 ```java @Controller public class ExcelController { @Resource private IFirRatingMaterialService iFirRatingMaterialService; /** * * @param response * @throws IOException */ @RequestMapping(value = "/getExcel", method = RequestMethod.GET) public void getExcel(HttpServletResponse response) throws IOException { //获取数据源 List list = iFirRatingMaterialService.selectDataByRmId(2L); //设置输入流,设置响应域 response.setContentType("application/ms-excel"); response.setCharacterEncoding("utf-8"); String fileName = URLEncoder.encode(list.get(0).getRatingYear()+"年第"+list.get(0).getRatingPhase()+"期"+list.get(0).getDeptName()+"评级打分表.xlsx","utf-8"); response.setHeader("Content-disposition","attachment;filename="+fileName); //需要合并的列 int[] mergeColumeIndex = {0, 1, 2, 3, 4, 5,6,7,8,9,10,11,12,13,14,16,17,18}; //需要从第一行开始,列头第一行 int mergeRowIndex = 1; EasyExcel//将数据映射到DownloadDTO实体类并响应到浏览器 .write(new BufferedOutputStream(response.getOutputStream()), EasyExcelDemoVo.class) //07的excel版本,节省内存 .excelType(ExcelTypeEnum.XLSX) .head(head("评级打分",list.get(0).getDeptName(),list.get(0).getDeptType(),list.get(0).getRatingYear(),list.get(0).getRatingPhase(),list.get(0).getLocation())) //是否自动关闭输入流 .autoCloseStream(Boolean.TRUE) .registerWriteHandler(new CustomCellWriteHandler()) .registerWriteHandler(new ExcelFillCellMergeStrategy(mergeRowIndex, mergeColumeIndex)) // // 自定义列宽度,有数字会 .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) //设置excel保护密码 // .password("123456") .sheet().doWrite(list); } /** * 自定义头 * @param headTitle 统一头 * * @return 返回整个头list。 头部相同连续的单元格会自动合并。 */ private static List> head(String headTitle, String deptName,String deptType,String ratingYear,String ratingPhase,String location){ List> list = new ArrayList>(); /* List head1 = new ArrayList(); head1.add(headTitle); head1.add("金融机构名称:"+deptName); head1.add("评分模板ID"); List head2 = new ArrayList(); head2.add(headTitle); head2.add(""); head2.add("模板号");*/ List head1 = new ArrayList(); head1.add(headTitle); head1.add("金融机构名称:"+deptName); head1.add("模块"); List head2 = new ArrayList(); head2.add(headTitle); head2.add(""); head2.add("一级指标"); List head3 = new ArrayList(); head3.add(headTitle); head3.add("机构类型:"+deptType); head3.add("二级指标"); List head4 = new ArrayList(); head4.add(headTitle); head4.add(""); head4.add("对应资料调阅清单编号"); List head5 = new ArrayList(); head5.add(headTitle); head5.add("评级年份:"+ratingYear); head5.add("评分内容ID"); List head6 = new ArrayList(); head6.add(headTitle); head6.add(""); head6.add("评分内容"); List head7 = new ArrayList(); head7.add(headTitle); head7.add("评级期数:"+ratingPhase); head7.add("评分方式"); List head8 = new ArrayList(); head8.add(headTitle); head8.add(""); head8.add("问询"); List head9 = new ArrayList(); head9.add(headTitle); head9.add("所在地:"+location); head9.add("现场核查"); List head10 = new ArrayList(); head10.add(headTitle); head10.add(""); head10.add("打分"); List head11 = new ArrayList(); head11.add(headTitle); head11.add(""); head11.add("打分指引依据"); List head12 = new ArrayList(); head12.add(headTitle); head12.add(""); head12.add("资料调阅依据"); list.add(head1); list.add(head2); list.add(head3); list.add(head4); list.add(head5); list.add(head6); list.add(head7); list.add(head8); list.add(head9); list.add(head10); list.add(head11); list.add(head12); return list; } } ``` 5. 访问下载:http://localhost:8989/getExcel 关于EasyExcel自定义表头可查看:https://blog.csdn.net/Y_hanxiong/article/details/108686528 源码:https://gitee.com/Xiao_bear/easyExcel-list-solve #### 总结 1. 其实第一种方法也有第二种的部分,只是我当时候并不知道可以全部用vo类来进行接收,所以采用继承的方式,还是太年轻了,学的太浅了