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

news2024/12/26 4:32:33

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 {
            @Override
            public 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 {
            @Override
            public Object evaluateDataByType(Cell dataCell, List<List<java.lang.String>> validateList) {
                double cellValue = dataCell.getNumericCellValue();
                return cellValue != 0 ? DateUtil.getJavaDate(cellValue) : null;
            }
        }, BigDecimal {
            @Override
            public 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.获取要操作的sheet
        Sheet 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 {

    @Autowired
    private ResourceLoader resourceLoader;
    
    @Test
    void 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.coloradmin.cn/o/1507681.html

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

相关文章

力扣--动态规划/回溯算法131.分割回文串

思路分析&#xff1a; 动态规划 (DP)&#xff1a; 使用动态规划数组 dp&#xff0c;其中 dp[i][j] 表示从字符串 s[i] 到 s[j] 是否为回文子串。预处理动态规划数组&#xff1a; 从字符串末尾开始&#xff0c;遍历每个字符组合&#xff0c;判断是否为回文子串&#xff0c;填充…

像SpringBoot一样使用Flask - 2.静态资源访问及模版

一、安装并导入 render_template 功能&#xff1a;渲染/加载模板&#xff0c;一般是html页面 参数&#xff1a;函数的第一个参数是模板的文件名&#xff0c;必填&#xff0c;后面的参数都是键值对&#xff0c;表示模板中变量对应的值&#xff0c;非必填 (不填界面也不会展示成变…

使用express+nginx+pm2+postman实现推送zip包自动更新前端网页

1.nginx配置将80端口代理到项目的3000端口 server {listen 80; #监听的端口server_name localhost; #监听的域名#charset koi8-r;#access_log logs/host.access.log main;location / {#root html;#index index.html index.html;proxy_pass http://127.0.0.1:3000; #转…

Android App冷启动耗时优化

Android应用启动过程 Android应用启动过程&#xff0c;主要包含app::onCreate及执行前的Application阶段及Activity::onCreate执行之后的Activity阶段&#xff0c;以及两个阶段之间的间隙handleMessage阶段和最终页面渲染上屏完成前数据加载阶段四个区间组成。 具体来看&#x…

如何提高内存和cpu使用率呢?-Linux类资源

最近公司项目上线时&#xff0c;红蓝线巡检时&#xff0c;部分服务器因配置高但使用率低而需要降级。 为了避免降级&#xff0c;如何增加内存和CPU使用率&#xff1f; 这可以通过脚本来实现。 当前服务器操作系统版本&#xff1a;-7_5。 连接服务器后&#xff0c;登录账号。 …

深入理解Debug断点调试技巧

在软件开发过程中&#xff0c;调试是一个非常重要的环节&#xff0c;而使用断点是提高调试效率的关键技巧之一。本文将深入探讨如何利用断点进行高效的调试&#xff0c;常用的快捷键以及一些常见的断点调试技巧。 一、调试技巧&#xff1a; 1. 设置断点&#xff1a;在代码中设…

你配赚米吗?韭菜就在繁荣时进场!防诈P的最简单方法,还不告诉你爸妈?——早读(逆天打工人爬取热门微信文章解读)

你听到哪里好&#xff0c;你去了就是韭菜 引言Python 代码第一篇 人民日报 1460万&#xff0c;保住了&#xff01;第二篇 人民日报 夜读 每一次努力&#xff0c;都是幸运的伏笔第三篇 人民日报 来啦 新闻早班车要闻社会政策 结尾 “故天将降大任于是人也&#xff0c;必先苦其心…

CMake 交叉编译

想知道“魔笛手”在这里能发挥什么作用吗&#xff1f;想象一下&#xff0c;把 CMake 当做法力高强的魔笛手&#xff0c;C 的项目则是故事中的那些被魔笛手拯救的孩子。 父母要抚养一个孩子并非易事&#xff0c;营养需要面面俱到&#xff0c;保证身体健康&#xff0c;关心事无巨…

【活动】探索人工智能的“迷惑瞬间”:真实体验与技术挑战

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 标题&#xff1a;探索人工智能的“迷惑瞬间”&#xff1a;真实体验与技术挑战引言…

vulhub中Weblogic 管理控制台未授权远程命令执行漏洞复现(CVE-2020-14882,CVE-2020-14883)

Weblogic是Oracle公司推出的J2EE应用服务器。在2020年10月的更新中&#xff0c;Oracle官方修复了两个长亭科技安全研究员voidfyoo 提交的安全漏洞&#xff0c;分别是CVE-2020-14882和CVE-2020-14883。 CVE-2020-14882允许未授权的用户绕过管理控制台的权限验证访问后台&#x…

英飞凌电源管理PMIC的安全应用

摘要 本篇文档主要用来介绍英飞凌电源管理芯片TLF35584的使用&#xff0c;基于电动助力转向应用来介绍。包含一些安全机制的执行。 TLF35584介绍 TLF35584是英飞凌推出的针对车辆安全应用的电源管理芯片&#xff0c;符合ASIL D安全等级要求&#xff0c;具有高效多电源输出通道&…

[综述笔记]Graph Neural Networks in Network Neuroscience

论文网址&#xff1a;Graph Neural Networks in Network Neuroscience | IEEE Journals & Magazine | IEEE Xplore 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xf…

阿里云服务器2核4G配置2024年活动价格及收费标准价格表

阿里云服务器2核4G配置2024年需要多少钱&#xff1f;2核4G配置的阿里云服务器选择不同实例规格和地域&#xff0c;其收费标准和活动价格都是不一样的&#xff0c;2024年3月1日&#xff0c;阿里云率先宣布云产品大规模调价&#xff0c;其中云服务器最高降幅达36%&#xff0c;因此…

计算机网络:网络层知识点汇总

文章目录 一、网络功能概述二、SDN基本概念三、路由算法与路由协议概述四、IP数据报格式五、IP数据报分片六、IPv4地址七、网络地址转换NAT八、子网划分和子网掩码九、无分类编址CIDR十、ARP协议十一、DHCP协议十二、ICMP协议十三、IPv6十四、RIP协议与距离向量算法十五、OSPF协…

qt-C++笔记之使用Cmake来组织和构建QWidget工程项目

qt-C笔记之使用Cmake来组织和构建QWidget工程项目 —— 杭州 2024-03-10 code review! 文章目录 qt-C笔记之使用Cmake来组织和构建QWidget工程项目1.运行2.文件结构3.CMakeLists.txt4.main.cpp5.widget.h6.widget.cpp7.widget.ui 1.运行 2.文件结构 3.CMakeLists.txt 代码 c…

Qt 数据库驱动未装载MYSQL

一、第一部分 0.Qt 连接mysql数据库时报错&#xff1a; QSqlDatabase: QMYSQL driver not loaded QSqlDatabase: available drivers: QSQLITE QODBC QODBC3 QPSQL QPSQL7 QT连接代码&#xff1a; bool createMysqlConn() {QSqlDatabase sqldb QSqlDatabase::addDatabase(&qu…

【算法面试题】-05

智能成绩表 class Student:def __init__(self):self.name "" # 学生名字self.scores [] # 每门课成绩students [Student() for _ in range(10004)] # 存储学生信息的数组 key_index 0 # 要排序的课程名的下标# 自定义排序函数 def student_comparator(a, b):…

中间件 | Redis - [基本信息]

INDEX 1 常规用法2 QPS3 pipeline 1 常规用法 分布式锁 最常见用法&#xff0c;需要注意分布式锁的redis需要单点 分布式事务 分布式事务中&#xff0c;核心的技术难点其实是分布式事务这个事本身作为数据的持久化 2PC&#xff0c;比如 seata 的 AT 模式下&#xff0c;将 un…

低功耗控制器结合Node-RED革新电力系统监控方式

现代电力行业的不断发展与技术进步&#xff0c;微电网ARM低功耗控制器与Node-RED的深度融合&#xff0c;为电力系统的智能化运维提供了崭新的解决方案。这一集成方案将硬件控制的高效稳定与软件逻辑处理的强大灵活性相结合&#xff0c;实现了对变电站设备、输电线路状态以及智能…

Importing ArkTS files to JS and TS files is not allowed. <etsLint> 解决方法

这个问题是 harmonyos 项目中 因为在ts中导入了 ets文件造成的 系统不需要在ts文件中 直接导入 ets文件 一般来讲 ts能写的代码 ets也可以 最简单的方式 就是直接将 ts 改为ets 右键文件 如下图选择 将后缀的ts改为 ets 就OK了