配合反射封装和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其他的导入工具更快更稳。
(以上所有都仅仅代表个人观点,欢迎大家积极指出错误和问题一起讨论交流哈)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
EasyExcel是一款基于Java语言的开源Excel解析工具,可以帮助我们快速、高效地读取和写入Excel文件。下面是使用EasyExcel实现Excel读取的简单示例: 1. 添加EasyExcel依赖 在pom.xml文件中添加以下依赖: ``` <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2.6</version> </dependency> ``` 2. 定义Excel实体类 我们需要定义一个Java类来表示Excel中的一行数据,类中的属性对应Excel中的列。 例如,我们有一个Excel表格,包含姓名、年龄、性别三列,那么我们可以定义一个如下的实体类: ``` public class Student { private String name; private int age; private String gender; // getter、setter方法省略 } ``` 3. 使用EasyExcel读取Excel文件 使用EasyExcel读取Excel文件非常简单,只需要实现一个监听器类,并调用EasyExcel读取方法即可。 例如,我们有一个名为"students.xlsx"的Excel文件,包含一个名为"Sheet1"的工作表,我们可以使用如下代码来读取Excel文件: ``` public class ExcelReader { public static void main(String[] args) { String fileName = "students.xlsx"; String sheetName = "Sheet1"; EasyExcel.read(fileName, Student.class, new ExcelListener()) .sheet(sheetName) .doRead(); } } public class ExcelListener extends AnalysisEventListener<Student> { @Override public void invoke(Student student, AnalysisContext analysisContext) { // 处理每一行数据 } @Override public void doAfterAllAnalysed(AnalysisContext analysisContext) { // 处理完所有数据后的操作 } } ``` 在上面的代码中,ExcelListener是一个监听器类,继承自AnalysisEventListener,其中的invoke()方法会在每读取一行数据时被调用,doAfterAllAnalysed()方法会在读取完所有数据后被调用。 通过调用EasyExcel.read()方法,我们可以指定要读取Excel文件名、工作表名和实体类类型,然后调用sheet()方法指定要读取的工作表名,最后调用doRead()方法开始读取Excel文件。 在invoke()方法中,我们可以获取到当前行的数据,可以根据需要对其进行处理。在doAfterAllAnalysed()方法中,我们可以在所有数据读取完后进行一些操作,例如关闭资源等。 以上就是使用EasyExcel实现Excel读取的简单示例,希望对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值