excel批量数据导入时用poi将数据转化成指定实体工具类

1.实现目标

excel进行批量数据导入时,将批量数据转化成指定的实体集合用于数据操作,实现思路:使用注解将属性与表格中的标题进行同名绑定来赋值。

2.代码实现

2.1 目录截图如下

在这里插入图片描述

2.2 代码实现
package poi.constants;/*** @description: 用来定义一些通用的常量数据* @author: zengwenbo* @date: 2024/3/10 13:08*/
public class Constant {public final static String POINT = ".";public final static String SPACE = " ";
}
package poi.exception;/*** @description: 用于解析excel报错提供的异常* @author: zengwenbo* @date: 2024/3/10 12:47*/
public class ExcelException extends RuntimeException {public ExcelException() {super();}public ExcelException(String message) {super(message);}public ExcelException(String message, Throwable cause) {super(message, cause);}
}
package poi.annotation;import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.springframework.util.StringUtils;import java.lang.annotation.*;
import java.util.List;import static poi.constants.Constant.SPACE;/*** @description: 用于绑定excel和实体字段属性的注解* @author: zengwenbo* @date: 2024/3/10 12:51*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelDescription {String desc(); // excel描述的标题的内容DataType type() default DataType.String; // 指定当前title数据的数据类型enum DataType {String {@Overridepublic Object evaluateDataByType(Cell dataCell, List<List<java.lang.String>> validateList) {String cellValue = dataCell.getStringCellValue();if (StringUtils.hasLength(cellValue) && StringUtils.hasLength(cellValue.trim())) {// 判断当前值是否在序列中,在的话用SPACE进行划分取前面的值,不在的话原值返回return validateList.stream().filter(item -> item.contains(cellValue)).map(item -> cellValue.split(SPACE)[0]).findFirst().orElse(cellValue);}return cellValue;}}, Date {@Overridepublic Object evaluateDataByType(Cell dataCell, List<List<java.lang.String>> validateList) {double cellValue = dataCell.getNumericCellValue();return cellValue != 0 ? DateUtil.getJavaDate(cellValue) : null;}}, BigDecimal {@Overridepublic Object evaluateDataByType(Cell dataCell, List<List<java.lang.String>> validateList) {return java.math.BigDecimal.valueOf(dataCell.getNumericCellValue());}};/*** 根据数据类型来获取excel的数据值** @param dataCell excel单元格对象* @param validateList excel当前的序列数据有效性* @return*/public abstract Object evaluateDataByType(Cell dataCell, List<List<String>> validateList);}
}
package poi.utils;import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.util.StringUtil;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import poi.annotation.ExcelDescription;
import poi.exception.ExcelException;import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.nio.file.OpenOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;import static poi.constants.Constant.POINT;/*** @description: 提供解析excel的工具类* @author: zengwenbo* @date: 2024/3/10 12:42*/
public class ExcelUtil {private final static String EXCEL_XLS = "xls";private final static String EXCEL_XLSX = "xlsx";/*** 将excel中table里面的数据转换成对应的实体** @param clazz       需要转化实体的类对象* @param titleRowNum table表格的标题行* @param sheetIndex  sheet的下标* @return 返回转换后对象的list集合*/public static <T> List<T> transTableToEntity(MultipartFile file, Class<T> clazz, int titleRowNum, int sheetIndex) {// 1.获取文件的后缀String filename = file.getOriginalFilename();String suffix = filename.substring(filename.lastIndexOf(POINT) + 1);// 2.根据获取的后缀名获取操作excel的对象Workbook workbook;try (InputStream inputStream = file.getInputStream()) {switch (suffix) {case EXCEL_XLS:workbook = new HSSFWorkbook(inputStream);break;case EXCEL_XLSX:workbook = new XSSFWorkbook(inputStream);break;default:throw new ExcelException("后缀名不符");}} catch (IOException e) {throw new ExcelException("文件解析失败", e);}// 3.获取要操作的sheetSheet sheet = workbook.getSheetAt(sheetIndex);// 4.通过表格标题获取操作的开始列和结束列Row titleRow = sheet.getRow(titleRowNum);short firstCellNum = titleRow.getFirstCellNum();short lastCellNum = titleRow.getLastCellNum();// 5.获取表格中序列的数据有效性List<List<String>> validateList = new ArrayList<>();if (null != sheet.getDataValidations()) {sheet.getDataValidations().forEach(item -> {// 筛选出有效性数据时序列的进行添加if (null != item.getValidationConstraint().getExplicitListValues()) {validateList.add(Arrays.asList(item.getValidationConstraint().getExplicitListValues()));}});}// 6.遍历数据进行解析ArrayList<T> list = new ArrayList<>();Field[] fields = clazz.getDeclaredFields();for (int i = titleRowNum + 1; i < sheet.getLastRowNum(); i++) {Row row = sheet.getRow(i);try {// 获取实例对每个绑定的属性进行赋值T t = clazz.newInstance();for (int j = firstCellNum; j < lastCellNum; j++) {Cell titleCell = titleRow.getCell(j);if (null == titleCell) {throw new ExcelException("标题缺失,请检查导入模板是否正常");}String title = titleCell.getStringCellValue();if (!StringUtils.hasLength(title)) {throw new ExcelException("标题内容为空,请检查导入模板是否正常");}Optional.ofNullable(row.getCell(j)).ifPresent(value -> evaluateField(t, fields, value, title, validateList));}list.add(t);} catch (InstantiationException e) {throw new ExcelException("创建实例异常,该类缺失无参构造方法");} catch (IllegalAccessException e) {throw new ExcelException("创建实例异常,权限不足");}}return list;}/*** 通过单元格的值给对象的属性进行赋值** @param t 对象实体* @param fields 对象对应的属性数组* @param dataCell 单元格对象* @param title 单元格对象对应的title* @param validateList 数据有效性列表*/private static <T> void evaluateField(T t, Field[] fields, Cell dataCell,String title, List<List<String>> validateList) {for (Field field : fields) {// 处理属性上有ExcelDescription注解的数据进行赋值if (field.isAnnotationPresent(ExcelDescription.class)) {ExcelDescription annotation = field.getAnnotation(ExcelDescription.class);// 获取注解的描述String desc = annotation.desc();// 获取注解的数据类型ExcelDescription.DataType type = annotation.type();// 如果title和描述desc一致,则将cell里面的值赋值给该属性if (title.equals(desc)) {// 获取value的值Object value = type.evaluateDataByType(dataCell, validateList);field.setAccessible(true);try {field.set(t, value);} catch (IllegalAccessException e) {throw new ExcelException("对象属性赋值权限异常");}}}}}
}

3.测试数据

测试实体

package poi.bean;import lombok.Data;
import poi.annotation.ExcelDescription;import java.math.BigDecimal;
import java.util.Date;/*** @description:* @author: zengwenbo* @date: 2024/3/10 14:07*/
@Data
public class Person {@ExcelDescription(desc = "名称")private String name;@ExcelDescription(desc = "年龄", type = ExcelDescription.DataType.BigDecimal)private BigDecimal age;@ExcelDescription(desc = "生日", type = ExcelDescription.DataType.Date)private Date birth;@ExcelDescription(desc = "国籍")private String country;
}

测试的excel文件数据截图
在这里插入图片描述
对国籍数据进行了有效性填充
在这里插入图片描述
测试代码:将excel文件放在resource目录下

package com.example.demo;import com.example.demo.redis.User;import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.ResourceUtils;
import org.springframework.web.multipart.MultipartFile;
import poi.bean.Person;
import poi.utils.ExcelUtil;import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.file.Files;
import java.util.List;@RunWith(SpringRunner.class)
@SpringBootTest
class DemoApplicationTests {@Autowiredprivate ResourceLoader resourceLoader;@Testvoid TestExcel() throws Exception {Resource resource = resourceLoader.getResource("classpath:test.xls" );String fileName = resource.getFilename();byte[] fileBytes = Files.readAllBytes(resource.getFile().toPath());MultipartFile multipartFile = new MockMultipartFile(fileName, fileName, "text/plain", fileBytes);List<Person> list = ExcelUtil.transTableToEntity(multipartFile, Person.class, 0, 0);}}

最终结果
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/744180.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【消息队列开发】 实现消息持久化

文章目录 &#x1f343;前言&#x1f340;消息存储格式设计&#x1f6a9;queue_data文件设计&#x1f6a9;queue_stat文件设计&#x1f6a9;拓展 &#x1f384;实现统计文件&#xff08;queue_stat&#xff09;的读写⭕总结 &#x1f343;前言 本次开发目标&#xff0c;实现消…

2024阿里云域名优惠口令大全(3月更新)

2024年阿里云域名优惠口令&#xff0c;com域名续费优惠口令“com批量注册更享优惠”&#xff0c;cn域名续费优惠口令“cn注册多个价格更优”&#xff0c;cn域名注册优惠口令“互联网上的中国标识”&#xff0c;阿里云优惠口令是域名专属的优惠码&#xff0c;可用于域名注册、续…

C# MES通信从入门到精通(1)——串口传输文件

前言: 在上位机软件开发领域,有一些工厂的mes系统需要我们通过串口发送文件的方式把一些图片或者检测数据csv文件等发送给服务器,这种方式是一些比较旧的工厂采用的方式,但是这种方式也是存在的,本文就是讲解如何使用串口发送文件详情见下文。 1、串口发送文件思路 将需…

【刷题节】美团2024年春招第一场笔试【技术】

1.小美的平衡矩阵 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner scanner new Scanner(System.in);int n scanner.nextInt();int[][] nums new int[n][n], sum new int[n][n];char[] chars;for (int i 0; i < n; i) {…

介绍Oracle的SQL调化健康检查脚本(SQLHC)

概述 Oracle提供了一个SQL调优健康检查脚本&#xff08;SQLHC&#xff09;&#xff0c;用于检查需要优化的SQL的运行环境&#xff0c;生成报告以便帮助DBA找到SQL性能不佳的原因。SQLHC是SQLT的一个子集&#xff08;我后续的文章会介绍SQLT&#xff09;&#xff0c;但SQLHC与S…

迁移学习怎么用

如果想实现一个计算机视觉应用&#xff0c;而不想从零开始训练权重&#xff0c;比方从随机初始化开始训练&#xff0c;更快的方式是下载已经训练好权重的网络结构&#xff0c;把这个作为预训练&#xff0c;迁移到你感兴趣的新任务上。ImageNet、PASCAL等等数据库已经公开在线。…

vue form表单验证

第一种&#xff1a;非必填不能为空&#xff0c;并且需要正则验证 第二种&#xff1a;必填&#xff0c;正则验证&#xff0c;不能只输入空格验证 第三种&#xff1a;必填&#xff0c;正则验证&#xff0c;不能输入空格(v-model.trim) 第四种&#xff1a;选了多选框后必填 第一种…

【笔记】学习Android.mk(三)

一、在Android.mk文件中引入系统动态库 要在 Android.mk 文件中引入系统动态库&#xff0c;你可以使用 LOCAL_SHARED_LIBRARIES 变量来指定系统动态库。以下是一个示例&#xff1a; LOCAL_PATH : $(call my-dir)include $(CLEAR_VARS)# 定义模块名称和源文件 LOCAL_MODULE : …

C#,数值计算,希尔伯特矩阵(Hilbert Matrix)的算法与源代码

Hilbert, David (1862-1943) 1 希尔伯特(Hilbert) 德国数学家,在《几何学基础》中提出了第一套严格的几何公理(1899年)。他还证明了自己的系统是自洽的。他发明了一条简单的空间填充曲线,即埃里克魏斯汀的数学世界,即希尔伯特曲线,埃里克魏斯汀的数学世界,并证明了不…

ms office学习记录12:Excel学习记录㈥

数据工具 分列的其他运用&#xff1a;身份证号中“出生日期”切片&#xff1a;分列→固定宽度→下一步→切割出三列→下一步→不导入第一列→导入第二列且转换成日期→不导入第三列→完成 删除重复值&#xff1a;定位到要“数据”选项卡→删除重复项→取消全选再勾选要删除的…

C/C++程序设计实验报告2 | 循环结构实验

本文整理自博主学校大一&#xff08;2021级&#xff09;C/C专业课的课程实验报告&#xff0c;适合学弟妹或C语言初学者入门C语言学习、练习。 编译器&#xff1a;gcc 10.3.0 ---- 注&#xff1a; 1.虽然课程名为C程序设计&#xff0c;但实际上当时校内该课的内容大部分其实都是…

ElasticSearch学习篇10_Lucene数据存储之BKD动态磁盘树

前言 基础的数据结构如二叉树衍生的的平衡二叉搜索树通过左旋右旋调整树的平衡维护数据&#xff0c;靠着二分算法能满足一维度数据的logN时间复杂度的近似搜索。对于大规模多维度数据近似搜索&#xff0c;Lucene采用一种BKD结构&#xff0c;该结构能很好的空间利用率和性能。 …

查找jdk的安装

方式1&#xff1a;which或者where java which java -- linux where java --windows 方式2: echo 使用echo 打印配置的java home环境变量 echo $JAVA_HOME$ --linux echo %JAVA_HOME% --windows 方式3&#xff1a;使用ls -lrt -a &#xff1a;显示所有文件即目录…

沃通SSL证书证券行业应用案例

金融证券行业作为现代经济体系中的重要组成部分&#xff0c;其安全性直接关系到国家经济的稳定和广大投资者的利益。沃通SSL证书基于密码技术保护传输数据的机密性、完整性&#xff0c;通过权威身份认证确保服务器身份真实性&#xff0c;已持续为众多知名证券行业客户提供服务&…

微信小程序之vue按钮切换内容变化

效果图如下&#xff1b; 上代码 <template><view class"content"><view class"searchDiv"><view class"paytab"><view class"buttab" v-for"(t,index) in tabList" :key"index" clic…

Linux服务器,使用ssh登录时越来越慢,有时甚至出现超时的现象,解决方案

一台Linux服务器&#xff0c;使用ssh登录时越来越慢&#xff0c;有时甚至出现超时的现象。一直以为这不算问题&#xff0c;但是有时候登录时间长的让人无法接受&#xff0c;查了一下&#xff0c;这“ssh登录慢”还真的是个问题&#xff0c;解决方案如下&#xff1a; 客户端进行…

Python小设计

1. 五个PPT上的界面打印【print、input函数】 &#xff08;1&#xff09;英雄商城登陆界面 print(英雄联盟商城登录界面 ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~1. 用户登录2. 新用户注册3. 退出系统 ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~ * ~…

Android UI: 自定义控件:可换行的布局控件

文章目录 继承ViewGroup重写onMeasure方法&#xff1a;计算并设置布局控件的高度重写onLayout方法&#xff1a;计算并设置每个子控件的位置具体的代码实现小结 继承ViewGroup 重写generateLayoutParams&#xff0c;设置子控件的LayoutParams为MarginLayoutParams类型 Overridep…

【Numpy】基础学习:一文了解np.expand_dims的作用、用法

【Numpy】基础学习&#xff1a;一文了解np.expand_dims的作用、用法 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448; 希望…

c/c++的内存分配,详细说一下栈、堆和静态存储区

栈区&#xff08;Stack&#xff09;&#xff1a;由编译器自动分配和回收&#xff0c;栈中存放函数调用的相关信息&#xff0c;栈帧&#xff08;记录函数的栈帧开始的位置&#xff09;&#xff0c;参数&#xff0c;局部变量&#xff0c;返回地址。其操作方法类似于数据结构中的栈…