使用poi加自定义注解使用反射机制实现把excel中的row转换成entity。
支持外键查询找
本人公司内部使用jpa框架,所以代码中从SpringContextHolder对象中获取的注解标识的JpaRepository对象,其他持久层框架也可以使用,需要修改查询接口。
这部分为解析excel 对单元格数据格式进行判断


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import xxx.ExcelCheck;
import xxx.BaseEntityStatus;
import xxx.FieldReflectUtil;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;/*** @author yaku*/
public class ExcelUtil {private final static String XLS = ".xls";private final static String XLSX = ".xlsx";/*** 读入excel文件,解析内容后返回结果** @param file    excel文件* @param handler 解析excel的*/public static ParseExcelResult readExcel(MultipartFile file, Class<?> clazz, ExcelRowsHandler<Object> handler) {ParseExcelResult parseExcelResult = new ParseExcelResult();parseExcelResult.setErrorRowList(new ArrayList<>());// 获得Workbook工作薄对象Workbook workbook;try {workbook = getWorkBook(Objects.requireNonNull(file.getOriginalFilename()), Objects.requireNonNull(file.getInputStream()));// 创建返回对象,把每行中的值作为一个数组,所有行作为一个集合返回if (workbook.getNumberOfSheets() < 1 || workbook.getSheetAt(0).getLastRowNum() == 0) {throw new RuntimeException("无法解析excel数据!");}} catch (IOException e) {throw new RuntimeException("无法解析该Excel");}// 获得当前sheet工作表Sheet sheet = workbook.getSheetAt(0);// 获得当前sheet的开始行int firstRowNum = sheet.getFirstRowNum();// 获取头行数据Row headRow = sheet.getRow(0);// 获得当前sheet的结束行int lastRowNum = sheet.getLastRowNum();// 循环除了第一行的所有行for (int rowNum = firstRowNum + 1; rowNum < lastRowNum; rowNum++) {// 获得当前行Row row = sheet.getRow(rowNum);ErrorRow errorRow;Object o;try {errorRow = check(headRow, row, clazz);} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {throw new RuntimeException("无法解析该Excel");}if (null != errorRow) {parseExcelResult.getErrorRowList().add(errorRow);parseExcelResult.setProcessedRowNum(parseExcelResult.getProcessedRowNum() + 1);continue;} else {o = createEntity(headRow, row, clazz);}// 改行执行sql 发生错误记录改行ErrorRow exceptionRow = handler.execute(rowNum, o);if (exceptionRow != null) {parseExcelResult.getErrorRowList().add(exceptionRow);parseExcelResult.setProcessedRowNum(parseExcelResult.getProcessedRowNum() + 1);}}return parseExcelResult;}/*** 根据当前行数据创建对象** @param clazz 对象字节码文件* @return 检测通过返回null 检测错误返回ErrorRow对象* @throws ClassNotFoundException 找不到class字节码文件* @throws InstantiationException 无法创建对象 没有默认构造* @throws IllegalAccessException 该对象没有权限访问*/private static ErrorRow check(Row headRow, Row currentRow, Class<?> clazz) throws ClassNotFoundException, InstantiationException, IllegalAccessException {Field[] fields = clazz.getDeclaredFields();boolean[] flag = new boolean[headRow.getLastCellNum()];for (Field field : fields) {ExcelCheck annotation = field.getAnnotation(ExcelCheck.class);for (int i = 0; i < currentRow.getLastCellNum(); i++) {if (flag[i]) {continue;}if (annotation.cellName().equals(headRow.getCell(i).getStringCellValue())) {flag[i] = true;// 判断为空单元格不能为空if (annotation.nullAble()) {continue;}if (currentRow.getCell(i).getCellTypeEnum().equals(CellType.BLANK)) {return new ErrorRow(i, currentRow.getRowNum(), annotation.cellName() + "不能为空!");}// 单元格类型校验if (!typeCheck(annotation.type(), currentRow.getCell(i))) {return new ErrorRow(i, currentRow.getRowNum(), annotation.cellName() + "数据格式错误!");}}}}return null;}/*** excel每行数据校验** @param type entity对象表明的类型* @param cell 单元格对象* @return 正确 true 错误 false*/private static boolean typeCheck(ExcelCheck.Type type, Cell cell) {switch (type) {case NUMBER: {if (cell.getCellTypeEnum().equals(CellType.NUMERIC)) {try {MathUtils.objectConvertBigDecimal(cell.getStringCellValue());} catch (Exception e) {return false;}return true;}return false;}case STRING: {return cell.getCellTypeEnum().equals(CellType.STRING);}case DATETIME: {return cell.getCellTypeEnum().equals(CellType.NUMERIC);}case LINK: {if (cell.getCellTypeEnum().equals(CellType.STRING)) {return StringUtils.isNotBlank(cell.getStringCellValue());}return false;}default:return false;}}/*** 根据注解 对象信息 来转换成对应值** @param excelCheck 注解对象* @param cell       单元格对象* @return 对应类型*/private static Object valueFormat(ExcelCheck excelCheck, Cell cell) {switch (excelCheck.type()) {case NUMBER: {return MathUtils.objectConvertBigDecimal(cell.getNumericCellValue());}case STRING: {return cell.getStringCellValue();}case DATETIME: {return new Timestamp(cell.getDateCellValue().getTime());}case LINK: {return linkQuery(excelCheck.repositoryClass(), excelCheck.methodName(), cell.getStringCellValue());}default:return null;}}private static String linkQuery(Class<? extends JpaRepository> clazz, String methodName, String value) {try {JpaRepository jpaRepository = SpringContextHolder.getBean(clazz);Method method = jpaRepository.getClass().getDeclaredMethod(methodName, String.class, Integer.class);Object invoke = method.invoke(jpaRepository, value, BaseEntityStatus.ACTIVE);if (null == invoke) {return null;}return (String) FieldReflectUtil.getFieldValue(invoke, "id");} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException | NoSuchFieldException e) {throw new RuntimeException("无法处理该外键值");}}private static Object createEntity(Row headRow, Row currentRow, Class<?> clazz) {try {boolean[] flag = new boolean[headRow.getLastCellNum()];Object entity = clazz.newInstance();Field[] fields = entity.getClass().getDeclaredFields();for (int i = 0; i < headRow.getLastCellNum(); i++) {for (Field field : fields) {if (flag[i]) {continue;}if (headRow.getCell(i).getStringCellValue().equals(field.getAnnotation(ExcelCheck.class).cellName())) {flag[i] = true;Cell cell = currentRow.getCell(i);//如果当前单元格为空时,跳过该单元格if (null == cell || cell.getCellTypeEnum().equals(CellType.BLANK)) {continue;}if (!currentRow.getCell(i).getCellTypeEnum().equals(CellType.BLANK)) {FieldReflectUtil.setFieldValue(entity, field.getName(), valueFormat(field.getAnnotation(ExcelCheck.class), cell));}}}}return entity;} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | NoSuchFieldException e) {e.printStackTrace();}return null;}/*** 根据文件名判断excel版本 获取Workbook对象** @param fileName    文件名* @param inputStream 输入流* @return WorkBook对象* @throws IOException 文件名称错误抛出异常*/private static Workbook getWorkBook(String fileName, InputStream inputStream) throws IOException {// 创建Workbook工作薄对象// 根据文件后缀名不同(xls和xlsx)获得不同的Workbook实现类对象if (fileName.endsWith(XLS)) {// 2003return new HSSFWorkbook(inputStream);} else if (fileName.endsWith(XLSX)) {// 2007return new XSSFWorkbook(inputStream);} else {throw new IOException("文件名后缀错误");}}/*** 每读取一行,就调用execute方法*/public interface ExcelRowsHandler<T> {/*** 可以在此方法校验数据,校验通过后封装 T,再返回,读入完后,再批量insert/update** @param lineNum 行号* @param t       解析excel每行后封装好的对象* @return 自定义封装成 T,返回null则不记录到list*/ErrorRow execute(int lineNum, T t);}@Data@AllArgsConstructor@NoArgsConstructorpublic static class ErrorRow {private Integer column;private Integer row;private String cause;}@Datapublic static class ParseExcelResult {/*** 错误行列表*/private List<ErrorRow> errorRowList;/*** 处理行数*/private int processedRowNum;/*** 错误行数*/private int errorRowNum;/*** 空行*/private int emptyRowNum;/*** 总行数*/private int totalRowNum;}
}

创建excel 中 row 对应的 entity ,解析后返回entity
cellName,为单元格头行名称
nullAble,为单元格是否可空
type,表明单元格的数据类型
method,为持久层框架需要调用的方法名称
repositoryClass,为持久层框架的类名


import org.springframework.data.jpa.repository.JpaRepository;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** @author yaku*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelCheck {String cellName();boolean nullAble() default false;Type type() default Type.STRING;String methodName() default "";Class<? extends JpaRepository> repositoryClass() default JpaRepository.class;enum Type{/*** 数字*/NUMBER,/*** 日期时间*/DATETIME,/*** 字符串*/STRING,/*** 外键引用,需要配合持久层使用*/LINK}}

这部分为使用反射获取获取 get entity的值和 set entity的值


import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;public class FieldReflectUtil {public static final String GET_STR = "get";public static final String IS_STR = "is";public static final String SET_STR = "set";/*** 获得 t.fieldName 的值* @param t* 操作对象的实例* @param fieldName* 属性名* @param <T>* 操作对象的类型* @return* 获得到的值* @throws NoSuchFieldException* 找不到对应属性时会抛出该异常* @throws IllegalAccessException* 如果该工具类没有权限访问该类型对象的时候会抛出该异常* @throws NoSuchMethodException* 在没有权限访问,且没有getter的情况下会抛出该异常* @throws InvocationTargetException* 访问setter的时候如果setter抛出了异常则会抛出该异常**/public static <T> Object getFieldValue(T t, String fieldName) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {Class<?> tClass=t.getClass();Field declaredField = tClass.getDeclaredField(fieldName);int modifiers = declaredField.getModifiers();if (Modifier.isPublic(modifiers)) {return declaredField.get(t);}String name = declaredField.getName();String methodName;if (declaredField.getType().equals(boolean.class)) {methodName = IS_STR;} else {methodName = GET_STR;}methodName=buildMethodName(methodName,name);Method method = tClass.getMethod(methodName);return method.invoke(t);}/*** 设置 t.fieldName 的值* 操作对象的类型* @param t* 操作对象的实例* @param fieldName* 属性名* @param value* 设置的值* @param <T>* 操作对象的类型* @throws NoSuchFieldException* 找不到对应属性时会抛出该异常* @throws IllegalAccessException* 如果该工具类没有访问该类型对象的时候会抛出该异常* @throws NoSuchMethodException* 在没有权限访问,且没有setter的情况下会抛出该异常* @throws InvocationTargetException* 执行setter的时候如果setter抛出了异常则会抛出该异常*/public static <T> void setFieldValue(T t, String fieldName, Object value) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {Class<?> tClass=t.getClass();Field declaredField = tClass.getDeclaredField(fieldName);int modifiers = declaredField.getModifiers();//是公共的且可修改if (Modifier.isPublic(modifiers)&&!Modifier.isFinal(modifiers)) {declaredField.set(t, value);}String name = declaredField.getName();String methodName = SET_STR;methodName=buildMethodName(methodName,fieldName);Method method = tClass.getMethod(methodName, declaredField.getType());method.invoke(t, value);}}

最后这部分如何使用,使用lambda代理内部匿名类,获取到的lineNum为当前行号,o为当前成生成的实体,这里可以取出entity的值进行 持久化操作

    @Testpublic void checkExcelHandle() throws IOException {File file = new File("xxx");FileInputStream input = new FileInputStream(file);MultipartFile multipartFile = new MockMultipartFile("file", file.getName(), "text/plain", IOUtils.toByteArray(input));ExcelUtil.ParseExcelResult parseExcelResult = ExcelUtil.readExcel(multipartFile, MatchVehicleItem.class, (ExcelUtil.ExcelRowsHandler) (lineNum, o) -> {System.out.println(o);return null;});System.out.println(parseExcelResult);}

目前还没有整体写完,加上最后的异常处理,错误行记录生成错误行excel,并在错误cell中红色背景并提示错误原因

使用poi解析excel 返回对象相关推荐

  1. ssh excel 导入 mysql_ssh poi解析excel并将数据存入数据库

    做完了一个报表导入数据库 功能 遇到几个问题:一 .Poi解析excel 的两种格式 xls xlsx. 二.form表单上传使用ajax 三.excel 单元格中有公式存在普通的读取方式读到的是公式 ...

  2. POI解析Excel表格

    Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能. 这里实现poi解析Excel表格的例子,导入Exc ...

  3. 【poi-3.8】poi解析excel插入数据库详解

    文章目录 poi解析excel&插入数据库详解 说明 一.目的 二.准备工作 三.思路分析 四.流程图分析 五.核心代码 1:上传文件 2:获取输入流 3:初始化excel表 4:遍历行和列 ...

  4. poi解析excel读取日期为数字的问题

    Apache poi 版本:3.12 今天在用poi解析excel文件时,碰到一个蛋疼的问题. 在我的excel文件中有一列是日期类型,例如有以下这么一行数据(日期中月份前面的0会自动去掉): 在读取 ...

  5. springboot + poi 解析 excel

    Apache POI 是用 Java 编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对 Microsoft Office 格式档案读和写的功能. 本文使用 s ...

  6. Java面试poi中excel版本大小_java 中 poi解析Excel文件版本问题解决办法

    java 中 poi解析Excel文件版本问题解决办法 发布时间:2020-10-02 03:46:15 来源:脚本之家 阅读:91 作者:程诺 poi解析Excel文件版本问题解决办法 poi解析E ...

  7. 为什么poi解析Excel慢?

    我们项目用xlsx配置业务数据,每个项目中会有500~600张表,解析耗时1分多钟. 尝试改成csv后,解析耗时5~6秒. 这不禁让我陷入思考,都是文本数据,为什么poi解析Excel会这么慢? 带着 ...

  8. 使用Apache POI解析excel

    1.使用Apache POI解析excel,包括.xls和.xlsx Apache POI 针对Workbook有两种不同实现方式,分别是HSSFWorkbook(解析.xls文件)和XSSFWork ...

  9. poi解析Excel文件版本问题

    poi解析Excel文件时有两种格式: HSSFWorkbook格式用来解析Excel2003(xls)的文件 XSSFWorkbook格式用来解析Excel2007(xlsx)的文件 如果用HSSF ...

最新文章

  1. 需求分析的接口需求_再谈需求分析
  2. 【BZOJ 1486】 [HNOI2009]最小圈
  3. Windows批处理命令学习三
  4. 牛客 - 双流机场(思维)
  5. Bootstrap公司年会抽奖活动代码
  6. Python学习笔记:微积分计算
  7. Flask-SQLALchemy查询
  8. Linux diff diff3 diffstat
  9. DataSet与XML导入导出方法
  10. jade模板引擎入门教程
  11. 删除控制面板java无效图标_win7系统删除控制面板中无效的图标的操作方法
  12. 桥接模式 和 中继模式
  13. 【CGAL_网格处理】平滑处理
  14. MATLAB递推最小二乘法(三输入一输出ARX模型、所有样本数据权重为1)
  15. 概率论基础(2)条件概率、全概率公式和贝叶斯公式
  16. 怎么在windows文件资源管理器中打开ftp
  17. pc端签名 vue 生成图片_使用vue实现一个电子签名组件
  18. 登陆模块之JWT单点登录
  19. 行业内口碑好值得信赖的短网址,让你不再选择恐惧
  20. HTML/CSS共7k字带你能独立完成简单网页的制作

热门文章

  1. Canvas(六)基本的动画
  2. Qt 改变QImage图片的fomat()
  3. 堡垒机动态口令使用手册
  4. 考研阅读唐迟视频总结
  5. mac通过adb命令安装apk
  6. 嵌入式处理器-2.3
  7. android屏幕适配无效_Android屏幕适配之全面屏适配
  8. 系统管理员限制了系统策略,无法安装 解决办法
  9. Redis基础类型ZSet增删改查(带Java库源码)
  10. Just2Trade捷仕公司母集团FINAM是俄罗斯最大金融公司之一