SpringBoot+Mybatis-plus整合easyExcel批量导入Excel到数据库+导出Excel

  • 阿里云国际版折扣https://www.yundadi.com

  • 阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

    一、前言

    今天小编带大家一起整合一下easyExcel,之所以用这个,是因为easyExcel性能比较好,不会报OOM

    市面上常见的导入导出Excel分为三种:

    • hutool
    • easyExcel
    • poi

    hutooleasyExcel都是对poi的封装,使用起来更加方便!
    如果想使用poihutool导出的可以看一下小编的之前写的文章:

    使用POI+hutool导入Excel
    使用POI把查询到的数据表数据导出到Excel中,一个表一个sheet

    导出的话看一下这篇,下面主要以导入来展开介绍!
    EasyExcel导出Excel表格到浏览器,并通过Postman测试导出Excel

    二、导入依赖

    小编这里是3.0.X版本的,版本不同可能导致部分有出入,如果大家版本是3.1.X,可以去官方文档看看有不一样的!

    官方文档

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

    三、实体类

    这里可以自带的转换器:

    • @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
    • LocalDateTimeStringConverter

    或者自定义转化器:
    实现:implements Converter<T>

    具体文档:官方文档

    @ExcelProperty参数注意:

    这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配

    用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据

    /**
     * @author wangzhenjun
     * @date 2022/12/2 15:52
     */
    @Data
    public class Test {
    
        @TableId
        private Integer id;
        @ExcelProperty(index = 0)
        private String name;
        @ExcelProperty(index = 1)
        private Integer age;
        @ExcelProperty(index = 2,converter = LocalDateTimeStringConverter.class)
        private LocalDateTime time;
    }
    

    四、编写监听器

    注意点:
    这个监听器一定不要是单例的,被spring管理默认为单例,如果要使用@Component,一定要加上:
    @Scope("prototype"),这样在创建完后spring不会进行管理,每次都会是新bean!
    不加@Component在导入时要进行new ImportDataListener
    小编这里不想new了直接这样写!!如果不想这样,可以使用构造器set进行使用!
    BATCH_COUNT :数据阈值,超过了就会清理list,在之前可以进行保存到数据库中,方便内存回收,防治OOM
    这里保存到数据库中一般使用批量保存,不要解析到一行就去保存数据库中,这样数据量大会给数据库增加IO,导致挂掉!这里小编使用ServiceImplsaveBatch()方法,也可以自己写一下,像小编这样写,会出现循环依赖,加上@Lazy就行!

    /**
     * @author wangzhenjun
     * @date 2022/12/2 15:38
     */
    @Slf4j
    @Component
    // 每次bean都是新的,不要单例
    @Scope("prototype")
    public class ImportDataListener implements ReadListener<Test> {
    
        @Autowired
        @Lazy
        private TestService testService;
        
        /**
         * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
         */
        private static final int BATCH_COUNT = 100;
        /**
         * 缓存的数据
         */
        private List<Test> importExcelDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    
    
        /**
         * 这个每一条数据解析都会来调用
         *
         * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
         * @param context
         */
        @Override
        public void invoke(Test data, AnalysisContext context) {
            log.info("解析到一条数据:{}", JSON.toJSONString(data));
            importExcelDataList.add(data);
            // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
            if (importExcelDataList.size() >= BATCH_COUNT) {
                saveData();
                // 存储完成清理 list
                importExcelDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
            }
        }
    
        /**
         * 所有数据解析完成了 都会来调用
         *
         * @param context
         */
        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            // 这里也要保存数据,确保最后遗留的数据也存储到数据库
            saveData();
            log.info("所有数据解析完成!");
        }
    
        /**
         * 加上存储数据库
         */
        private void saveData() {
            log.info("{}条数据,开始存储数据库!", importExcelDataList.size());
            testService.saveBatch(importExcelDataList);
            log.info("存储数据库成功!");
        }
    }
    
    

    五、Controller

    /**
     * @author wangzhenjun
     * @date 2022/10/26 16:51
     */
    @Slf4j
    @RestController
    @RequestMapping("/test")
    public class TestController {
    
        @Autowired
        private TestService testService;
    
        @PostMapping("/import")
        public Result importExcel(@RequestBody MultipartFile multipartFile){
            testService.importExcel(multipartFile);
            return Result.success("ok");
        }
    }
    

    六、Service

    /**
     * @author wangzhenjun
     * @date 2022/10/26 16:55
     */
    public interface TestService extends IService<Test> {
        void importExcel(MultipartFile multipartFile);
    }
    

    七、ServiceImpl

    /**
     * @author wangzhenjun
     * @date 2022/10/26 16:56
     */
    @Service
    public class TestServiceImpl extends ServiceImpl<TestDbMapper, Test> implements TestService{
    
        @Autowired
        private ImportDataListener importDataListener;
    
        @SneakyThrows
        @Override
        public void importExcel(MultipartFile multipartFile) {
            InputStream inputStream = multipartFile.getInputStream();
            // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
            EasyExcel.read(inputStream, Test.class, importDataListener).sheet().doRead();
        }
    }
    
    

    八、Mapper

    /**
     * @author wangzhenjun
     * @date 2022/10/26 17:07
     */
    public interface TestDbMapper extends BaseMapper<Test> {
    }
    

    九、测试

    准备Excel数据:
    在这里插入图片描述
    postman上传:

    在这里插入图片描述
    控制台打印:
    在这里插入图片描述
    数据库查看:
    在这里插入图片描述
    完美搞定!!

    十、总结

    这样就完成了easyExcel批量导入Excel到数据库,还是有很多要注意的点:

    • 自定义转换器
    • 监听器不要单例
    • 保存数据库采用批量
    • 版本差距

    如果对你有帮助,还请不要吝啬您的发财小手,你的一键三连是我写作的动力,谢谢大家哈!!


    可以看下小编的微信公众号,文章首发看,欢迎关注,一起交流哈!!

  • 阿里云国际版折扣https://www.yundadi.com

  • 阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
    标签: Spring数据库

    “SpringBoot+Mybatis-plus整合easyExcel批量导入Excel到数据库+导出Excel” 的相关文章