相关文章推荐
坚强的大蒜  ·  StringAppender ...·  1 年前    · 
听话的吐司  ·  Linux ...·  1 年前    · 
含蓄的镜子  ·  linq to sql (Group ...·  1 年前    · 

【二】springboot整合swagger(自定义)(超详细)

【三】springboot整合token(超详细)

【四】springboot整合mybatis-plus(超详细)(上)

【五】springboot整合mybatis-plus(超详细)(下)

【六】springboot整合自定义全局异常处理

【七】springboot整合redis(超详细)

【八】springboot整合AOP实现日志操作(超详细)

【九】springboot整合定时任务(超详细)

【十】springboot整合redis实现启动服务即将热点数据保存在全局以及redis(超详细)

【十一】springboot整合quartz实现定时任务优化(超详细)

【十二】springboot整合线程池解决高并发(超详细,保你理解)

【十三】springboot整合异步调用并获取返回值(超详细)

【十四】springboot整合WebService(超详细)

【十五】springboot整合WebService(关于传参数)(超详细)

【十六】springboot整合WebSocket(超详细)

【十七】springboot整合WebSocket实现聊天室(超详细)

【十八】springboot实现自定义全局异常处理

【十九】springboot整合ElasticSearch实战(万字篇)

【二十】springboot整合过滤器实战

【二十一】springboot整合拦截器实战并对比过滤器

【二十二】springboot整合activiti7(1) 实战演示篇

【二十三】springboot整合spring事务详解以及实战

【二十四】springboot使用EasyExcel和线程池实现多线程导入Excel数据

【二十五】springboot整合jedis和redisson布隆过滤器处理缓存穿透

在公司开发时,遇到一个很常见的 导入功能 的需求,需要 导入Excel文件 ,由此想到了阿里巴巴的 EasyExcel 这个方便的工具,当客户给我说需要支持大数据量导入时,我想到了使用线程池来 多线程处理 导入数据库这个操作。由此本章记录一下这次操作。

qq交流群导航——>231378628

首先,整体的大概流程差不多是这个样子:

真实项目上是公司封装的RPC框架,这次demo直接使用的前面的整合 mybatis-plus 的demo来写的。

首先看下 目录结构

图片中 框选部分 就是本章需要 修改 到的或者 新创建的文件 ,后面一一解读,下面的描述可以对标这个图片中的类名。

然后介绍下本章需要做的准备工作:

  • 数据库表
  • 修改application配置文件,修改tomcat的最大文件上传限制(否则excel文件太大,上传会报错)
  • 开启mybatis-plus的批量插入功能,mybatis-plus默认只有insert这个单条插入功能(若自己的项目不使用这个,则不需要,这只是我的demo上没有批量插入方法)
  • 创建excel多线程导入接口所需的各个类

一、准备数据库和Excel文件

二、引入所需依赖

三、修改配置文件,修改文件大小默认限制

四、开启mybatis-plus的批量保存功能

五、创建所需工具类

六、创建业务各层代码

七、创建easyExcel事件监听器

八、创建自定义线程类

九、测试单线程和多线程处理的效率

一、准备数据库和Excel文件

CREATE TABLE `deadman` (
  `uid` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL COMMENT '主键',
  `idCard` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '身份证号',
  `userName` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '死者姓名',
  `sex` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '死者性别',
  `age` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '死者年龄',
  `reason` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '死因',
  `house` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '安排地狱层数',
  PRIMARY KEY (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8_bin;

二、引入所需依赖

主要是导入easyExcel 所涉及的依赖 ,如下:

三、修改配置文件,修改文件大小默认限制

这个 必须修改 ,不然后面接口都进不去,默认tomcat服务器是限制了上传文件的大小的。

spring:
  servlet:
    multipart:
      max-file-size: 100MB
      max-request-size: 100MB
server:
  tomcat:
    max-swallow-size: 100MB 

四、开启mybatis-plus的批量保存功能

这次demo整合的rpc框架是Mybatis-plus,所以找了一个方法去 实现批量保存 ,如下:

1、新建一个SpiceSqlInjector类

PS:注意框选部分类名。

2、创建SpiceBaseMapper接口类

PS:本来是业务的接口层去继承BaseMapper,此处又它去继承BaseMapper,然后加如上图这个方法,注意名称必须是这个否则后面调用该方法会报错。

3、创建业务所需的Mapper层

继承刚才自定义的接口类。

由此就可以通过注入DeadManMapper调用上面的方法实现批量插入了。

五、创建所需工具类

在通过EasyExcel导入时,会涉及到一些数据类型不满足入参之类的,本次demo涉及到如下工具类。

1、MultipartFileToFileUtils

将传入的MultipartFile类型转为File类型,Controller接收到的是MultipartFile类型,EasyExcel.read方法所需要的是File类型。

2、SpringJobBeanFactory

在监听器类注入mappe时会报空指针,使用如下工具类继承ApplicationContextAware,获取bean对象。

六、创建业务各层代码

1、controller层

2、映射数据库的实体类

关于mybatis-plus相关的注解可以去看看前面的文章。

3、easyExcel的实体类

index指的是excel表上面的行编号,例如

4、service层

5、service的实现类层

上面的方法是多线程处理的事件监听器,下面的是单线程的事件监听器,后面针对两种方式都做一下对比。

七、创建easyExcel事件监听器

1、单线程的事件监听器

解析:该监听器继承自AnalysisEventListener类,泛型指定为上面easyExcel指定的实体类对象类。重写该类的两个方法:invoke和doAfterAllAnalysed。

invoke():该方法会从excel表的第二行开始读取数据。

doAfterAllAnalysed():当invoke将excel的数据全部解析完后,会执行该方法,所以在该类进行数据的入库即可。

此时,单线程的导入已经完成,我们最后再对比测试两种监听器的效率,现在创建多线程事件监听器。

2、多线程的事件监听器

解析:同样是重写上面说的那两个方法。invoke方法的处理不变,修改doAfterAllAnalysed方法的处理,在该方法通过创建线程池的方法,将创建的线程任务提交到线程池,让线程池进行多线程任务的执行,从而实现多线程执行导入操作。

流程:创建线程池——》计算每个线程需要处理的数据——》创建CountDownLatch对象(保证最后每个线程会回到主线程)——》循环线程数量并提交线程任务到线程池——》执行CountDownLatch对象的await方法,让当前线程处于等待状态,等待CountDownLatch减少为1后会唤醒当前线程——》每个线程处理自己的数据并在处理完后执行CountDownLatch对象的countDown方法,让CountDownLatch对象的值减一——》当CountDownLatch的值为0时(说明线程池类的线程任务以及执行完成),执行主线程的代码——》关闭线程池

八、创建自定义线程类

此步骤创建上面描述的线程任务类。

实现Callable或者Runable或者继承Thread都行,此处实现Runable,重写run方法。

根据传入的划分给他的数据区间,将该区间的数据通过subList方法取出来之后,执行上面实现的批量插入方法进行数据的入库操作。为了防止代码出现问题不执行coutDown方法,将该句代码写入finally。

PS:CountDownLatch的两个方法(countDown,await)配合使用,保证每个线程的代码执行时,主线程进入等待,然后等各个线程任务执行完后,又回到主线程进行执行。

九、测试单线程和多线程处理的效率

上面已经完成了单线程和多线程事件监听器的编写,下面开始测试两者的效率。

1、使用单线程事件监听器

结果:100033条数据,数据正确。

2、使用多线程事件监听器

结果:100033条数据,数据正确,确实快了很多。

我觉得对于业务简单的多线程处理类似场景的都可以拿这个demo拿去改造一下,如果有问题,谢谢大家指出。瑞思拜。

qq交流群导航——>231378628

光阴似箭、岁月如梭转眼间已至2020年末 想到这一年发生的糟心事不由得感慨万千、思绪横飞特别是互联网方面内卷依旧 竞争依旧激烈、就业环境依旧严酷因此巩固自身核心竞争力扎实自身技术根基 势在必行!!! 故而,debug也趁此机会撸了一套新的实战课程:《 Java 核心技术-典型案例与面试实战系列二(基于 Spring Boot2.0)》 顾名思义,这是debug为诸位规划的实战系列课程,其课程内容、学习收益、课程目录、技术栈等内容参见下图:  其课程的思维导图如下所示: 剩下的即为本课程相关技术在实战期间的效果图:       只有业务层,可根据需求修改 @Override public CommonResultVo mxImport(String date, MultipartFile[] files) { List<String> errorTableNames = new ArrayList<>(); List<List<Object>> listenerList = getListenerList(date, files); 一、为什么用它?      由于apache poi和jxl, excel POI都有一个严重的问题,就是非常消耗内存,特别处理 数据 量多时,速度慢并且时有异常发生,所以改用由阿里研发的 easyExcel 更可靠一些,它的官方建议对于1000行以内的采用原来poi的写法一次读写,但于1000行以上的 数据 ,有用了一行行进行解析的方案,这样避免了内存的溢出。 二、 easyExcel 主要功能: 1.支持 Excel 导入 与导出,同时支持xls和xlsm,即07版本和03版本(官方建议03版本不要超过2000行)的 Excel 文件格式。 2.支持pojo注释时,映射成为 java 实体模型。 3.支持多个sheet,同 @Test public void test() throws InterruptedException, ExecutionException { String resStr = "{\"languageCode\":\"en-US\",\"categoryType\":\"\",\"categoryLevel\":\"\"}"; GoodsPriceSellListDTO condition = JSON.parseObject(resStr, GoodsPr.. excel 表格的 导入 与导出,可以说是业务系统里比较常见的功能了,早些时候相信很多人都是 使用 POI 实现 excel 导入 与导出功能,后来出现了 easyexcel ,从我自己的 使用 感受来说,我更喜欢 使用 easyexcel ,除了封装的比较好外,最重要的是对超级大 excel 导入 有了更好的方案,与POI相比,速度更快,占用内存更少。 Java 结合Alibaba EasyExcel ,通过 多线程 加redis缓存的方式,将明细表 数据 带有 数据 格式(字体格式,表头冻结,单元格合并等格式),导出百万 数据 量,响应时间30s左右。 3、 线程池 不需要每次都去创建,响应时间更快。 我们详细的解释一下为什么要 使用 线程池 ? 在 java 中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际 使用 中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换