配合反射封装和EasyExcel完成Excel读取入库

配合反射封装和EasyExcel完成Excel读取入库

相信大家在写Java程序时,对于导入Excel文件各种读行读列的超长代码肯定早就恨之入骨了,哪怕是一层又一层的封装下去,整体也是非常的庞大,并且读取速度瓶颈非常大,稍稍上百万条数据就会导致前端响应超时等等问题,体验非常不好,所以今天给大家介绍一个新的工具EasyExcel,下面会贴出他的用法以及非常使用的工具类。

EasyExcel的使用

官方网址:EasyExcel语雀
有什么不解的问题可以去上面的地址官网里查找API文档,里面的使用步骤也说明的非常清晰。
EasyExcel的使用方式非常的多,今天我在这里就说明一种,其他的可以自行去官网了解,好的话不多说上代码。

导入Excel接收的DTO类

@Data
public class UserDTO {
	@ExcelProperty(value = "用户名", index = 0)
	private String userName;
	
	@ExcelProperty(value = "密码", index = 2)
	private String userName;
}

业务层执行导入代码

@Resource
private UserMapper userMapper;
public void importData(MultipartFile file) {
        try {
            EasyExcel.read(file.getInputStream(), UserDTO.class,
                    new UserExcelImportListener(userMapper)).sheet().doRead();
        } catch (Exception e) {
            throw new RuntimeException(e.getLocalizedMessage());
        }
    }

导入监听类

public class UserExcelImportListener extends AnalysisEventListener<UserDTO> {
	private UserMapper userMapper;
	private static Map<Integer, CellData> HEADER_MAP = null;
	public UserExcelImportListener(UserMapper userMapper) {
		this.userMapper = userMapper;
	}
	@Override
	public void invoke(Map<Integer, CellData> headMap, AnalysisContext context) {
		HEADER_MAP = headMap;
	}
	@Override
	public void invoke(UserDTO dto, AnalysisContext analysisContext) {
		int rowIndex = ((XlsxReadSheetHolder) ((DefaultXlsxReadContext)analysisContext).currentReadHolder()).getRowIndex();
		// 判断当前对象里是否有为空的字段
		ImportObjectNullUtil.validateByHeaderMap(HEADER_MAP, dto, rowIndex);
		// 下面可以进行特殊对象处理,比如数字,时间单独处理等等...
		// 最后处理完入库
		userMapper.insert(dto);
	}
	@Override
	public void doAfterAllAnalysed(AnalysisContext analysisContext) {
		// 防止导入空的Excel
		if (analysisContext.readRowHolder().getRowIndex() <= 0) {
			throw new RuntimeException("import file row lt zero!");
		}
	}
}

对象判空工具类

public class ImportObjectNullUtil {
	/**
     * excel导入根据表头Map判断当前行不能为空的字段是否为空
     * @param HEADER_MAP 表头Map
     * @param dto 当前行对象
     * @param rowIndex 当前行下标
     */
    public static void validateByHeaderMap(Map<Integer, CellData> HEADER_MAP, Object dto, int rowIndex) {
        CellData cellData = HEADER_MAP.get(validateField(dto, null, null));
        if(null != cellData){
            log.error(ExceptionMessageUtils.returnRowCellExceptionMessage(rowIndex, cellData.getStringValue()) + "为空");
            throw new RuntimeException(ExceptionMessageUtils.returnRowCellExceptionMessage(rowIndex, cellData.getStringValue()) + "为空");
        }
    }
    /**
     * 判断对象内不能为空的字段是否为空
     * @param object 判空对象
     * @param noJudgments 不在导入列的属性数组
     * @param emptys 在导入列可以为空的属性数组
     * @return 返回为空的属性下标
     */
    public static Integer validateField(Object object, String[] noJudgments, String[] emptys){
        Integer target = 0;
        for (Field f : object.getClass().getDeclaredFields()) {
            f.setAccessible(true);
            try {
                // 不在导入列的属性
                if(arrIsNotNull(noJudgments) && Arrays.asList(noJudgments).contains(f.getName())){
                    continue;
                }
                // 在导入列可以为空的属性
                if (arrIsNotNull(emptys) && Arrays.asList(emptys).contains(f.getName())) {
                    target ++;
                    continue;
                }
                // 在导入列不能为空的列进行判断
                if (f.get(object) == null) {
                    break;
                }
                // 计算下标
                target ++;
            } catch (IllegalArgumentException e) {
                log.error("对象属性解析异常" + e.getMessage());
                return target;
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                log.error("对象属性解析异常" + e.getMessage());
                return target;
            }
        }
        return target;
    }

    /**
     * 数组判空
     * @param array
     * @return
     */
    public static boolean arrIsNotNull(Object[] array) {
        return null != array && array.length > 0;
    }
}

好的,如上就是整个导入过程,上面注释不多,我解释一下,大致可以划分为如下几个点:

  1. 建立用来接收读取的每一行的类,每个属性对应一个列。
  2. 建立导入监听,在监听里面进行非空判断,进行类型转换或者其他的逻辑。
  3. 使用EasyExcel在业务层使用监听读取前端传入的Excel文件流。
  4. 导入成功 or 导入失败。

最后总结一下为什么使用EasyExcel,以及为什么EasyExcel做导入会更快:

实际上为什么使用EasyExcel最开始已经做了说明了,无非两个词,1:简单,2:快速,简单从代码层面就可以看出来了 短短几十行代码包括了所有的导入过程。
那为什么EasyExcel会比其他的导入工具更快速呢,原因在源码里,通过分析它的源码不难看出,EasyExcel在接收到文件流导出之前,配合es(搜索引擎)进行读取,每次读取前会在本地磁盘建立一个临时空间用来加快读取速度,这就是为什么EasyExcel会比POI其他的导入工具更快更稳。
(以上所有都仅仅代表个人观点,欢迎大家积极指出错误和问题一起讨论交流哈)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值