Java中easyExcel实现通用导入

一,EasyExcel使用
1.1 添加EasyExcel依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.2.7</version>
</dependency>

1.2 导入实体

/**
 * 导入模板
 */
@Data
public class FrozenBuckleImportDTO implements Serializable {
    @ExcelProperty(value = "客户号*",index = 0)
    @ExcelDataValid(message = "客户号不能为空,请填写后重新导入!")
    @Size(max = 64, message = "客户号 不能超过64")
    private String custNo;

    @ExcelProperty(value = "客户名称*",index = 1)
    @ExcelDataValid(message = "客户名称不能为空,请填写后重新导入!")
    @Size(max = 128, message = "客户名称 不能超过128")
    private String custCnName;

    @ExcelProperty(value = "客户类型",index = 2,converter = WhetherConverter.class)
    @ExcelDataValid(message = "客户类型不能为空,请填写后重新导入!")
    @Size(max = 12, message = "客户类型 不能超过12")
    @DictTypeProperty("CustRisk_CustClass")
    private String custClass;
    }

1.3 导入实现
a. Controller

 @ApiOperation(value = "导入", notes = "导入")
    @PostMapping(value = "/importExcel", headers = {"content-type=multipart/form-data"})
    public ObjectRestResponse importExcel(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return new ObjectRestResponse(HttpStatus.HTTP_INTERNAL_ERROR, "文件不能为空", null );
        }
        try {
            return frozenBuckleService.importExcelInfo(file);
        } catch (Exception e) {
            log.error("导入异常:", e);
            String substring = e.getMessage().substring(66);
            String s = substring.replaceAll("[a-zA-Z={}]", "");
            return new ObjectRestResponse(HttpStatus.HTTP_INTERNAL_ERROR, "导入异常:"+s, null );
        }
    }

b. 具体实现方法

@Override
    public ObjectRestResponse importExcelInfo(MultipartFile file) throws Exception{
        List<FrozenBuckleImportDTO> list = EasyExcel.read(new BufferedInputStream(file.getInputStream())).head(FrozenBuckleImportDTO.class).sheet().doReadSync();
        log.info("进入导入方法……{}",list);
        EasyExcel.read(new BufferedInputStream(file.getInputStream()),FrozenBuckleImportDTO.class, EasyExcelUtils.getReadListener(dataProcess(),100)).sheet().doReadSync();
        return ObjectRestResponse.ok();
    }

c. 数据处理方法

 public Consumer<List<FrozenBuckleImportDTO>> dataProcess() {
        log.info("具体执行函数……");
        List<Td51FrozenBuckle> list = new ArrayList<>();
        Consumer<List<FrozenBuckleImportDTO>>  consumer= datas -> {
            for (FrozenBuckleImportDTO data : datas) {
                log.info("获取数据:{}",data);
                log.info("添加自己的业务逻辑代码……");
            }
            //数据处理
            log.info("所有数据……{}",list);
        };
        return consumer;
    }

二,数据转换

  1. 数据转换实现方法。数据字典采用缓存获取的方式处理,对应的数据字典类型可以使用注解形式获取,其中的缓存获取数据字典需要自己根据项目情况实现。
/**
 * @ClassName: 数据转换
 * @Description TODO 获取数据字典进行数据转换
 **/
@Slf4j
public class WhetherConverter  implements Converter<String> {

    private Map<String, String> getDictMap(ExcelContentProperty contentProperty) throws Exception{
        //获取当前字段
        Field field = contentProperty.getField();
        //获取字段上的注解
        DictTypeProperty declaredAnnotation = field.getDeclaredAnnotation(DictTypeProperty.class);
        if (Objects.isNull(declaredAnnotation)){
            throw new Exception("没有这个注解");
        }
        //获取DictTypeProperty的value
        String dictType = declaredAnnotation.value();
        //缓存类型
        JSONArray jsonArray = (JSONArray) WebUtils.getRedisValue("aml-basic:dict:"+dictType);
        //取出字典
        Map<String, String> dicMap = jsonArray.stream().collect(Collectors.toMap(e -> ((JSONObject) e).get(CommonConstants.DICT_VALUE).toString(), e -> ((JSONObject) e).get(CommonConstants.DICT_LABEL).toString()));
        return dicMap;
    }


