Easy Excel解析excel后全部为空的问题(附上EasyExcel使用指南博客)
问题描述:
将excel解析之后VO类列表全为null
原因描述:
在Java中,@Accessors(chain = false) 是Lombok库中的一个注解,用于控制生成的setter方法是否返回this对象,从而支持方法链式调用。具体来说:
当 @Accessors(chain = true) 时,生成的setter方法会返回当前对象实例,这样就可以通过链式调用来设置对象的属性值,例如 obj.setProp1(value1).setProp2(value2);。 当 @Accessors(chain = false) 时,生成的setter方法不返回当前对象实例,因此不能通过链式调用来设置属性值,而是通过单独的语句设置每个属性值,例如 obj.setProp1(value1); obj.setProp2(value2);。 在你提供的代码中,@Accessors(chain = false) 的作用是明确告知Lombok不要生成支持链式调用的setter方法。这样做可能是因为在处理Excel导入时,通过链式调用setter可能会导致数据处理逻辑混乱或者不符合业务需求。因此,通过设置 chain = false 可以避免这类问题的发生,确保数据导入过程的稳定性和可控性。
解决方案:
VO类加上@Accessors(chain = false)
package cn.iocoder.yudao.module.as.controller.admin.financialaidretirementlist.vo; import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; import cn.iocoder.yudao.module.system.enums.DictTypeConstants; import com.alibaba.excel.annotation.ExcelProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; import javax.validation.constraints.NotNull; import java.time.LocalDateTime; @Schema(description = "管理后台 - 家庭经济困难退役复学学生汇总导入excel VO") @Data @Builder @AllArgsConstructor @NoArgsConstructor @Accessors(chain = false) // 设置 chain = false,避免导入有问题 public class FinancialAidRetirementListExcelVO { /* @Schema(description = "序号", example = "1") @ExcelProperty("序号") private Long id;*/ @Schema(description = "学院名字", example = "赵六") @ExcelProperty(value = "学院") private String collegeName; @Schema(description = "姓名", example = "芋艿") @ExcelProperty("姓名") private String studentName; @Schema(description = "学生性别(男生为1,女生为2,未知为3)") @ExcelProperty(value = "性别", converter = DictConvert.class) @DictFormat(DictTypeConstants.USER_SEX) private Integer studentSex; @Schema(description = "民族") @ExcelProperty("民族") private String studentNation; @Schema(description = "年级") @ExcelProperty("年级") private String studentGrade; @Schema(description = "专业") @ExcelProperty("专业") private String studentMajor; @Schema(description = "班级") @ExcelProperty("班级") private String studentClass; @Schema(description = "学号") @ExcelProperty("学号") private String studentCode; @Schema(description = "入学年月") @ExcelProperty("入学年月") private String studentEnrollmentDate; @Schema(description = "困难认定等级,如1,2,3") @ExcelProperty("困难认定等级") private Integer studentDifficultyLevel; @Schema(description = "户籍地") @ExcelProperty("户籍地") private String studentHometown; @Schema(description = "退役复学学生核对情况,如退役复学学生") @ExcelProperty("退役复学学生核对情况") private String studentCheck; }
使用指南博客:
一文带你彻底玩转EasyExcel:导入导出excel数据起飞_easyexcel 导入excel-CSDN博客
官方文档:
EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel 官网 (alibaba.com)
简单使用示例:
无监听器,单个sheet,转化为list
EasyExcel.read(file.getInputStream(), head, null) .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 .doReadAllSync();
有监听器,多个sheet,监听器处理业务:
EasyExcel.read(file.getInputStream(), FinancialAidRetirementListExcelVO.class, new FinancialAidRetirementListExcelListener()).sheet("2023-2024学年家庭经济困难学生汇总表").doRead();
监听器示例:
package cn.iocoder.yudao.module.as.controller.admin.financialaidretirementlist; import cn.iocoder.yudao.module.as.controller.admin.financialaidretirementlist.vo.FinancialAidRetirementListExcelVO; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import com.alibaba.excel.metadata.data.ReadCellData; import com.alibaba.excel.util.ConverterUtils; import com.alibaba.excel.util.ListUtils; import lombok.extern.slf4j.Slf4j; import java.util.List; import java.util.Map; /** * @author fjzheng * @version 1.0 * @date 2024/4/12 16:22 */ @Slf4j public class FinancialAidRetirementListExcelListener extends AnalysisEventListener<FinancialAidRetirementListExcelVO> { /** * 每隔100条处理下,然后清理list ,方便内存回收 */ private static final int BATCH_COUNT = 100; /** * 缓存的数据 */ private List<FinancialAidRetirementListExcelVO> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT); /** * * @param exception * @param context * @throws Exception */ @Override public void onException(Exception exception, AnalysisContext context) throws Exception { log.error("======>>>解析异常:", exception); throw exception; } @Override public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) { log.info("表头数据:{}", ConverterUtils.convertToStringMap(headMap, context)); } //@Override //public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) { //log.info("解析到一条头数据:{}", JSON.toJSONString(headMap)); // 如果想转成成 Map<Integer,String> // 方案1: 不要implements ReadListener 而是 extends AnalysisEventListener // 方案2: 调用 ConverterUtils.convertToStringMap(headMap, context) 自动会转换 //} /** * 当读取到一行数据时,会调用这个方法,并将读取到的数据以及上下文信息作为参数传入 * 可以在这个方法中对读取到的数据进行处理和操作,处理数据时要注意异常错误,保证读取数据的稳定性 * @param user * @param context */ @Override public void invoke(FinancialAidRetirementListExcelVO user, AnalysisContext context) { log.info("解析到一条数据:{}", user); cachedDataList.add(user); if (cachedDataList.size() >= BATCH_COUNT) { // 处理缓存的数据,比如说入库。。。 // 然后清空 cachedDataList.clear(); } } /** * 当每个sheet所有数据读取完毕后,会调用这个方法,可以在这个方法中进行一些收尾工作,如资源释放、数据汇总等。 * @param context */ @Override public void doAfterAllAnalysed(AnalysisContext context) { // 收尾工作,处理剩下的缓存数据。。。 log.info("sheet={} 所有数据解析完成!", context.readSheetHolder().getSheetName()); } }
多个sheet,表头在第二行,无监听器,生成list
List<FinancialAidRetirementListExcelVO> list = EasyExcel.read(file.getInputStream(), FinancialAidRetirementListExcelVO.class, null).sheet("2023-2024学年家庭经济困难学生汇总表").headRowNumber(2).doReadSync();