记录万级数据Excel导入优化

实现方式:Poi+数据校验+多线程+异步批量分组插入

poi依赖

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
            <version>3.9</version>
        </dependency>

  

参数校验:非空,或正则校验

class DownloadTemplateVO extends IExcelModel{
  //卡
  @Excel(name="号")
  @NotBlank(message = "号不能为空")
  @BeanProperty
  var no:String =_

  //登记时间
  @Excel(name="登记时间" ,format = "yyyy-MM-dd hh:mm:ss")
  @BeanProperty
  @NotNull
  var enterTime:Date =_

  //失败原因  用于设置失败原因
 // @Excel(name="失败原因")
  @BeanProperty
  var errorMsg:String =_

}

  

controller

 @PostMapping(value = Array("/importExcel"), produces = Array(MediaType.MULTIPART_FORM_DATA_VALUE))
  def importExcel(entityVO: CardVO, @RequestParam("multipartFile") multipartFile: MultipartFile,
                  request: HttpServletRequest, response: HttpServletResponse) {
    //获取文件后缀
    val suffix = multipartFile.getOriginalFilename().substring(multipartFile.getOriginalFilename().lastIndexOf(".") + 1)
    //文件格式
    val EXT_FILE_NAME = "xlsx"
    if (!EXT_FILE_NAME.equals(suffix)) {
      return write(response, BaseResponse.error("文件格式不正确"))
    }
    val importParams: ImportParams = new ImportParams()

    // 需要验证
    importParams.setNeedVerify(true);
    val list = new java.util.ArrayList[SuccessCardVO]()

    val result: ExcelImportResult[DownloadTemplateVO] = ExcelImportUtil.importExcelMore(multipartFile.getInputStream,
      classOf[DownloadTemplateVO], importParams)

    //验证通过List
    val successList: util.List[DownloadTemplateVO] = result.getList
    val saveList=new CopyOnWriteArrayList[Card]()
    //创建线程池
    val executor=Executors.newFixedThreadPool(1000)
    //记录成功、失败数
    val successTotal=new java.util.concurrent.atomic.AtomicInteger()
    val failTotal=new java.util.concurrent.atomic.AtomicInteger()
    successList.forEach(
      downloadTemplateVo => {
       val thread=new Thread(){
        override def run(): Unit ={
            val hdqryCard = hdqryService.getEntityByCardNo(downloadTemplateVo.cardNo)
          //查询数据库不存在 失败数自增
            if(hdqryCard==null){
              failTotal.incrementAndGet()
            }
            val card: Card = new Card
            copyHdqryCardEntityToCardEntity(hdqryCard, card)
            card.setCardNo(downloadTemplateVo.cardNo)
            card.setOperationUserId(getOptId.toString)
            card.setEnterTime(entityVO.enterTime)
            val createTime = new Date()
            card.setCreateTime(createTime)
            card.setIsTimeout("0")
            card.setIsInform("0")
            card.setCardTreatmentState("0")

            saveList.add(card)
          //成功数自增
          successTotal.incrementAndGet()

            val successCardVO = new SuccessCardVO
            BeanUtils.copyProperties(downloadTemplateVo, successCardVO)
            successCardVO.setCreateTime(createTime)
            successCardVO.setResult("上传成功")
            list.add(successCardVO)
          }
        }
        executor.submit(thread)
      })
    //关闭线程池
    executor.shutdown()
    //异步批量插入
    service.saveList(saveList)

    //验证失败List
    val failList: util.List[DownloadTemplateVO] = result.getFailList
    failList.forEach(
      downloadTemplateVo => {
        val s = new SuccessCardVO
        BeanUtils.copyProperties(downloadTemplateVo, s)

        val failWorkbook = result.getFailWorkbook
        val sheet = failWorkbook.getSheetAt(0)
        //遍历获取失败原因
        for (a <- 1 until sheet.getPhysicalNumberOfRows) {
          val row = sheet.getRow(a)
          val cell = row.getCell(row.getLastCellNum - 1)
          if (cell != null && cell != "") {
            cell.setCellType(CellType.STRING)
            val value = cell.getStringCellValue()
            s.setResult(value)
          }
        }
        list.add(s)
      }
    )


    val re = new util.HashMap[String, Object]()
    re.put("result", list)
    re.put("total", list.size().toString)
    re.put("successTotal",successList.size().toString)
    re.put("failTotal",failList.size().toString)
    write(response, BaseResponse.success(re))

  }


}

  

//异步批量插入   启动类添加注解 @EnableAsync

  @Async
  def saveList(list: List[Card]): BaseResponse[Any] = {
    //在这里将数组拆分成多个数组插入 代码省略
    mapper.insertList(list)
    BaseResponse.success()
  }

  

原文地址:https://www.cnblogs.com/castlechen/p/13518544.html