    /** Java数据类型 **/
    @Override
    public Class supportJavaTypeKey() {
        return String.class;
    }

    /** Excel文件中单元格的数据类型  string **/
    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
        return CellDataTypeEnum.STRING;
    }

    /** 读取Excel文件时 **/
    @Override
    public String convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        String value = cellData.getStringValue();
        Map<String, String> dictMap = getDictMap(contentProperty);
        log.info("读取Excel数据:{},{}",value,dictMap);
        for (String key: dictMap.keySet()){
            String dictValue = dictMap.get(key);
            if(dictValue.equals(value)){
                return key;
            }
        }
        return value.toString();
    }


    /** 写入Excel文件时 **/
    @Override
    public CellData convertToExcelData(String value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        Map<String, String> dictMap = getDictMap(contentProperty);
        String dictValue = dictMap.get(value);
        log.info("写入Excel数据:{},{},{}",value,dictMap,dictValue);
        return new CellData(dictValue);
    }
}
  1. 添加自定义注解
 /**
 * @ClassName: DictTypeProperty
 * @Description TODO 数据字典注解,没有具体实现,用作导出转换
 **/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DictTypeProperty {
    /** 数据字典 **/
    String value() default "";
}
  1. 导入实体中使用
    例:如图,
    a. 在注解@ExcelProperty添加数据字典转换属性converter = WhetherConverter.class
    b. 添加自定义注解@DictTypeProperty(“CustRisk_CustClass”),value为数据字典类型
    @ExcelProperty(value = "客户类型",index = 2,converter = WhetherConverter.class)
    @ExcelDataValid(message = "客户类型不能为空,请填写后重新导入!")
    @Size(max = 12, message = "客户类型 不能超过12")
    @DictTypeProperty("CustRisk_CustClass")
    private String custClass;

三,通用的监听工具类,避免每添加一个导入都需要写监听的冗余代码。

/**
 * @ClassName: easyexcel工具类
 * @Description TODO
 **/
