当前位置: 首页 > news >正文

EasyExcel 批量导入并校验数据

文章目录

  • 前言
  • 一、pom
  • 二、使用步骤
    • 1.导入对象
    • 2.读入数据并保存

前言

EasyExcel 批量导入并校验数据

一、pom

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

二、使用步骤

1.导入对象

日期形式的字段因为校验需要,提供了String类型的字段,再转换赋值给真正的数据库字段对象,如果不考虑校验问题可直接转换@ExcelProperty(value = "处罚信息公示日期", index = 5, converter = LocalDateConverter.class)

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.validator.constraints.Length;import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import java.io.Serializable;
import java.time.LocalDate;/*** 信用信息修复** @author huaiyu.zhang* @since 2023-04-19 10:59:49*/
@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel("信用信息修复")
public class CreditInfoExcelInReq implements Serializable {private static final long serialVersionUID = 370622351109421619L;@ApiModelProperty("${column.comment}")@ExcelIgnoreprivate String id;@ApiModelProperty("企业名称")@Length(max = 30, message = "企业名称:最多可输入30个字")@NotBlank(message = "企业名称不能为空")@ExcelProperty(index = 0, value = "企业名称")private String companyName;@ApiModelProperty("统一社会信用代码")@Length(min = 18, max = 18, message = "统一社会信用代码必须18位")@NotBlank(message = "统一社会信用代码不能为空")@ExcelProperty(index = 1, value = "统一社会信用代码")private String creditCode;@ExcelProperty(index = 2, value = "行政区划--地市")@NotBlank(message = "行政区划--地市不能为空")private String districtCodeCity;@ExcelProperty(index = 3, value = "行政区划--区/县")private String districtCodeCountry;@ApiModelProperty("行政区划")@NotBlank(message = "行政区划不能为空")@ExcelIgnoreprivate String districtCode;@NotBlank(message = "失信行为类别不能为空")@ExcelProperty(index = 4, value = "失信行为类别")private String typeCodeName;@ApiModelProperty("失信行为类别 0一般失信行为 1-严重失信行为")@NotBlank(message = "失信行为类别不能为空")@ExcelIgnoreprivate String typeCode;@ExcelProperty(index = 5, value = "处罚信息公示日期")@Pattern(regexp = "[0-9]{4}-[0-9]{2}-[0-9]{2}", message = "处罚信息公示日期格式必须为yyyy-MM-dd")private String punishTimeOri;@ApiModelProperty("处罚信息公示日期")@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")@ExcelIgnoreprivate LocalDate punishTime;@ApiModelProperty("信用修复部门")@Length(max = 20, message = "信用修复部门:最多可输入20个字")@ExcelIgnoreprivate String repairDepartment;@ApiModelProperty("信用修复完成日期")@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")@ExcelIgnoreprivate LocalDate repairTime;@ApiModelProperty("状态 0-未修复 1-已修复")@ExcelIgnoreprivate String status;@ApiModelProperty("备注")@Length(max = 300, message = "备注:最多300字")@ExcelIgnoreprivate String mark;}

2.读入数据并保存

读取数据后Validation校验,校验通过直接保存(如果数据已经存在,则copy excel外其他字段后删除原数据,导入新数据),校验失败则返回失败行数(这里也可以导出校验失败详情)

默认规则:设置excel最大导入数据行数为LIST_COUNT = 1000;
如果需要导入更多数据,改大这个值即可,也可invoke时分批读取数据
但是每次执行完invoke后都会执行doAfterAllAnalysed下的saveData,那么校验逻辑将只针对本批次数据进行校验,如校验失败,会直接返回给前端。后续批次由于异常被抛出不会执行(可更改校验逻辑,或错误信息返回形式)

