easyexcel加springboot实现excel导入基础数据

  在企业级应用开发场景中,数据的高效流转与处理始终是核心需求之一。Excel 作为最常用的数据载体,如何快速、稳定地将其内容导入系统成为开发人员的高频任务。EasyExcel 凭借其轻量级架构、低内存占用和简洁的 API 设计,完美解决了传统 POI 框架在大数据量处理时内存溢出等痛点;而 Spring Boot 以 “约定优于配置” 的理念,极大简化了 Java 项目的搭建与部署流程。当 EasyExcel 与 Spring Boot 强强联合,不仅能实现基础数据的快速导入,更以标准化、模块化的开发模式,降低代码耦合度,提升项目的可维护性与扩展性。无论是财务报表的批量录入,还是基础档案的系统迁移,这套技术组合都能以优雅高效的方式完成数据交互,成为企业数字化转型中不可或缺的技术利器。

  第一步 导入easyexcel依赖

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>easyexcel</artifactId>

<version>3.3.2</version>

</dependency>

第二步  将基础数据domain类添加easyexcel注解

@ExcelIgnoreUnannotated

@ExcelIgnoreUnannotated 是 EasyExcel 提供的一个类级别注解,用来决定是否忽略那些没有被其他 EasyExcel 注解(如 @ExcelProperty)标注的字段。这个注解可以帮助开发者更好地控制数据模型与 Excel 表格之间的映射关系。简单来讲就是忽略没有@ExcelProperty注解的字段,添加在类上

@ExcelProperty

ExcelProperty注解用于匹配excel和实体类字段之间的关系。注意value值需要与导入excel表的表头字段对应

例如
@ExcelIgnoreUnannotated
@Data
@AutoMapper(target = MdItem.class, reverseConvertGenerate = false)
public class MdItemBo implements Serializable {
    @Serial
    private static final long serialVersionUID = 1L;
    /**
     * 产品物料ID
     */
    @NotNull(message = "产品物料ID不能为空", groups = {EditGroup.class})
    private Long itemId;

    /**
     * 产品物料编码
     */
    @ExcelProperty(value = "产品物料编码")
    @NotBlank(message = "产品物料编码不能为空", groups = {AddGroup.class, EditGroup.class})
    private String itemCode;

    /**
     * 产品物料名称
     */
    @ExcelProperty(value = "产品物料名称")
    @NotBlank(message = "产品物料名称不能为空", groups = {AddGroup.class, EditGroup.class})
    private String itemName;

第三步  建一个类去继承AnalysisEventListener。这个接口就easyexcel的公共监听器,实现里面三个方法,泛型使用刚定义的实体类,记得这个类要加@service注解给spring管理

@Service
@Slf4j
public class MdItemListener extends AnalysisEventListener<MdItemBo> {
    @Autowired
    private MdItemMapper  mdItemMapper;
    private List<MdItem> listData = new ArrayList<>();
    @Autowired
    private IMdUnitService mdUnitService;
    @Autowired
    private IMdBizCategoryService  mdBizCategoryService;
    @Override
    public void invoke(MdItemBo mdItemBo, AnalysisContext analysisContext) {
        log.info("解析到一条数据:{}", mdItemBo);
        MdItem mdItem = MapstructUtils.convert(mdItemBo, MdItem.class);
        LambdaQueryWrapper<MdItem> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(MdItem::getItemCode, mdItem.getItemCode());
        boolean notUnique = mdItemMapper.exists(wrapper);
        if (notUnique) {
            throw new RuntimeException("物料编码不能重复:" + mdItemBo.getItemCode());
        }
        String unitName = mdItemBo.getUnitName();
        String itemTypeName = mdItemBo.getItemTypeName();
        MdUnit unit = mdUnitService.lambdaQuery().eq(MdUnit::getUnitName, unitName).one();
        mdItem.setUnitId(unit.getUnitId());
        MdBizCategory mdBizCategory = mdBizCategoryService.lambdaQuery().eq(MdBizCategory::getCategoryName, itemTypeName).one();
        mdItem.setItemTypeId(mdBizCategory.getCategoryId());
        listData.add(mdItem);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        mdItemMapper.insertBatch(listData);
        listData.clear();
        log.info("所有数据解析完成!");
    }
    @Override
    public void onException(Exception exception, AnalysisContext context) {
        // 如果是某一个单元格的转换异常 能获取到具体行号
        // 如果要获取头的信息 配合doAfterAllAnalysedHeadMap使用
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
            log.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex()+1,
                excelDataConvertException.getColumnIndex()+1,exception);
        }   throw new RuntimeException("导入失败:" + exception.getMessage(), exception);
    }
}

invoke方法

easyexcel每解析一条数据就会调用一次,参数就是读取到的实体类,

可以在方法里面加上业务逻辑,如唯一性判断

doAfterAllAnalysed方法

excel所有行数据解析完会执行该方法

onException方法:异常处理方法

最后一步 后端就处理完了,写一个接口,调用read方法完成

@Autowired
private MdItemListener listener;
  @PostMapping("/import")
    public R<Void> importData(@RequestParam("file") MultipartFile file) throws IOException {
        try {
            // 读取Excel并自动填充到监听器
            EasyExcel.read(file.getInputStream(), MdItemBo.class,listener )
                .sheet("物料产品")
                .headRowNumber(1) // 跳过表头行
                .doRead();
            return R.ok();
        } catch (Exception e) {
            // 捕获异常并返回失败响应
            return R.fail(e.getMessage());
        }
    }
}

注意点 listener要使用依赖注入方式

前端只要上传文件到这个接口就完成了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值