@Slf4j
public class EasyExcelUtils<T> {
    /**
     * 获取读取Excel的监听器对象
     * 为了解耦及减少每个数据模型bean都要创建一个监听器的臃肿, 使用泛型指定数据模型类型
     * 使用jdk8新特性中的函数式接口 Consumer
     * 可以实现任何数据模型bean的数据解析, 不用重复定义监听器
     * @param consumer 处理解析数据的函数, 一般可以是数据入库逻辑的函数
     * @param threshold 阈值,达到阈值就处理一次存储的数据
     * @param <T> 数据模型泛型
     * @return 返回监听器
     */
    public static <T> AnalysisEventListener<T> getReadListener(Consumer<List<T>> consumer, int threshold) {

        return new AnalysisEventListener<T>() {

            /**
             * 存储解析的数据 T t
             * ArrayList基于数组实现, 查询更快
             * LinkedList基于双向链表实现, 插入和删除更快
             */
            List<T> dataList = new LinkedList<>();

            /**
             * 每解析一行数据事件调度中心都会通知到这个方法, 订阅者1
             * @param data 解析的每行数据
             * @param context
             */
            @Override
            public void invoke(T data, AnalysisContext context) {
                log.info("订阅者1执行时间------{}----数据:{}", LocalDateTime.now(),data);
                valid(data);
                dataList.add(data);
                // 达到阈值就处理一次存储的数据
                if (dataList.size() >= threshold) {
                    consumer.accept(dataList);
                    dataList.clear();
                }
            }

            /**
             * excel文件解析完成后,事件调度中心会通知到该方法, 订阅者2
             * @param context
             */
            @Override
            public void doAfterAllAnalysed(AnalysisContext context) {
                log.info("订阅者2执行时间------{}----数据:{}", LocalDateTime.now(),dataList);
                // 最后阈值外的数据做处理
                if (dataList.size() > 0) {
                    consumer.accept(dataList);
                }
            }

            /**
             * 异常方法 (类型转换异常也会执行此方法)  (读取一行抛出异常也会执行此方法)
             */
            @Override
            public void onException(Exception exception, AnalysisContext context) {
                log.info("数据异常");
                // 如果是某一个单元格的转换异常 能获取到具体行号
                if (exception instanceof ExcelDataConvertException) {
                    ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
                    log.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),
                            excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());
                    throw new RuntimeException("第"+excelDataConvertException.getRowIndex()+"行" +
                            ",第" + (excelDataConvertException.getColumnIndex() + 1) + "列读取错误");
                }
                //抛出非空校验异常
                throw new ApiException(exception.getMessage());
            }
        };

    }

    /**
     * 获取读取Excel的监听器对象, 不指定阈值, 默认阈值为 2000
     * @param consumer
     * @param <T>
     * @return
     */
    public static <T> AnalysisEventListener<T> getReadListener(Consumer<List<T>> consumer) {
        return getReadListener(consumer, 2000);
    }

    /**
     * Excel导入字段校验
     * @param object 校验的JavaBean 其属性须有自定义注解
     */
    public static void valid(Object object) {
        Field[] fields = object.getClass().getDeclaredFields();
        for (Field field : fields) {
            //设置可访问
            field.setAccessible(true);
            //属性的值
            Object fieldValue = null;
            try {
                fieldValue = field.get(object);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("导入参数检查失败");
            }
            //是否包含必填校验注解
            boolean isExcelValid = field.isAnnotationPresent(ExcelDataValid.class);
            if (isExcelValid && Objects.isNull(fieldValue)) {
                log.error(field.getAnnotation(ExcelDataValid.class).message());
                throw new RuntimeException(field.getAnnotation(ExcelDataValid.class).message());
            }
        }
    }
}

参考:
https://blog.csdn.net/u013044713/article/details/120249233
https://blog.csdn.net/weixin_54566947/article/details/136459335

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很高兴为您解答这个问题!关于 Java 实现 easyexcel 读取数据导入数据库的例子,我可以提供一些参考: 1. 您可以先在项目引入 easyexcel 的相关依赖,例如: <!-- 引入 easyexcel --> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2.10</version> </dependency> 2. 创建一个实体类来存储 Excel 的数据,例如: @Data public class ExcelData { @ExcelProperty(index = 0) private String name; @ExcelProperty(index = 1) private Integer age; @ExcelProperty(index = 2) private String gender; // 省略 getter 和 setter 方法 } 3. 在代码使用 easyexcel 进行读取 Excel 数据,例如: public void readExcelData() throws IOException { InputStream inputStream = new FileInputStream("data.xlsx"); EasyExcel.read(inputStream, ExcelData.class, new ExcelDataListener()).sheet().doRead(); } 4. 实现一个监听器来处理读取到的数据,例如: public class ExcelDataListener extends AnalysisEventListener<ExcelData> { @Override public void invoke(ExcelData data, AnalysisContext context) { // 处理读取到的数据,可以写入数据库等操作 } @Override public void doAfterAllAnalysed(AnalysisContext context) { // 读取完成后的操作 } } 5. 在 invoke 方法可以将读取到的数据写入数据库等操作,例如: @Override public void invoke(ExcelData data, AnalysisContext context) { // 将读取到的数据写入数据库 jdbcTemplate.update("INSERT INTO user (name, age, gender) VALUES (?, ?, ?)", data.getName(), data.getAge(), data.getGender()); } 希望以上内容能够对您有所帮助!如果您还有其他问题,欢迎提出。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值