Listener 端代码:

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.gsafety.bg.sv.model.dto.req.CreditInfoExcelInReq;
import com.gsafety.bg.sv.model.dto.req.CreditInfoReq;
import com.gsafety.bg.sv.model.dto.resp.BasDistrictResp;
import com.gsafety.bg.sv.model.po.CreditInfoPO;
import com.gsafety.bg.sv.service.CreditInfoService;
import com.gsafety.bg.sv.service.constant.CreditTypeEnum;
import com.gsafety.bg.sv.service.utils.MappingConvertUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.BeanUtils;import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import java.time.LocalDate;
import java.util.*;/*** @author huaiyu.zhang* @since 2023-6-5 0005 17:10*/@Slf4j
public class CreditInfoExcelDataListener extends AnalysisEventListener<CreditInfoExcelInReq> {private final Integer LIST_COUNT = 1000;List<CreditInfoExcelInReq> list = new ArrayList<>(LIST_COUNT);// 由于监听器只能通过new的方式创建,所以可以通过构造器传入dao层对象private final CreditInfoService service;public CreditInfoExcelDataListener(CreditInfoService service) {this.service = service;}@Overridepublic void invoke(CreditInfoExcelInReq req, AnalysisContext analysisContext) {//每读取一行数据都会调用一次list.add(req);if (list.size() >= LIST_COUNT) {throw new ExcelAnalysisException("当前excel数据量不得大于" + LIST_COUNT + "条!");}}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {//所有数据解析完毕执行该方法// 防止导入空的Excelif (analysisContext.readRowHolder().getRowIndex() <= 0) {throw new ExcelAnalysisException("当前excel无数据!");}saveData();}protected void saveData() {Set<String> errorRow = new HashSet<>();List<BasDistrictResp> districtList = MappingConvertUtil.getDistrictList();list.forEach(l -> {Integer row = list.indexOf(l) + 2;if (ObjectUtils.isEmpty(l.getDistrictCodeCountry())) {Optional<BasDistrictResp> opt = districtList.stream().filter(d -> l.getDistrictCodeCity().equals(d.getDistName())).findFirst();if (!opt.isPresent()) {errorRow.add(row.toString());} else {l.setDistrictCode(opt.get().getDistCode());}} else {String parentCode = districtList.stream().filter(d -> l.getDistrictCodeCity().equals(d.getDistName())).findFirst().orElse(BasDistrictResp.builder().distCode("").build()).getDistCode();Optional<BasDistrictResp> opt = districtList.stream().filter(d -> parentCode.equals(d.getParentCode()) && l.getDistrictCodeCountry().equals(d.getDistName())).findFirst();if (!opt.isPresent()) {errorRow.add(row.toString());} else {l.setDistrictCode(opt.get().getDistCode());}}l.setTypeCode(CreditTypeEnum.getCode(l.getTypeCodeName()));Set<ConstraintViolation<Object>> validate = Validation.buildDefaultValidatorFactory().getValidator().validate(l);//用于存储验证后的错误信息if (validate.size() > 0) {errorRow.add(row.toString());//防止相同数据indexof定位错误l.setId(UUID.randomUUID().toString());} else {//日期格式校验成功后再转换punishTimeOri,否则直接报错l.setPunishTime(LocalDate.parse(l.getPunishTimeOri()));CreditInfoReq req = new CreditInfoReq();//构造新数据覆盖旧数据BeanUtils.copyProperties(l, req);Optional<CreditInfoPO> opt = service.loadByCreditCode(l.getCreditCode());if (opt.isPresent()) {req.setRepairTime(opt.get().getRepairTime());req.setStatus(opt.get().getStatus());req.setMark(opt.get().getMark());req.setRepairDepartment(opt.get().getRepairDepartment());//删除旧数据service.delete(opt.get().getId());}l.setId(service.add(req));}if (row - 1 == list.size() && errorRow.size() > 0) {throw new ExcelAnalysisException("部分导入成功,其中第" + String.join(",", errorRow) + "行导入失败!", null);}});}
}

service 端代码:

    public String importData(MultipartFile file) {CreditInfoExcelDataListener listener = new CreditInfoExcelDataListener(this);InputStream inputStream;try {inputStream = file.getInputStream();EasyExcel.read(inputStream, CreditInfoExcelInReq.class,listener).sheet().doRead();return "全部导入成功!";} catch (IOException e) {throw new BusinessCheckException("Excel 文件流读取失败");} catch (ExcelAnalysisException e) {return e.getMessage();} catch (Exception e) {throw new BusinessException("数据导入失败", e);}}

在这里插入图片描述

在这里插入图片描述

相关文章:

EasyExcel 批量导入并校验数据

文章目录 前言一、pom二、使用步骤1.导入对象2.读入数据并保存 前言 EasyExcel 批量导入并校验数据 一、pom <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.7</version></depend…...

亚马逊、Allegro卖家建立属于自己的测评系统,实现批量优质账号养成

卖家搭建一套完整的测评系统&#xff0c;卖家自己能够养出批量优质账号&#xff0c;并完全掌控真实买家的浏览、加购、下单和评价等风控数据规律。我们的系统能够自主加速推广&#xff0c;防御反击&#xff0c;同时节省运营成本&#xff0c;实现高效的测评运营。 我们的系统支…...

springboot的目录结构作用

springboot单体项目结构大概如下。 代码都在src/main下&#xff0c; java是后端代码 java下最基本的包 dao(mapper) entity(model) service controller 其他的包根据项目需求扩展。 resources下是配置文件。 如果不是前后端分离&#xff0c;resources下放的是静态文件…...

微信小程序基础使用-请求数据并渲染

小程序基本使用-请求数据并渲染 小程序模板语法-数据绑定 在js中定义数据 Page({data: {isOpen: true,message: hello world!} })小程序的data是一个对象&#xff0c;不同于vue的data是一个函数 在模块中获取使用数据 小程序中使用 {{}} 实现数据与模板的绑定 内容绑定&a…...

代码随想录训练营Day55| 392.判断子序列 ;115.不同的子序列

392.判断子序列 class Solution {public boolean isSubsequence(String s, String t) {int m s.length();int n t.length();if(m>n) return false;int[][] dp new int[m1][n1];dp[0][0]0;for(int i1;i<m;i){for(int j1;j<n;j){if(s.charAt(i-1)t.charAt(j-1)){dp[i…...

网络作业9【计算机网络】

网络作业9【计算机网络】 前言推荐网络作业9一. 单选题&#xff08;共12题&#xff0c;36分&#xff09;二. 多选题&#xff08;共1题&#xff0c;3分&#xff09;三. 填空题&#xff08;共2题&#xff0c;10分&#xff09;四. 阅读理解&#xff08;共1题&#xff0c;17分&…...

C++ QT 上传图片至mysql数据库

以下是一个简单的C QT上传图片至MySQL数据库的代码示例&#xff1a; #include <QtSql> #include <QFile> #include <QByteArray> int main() { //连接数据库 QSqlDatabase db QSqlDatabase::addDatabase("QMYSQL"); …...

2023去水印小程序saas系统源码修复独立版v1.0.3+uniapp前端

&#x1f388; 限时活动领体验会员&#xff1a;可下载程序网创项目短视频素材 &#x1f388; &#x1f389; 有需要的朋友记得关赞评&#xff0c;阅读文章底部来交流&#xff01;&#xff01;&#xff01; &#x1f389; ✨ 源码介绍 一个基于uniapp写的小程序&#xff0c;后端…...

【ChatGPT】数据科学 ChatGPT Cheat Sheet 书籍分享(阿里云盘下载)

封皮 以下为书中部分内容的机器翻译 我们的重要提示指南 1. 以 AI 角色的描述开始提示。 例如&#xff0c;“你是{x}”或“我希望你扮演{x}”。如果您不确定&#xff0c;请尝试“你是一个有帮助的助手”。 例如&#xff0c;您是 OpenAI 的数据科学家&#xff0c;您正在研究大型…...

使用 Docker-compose 搭建lnmp

服务编排&#xff1a; 应用编排&#xff1a; 单机环境下&#xff1a;shell/python脚本多机/集群环境下&#xff1a;ansible、saltstack、pubbet docker容器编排&#xff1a; 单机&#xff1a;docker-compose多机/集群&#xff1a;docker swarm&#xff0c;mesos marathon&a…...

chatgpt赋能python:Python中的矩阵合并方法:介绍和使用方法

Python中的矩阵合并方法: 介绍和使用方法 矩阵合并是Python编程中常用的操作之一&#xff0c;特别是针对数据分析、机器学习和深度学习等领域。Python提供了多种方法来合并矩阵&#xff0c;本文将介绍这些方法并分享如何在实际应用中使用它们。 普通矩阵合并 最基础的矩阵合…...

Java动态代理:优化静态代理模式的灵活解决方案

文章目录 代理模式定义具体实现分析优缺点 优化使用动态代理解决优化相关知识动态代理种类场景应用 代理模式 定义 代理模式&#xff0c;为其他对象提供一种代理以控制对这个对象的访问 具体实现 代理模式的具体实现描述可以分为以下几个步骤&#xff1a; 创建抽象对象接…...

四种Bootloader程序安全机制设计

正文 大家周末好&#xff0c;我是bug菌~ 不管是玩单片机还是嵌入式linux&#xff0c;基本上都会接触到bootloader&#xff0c;所以bootloader程序也是一个关键的组件&#xff0c;进行硬件初始化&#xff0c;应用程序的合法性、完成性检测、升级功能等等都与其息息相关。 像一些…...

【DBA 警世录之习惯性命令---读书笔记】

&#x1f448;【上一篇】 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 【下一篇】&#x1f449; &#x1f53b;【&#x1f4a3; 话题引入&#xff1a;既然 DBA 这个职业如此危险&#xff0c;那么哪些习惯是 DBA 必须养成的呢&#x…...

Vue中如何进行状态持久化(LocalStorage、SessionStorage)

Vue中如何进行状态持久化&#xff08;LocalStorage、SessionStorage&#xff09;&#xff1f; 在Vue应用中&#xff0c;通常需要将一些状态进行持久化&#xff0c;以便在用户关闭浏览器或刷新页面后&#xff0c;仍能保留之前的状态。常见的持久化方式包括LocalStorage和Sessio…...

【30天熟悉Go语言】6 Go 复杂数据类型之指针

文章目录 一、前言二、数据类型总览三、指针1、特殊运算符& *2、内存角度来看指针3、使用指针修改数据4、指针使用的注意事项5、对比着看Java的引用类型 三、总结 一、前言 Go系列文章&#xff1a; GO开篇&#xff1a;手握Java走进Golang的世界2 Go开发环境搭建、Hello Wor…...

Linux内核使用红黑树的场景

进程调度队列 (Process Scheduling)&#xff1a;内核需要对进程按照一定的调度策略进行排队&#xff0c;以便更好地利用 CPU 的时间片。因此&#xff0c;内核使用红黑树作为查找和管理进程调度队列的数据结构&#xff0c;以支持快速的查找、插入和删除操作。 文件系统 (File S…...

遗留的 AppSec 工具迷失在云端

随着应用程序开发步伐的加快&#xff0c;IT 和安全团队正在对旧的应用程序安全(AppSec) 工具失去信心。 根据 Backslash 对 300 名 CISO、AppSec 经理和工程师的调查&#xff0c;遗留工具无法跟上并陷入永远的追赶游戏。 影响是深远的&#xff0c;大多数组织都看到云原生 App…...

直流稳压电源与信号产生电路(模电速成)

目录 一、直流稳压电源 1、直流稳压电路 2、串联型稳压电路 3、集成稳压电路 二、信号产生电路 1、振荡电路 2、波形发生器 一、直流稳压电源 1、直流稳压电路 直流电源由 变压器、整流、滤波、稳压 四部分组成 整流&#xff1a;将交流变为直流 滤波&#xff1a;减小…...

0202性能分析-索引-MySQL

1 索引语法 创建索引 CREATE [UNIQUE|FULLTEXT] INDEX index_name ON table_name(index_column_name,...);Index_name&#xff1a;规范为idx_表名_字段名... 查看索引 SHOW INDEX FROM table_name;删除索引 DROP INDEX index_name ON table_name;按照下列要求&#xff0c;创建…...

Python|GIF 解析与构建(5):手搓截屏和帧率控制

目录 Python&#xff5c;GIF 解析与构建&#xff08;5&#xff09;&#xff1a;手搓截屏和帧率控制 一、引言 二、技术实现&#xff1a;手搓截屏模块 2.1 核心原理 2.2 代码解析&#xff1a;ScreenshotData类 2.2.1 截图函数&#xff1a;capture_screen 三、技术实现&…...

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

Spring AI与Spring Modulith核心技术解析

Spring AI核心架构解析 Spring AI&#xff08;https://spring.io/projects/spring-ai&#xff09;作为Spring生态中的AI集成框架&#xff0c;其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似&#xff0c;但特别为多语…...

docker 部署发现spring.profiles.active 问题

报错&#xff1a; org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...