Java 读取Excel模板中的数据到实体类

目录

  • 一. 前提条件
    • 1.1 需求
    • 1.2 分析
  • 二. 准备
    • 2.1 自定义注解
    • 2.2 封装Excel的实体类
  • 三. 前台
  • 四. Controller层
  • 五. Service层💪💪💪
  • 六. 效果


一. 前提条件

1.1 需求

  • 从指定的Excel模板中读取数据,将读取到的数据存储到数据库中。

在这里插入图片描述

1.2 分析

  • 需要用到 poi 读取Excel
  • 使用自定义注解标记Excel单元格的行,列,数据类型方便读取数据
  • 需要使用 hibernate validation 校验数据
  • 前台需要使用 FormData() 对象向后台传入文件,需要指定只能上传Excel类型的文件
  • 读取到的数据依次的get和set到entity中很麻烦,需要用到 反射 进行封装

二. 准备

2.1 自定义注解

  • 校验项目不为空
import javax.validation.Constraint;
import javax.validation.constraints.NotEmpty;
import javax.validation.Payload;
import javax.validation.ReportAsSingleViolation;
import java.lang.annotation.*;

@Documented
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {})
@NotEmpty
@ReportAsSingleViolation
public @interface ValidateNotEmpty {

    String msgArgs() default "";
	
	// 1001E=请输入{msgArgs}。
	String message() default "{1001E}";

	Class<?>[] groups() default {};

	Class<? extends Payload>[] payload() default {};
}
  • 标记Excel单元格的行,列,属性信息的注解
import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface ExcelCellAnnotation {
	
	// Excel单元格行的index
    int rowIndex() default 0;
	// Excel单元格列的index
    int columnIndex() default 0;
	// Excel单元格列的类型,默认是字符串类型
    java.lang.Class type() default String.class;
}

2.2 封装Excel的实体类

  • 以「契約者申請日」这个项目为例说明
    • 在Excel模板中的位置为第2行
    • 在Excel模板中的位置为第22列
    • 在Excel模板中的数据类型为 Date 类型
import lombok.Data;

import java.util.Date;

@Data
public class ExcelEntity {

    /**
    * 契約者申請日
    */
    @ValidateNotEmpty(msgArgs = "契約者申請日")
    @ExcelCellAnnotation(rowIndex = 1, columnIndex = 21, type = Date.class)
    private String keiyakushaShinseibi;

    /**
    * フリガナ
    */
    @ValidateNotEmpty(msgArgs = "フリガナ")
    @ExcelCellAnnotation(rowIndex = 4, columnIndex = 5)
    private String furikana;

    /**
    * 契約者(氏名)
    */
    @ValidateNotEmpty(msgArgs = "契約者(氏名)")
    @ExcelCellAnnotation(rowIndex = 5, columnIndex = 5)
    private String keiyakuShaName;

    /**
    * 性別_男
    */
    @ExcelCellAnnotation(rowIndex = 5, columnIndex = 20)
    private String sexMan;

    /**
     * 性別_女
     */
    @ExcelCellAnnotation(rowIndex = 5, columnIndex = 22)
    private String sexWoman;
  
    /**
    * 契約者住所
    */
    @ValidateNotEmpty(msgArgs = "契約者住所")
    @ExcelCellAnnotation(rowIndex = 8, columnIndex = 5)
    private String keiyakushaJyusho;

    /**
    * 契約者連絡先_携帯
    */
    @ValidateNotEmpty(msgArgs = "契約者連絡先_携帯")
    @ExcelCellAnnotation(rowIndex = 10, columnIndex = 8)
    private String keiyakushaPhone;

    /**
    * 契約者連絡先_メール
    */
    @ValidateNotEmpty(msgArgs = "契約者連絡先_メール")
    @ExcelCellAnnotation(rowIndex = 10, columnIndex = 16)
    private String keiyakushaMail;
}

三. 前台

  • 通过accept="application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"来实现只能上传Excel类型的数据
  • 上传成功或者失败都需要把文件上传input中的value置为空,保证同一个文件可以上传多次
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <input 
    	type="file" 
    	id="excel" 
    	accept="application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
     />
    <button id="btn">上传</button>
</body>
<script src="https://code.jquery.com/jquery-3.6.3.js"></script>
<script>
    $(function () {
        $("#btn").click(function () {

            const formData = new FormData();
            formData.append("excelFile", $("#excel").get(0).files[0]);

            $.ajax({
                url: `/poi/excel`,
                type: 'POST',
                data: formData,
                processData: false,
                contentType: false,
                success: function (data, status, xhr) {
                    console.log(data);
                },
                error(xhr, textStatus, errorMessage) {
                    console.log(textStatus);
                },
                complete(jqXHR, textStatus) {
                    // 清空上传的文件,保证可以多次上传同一个文件
                    $("#excel").val("");
                }
            });
        })
    })
</script>
</html>

四. Controller层

  • 通过MultipartHttpServletRequest来获取前台上传到后台的文件
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;

@Controller
@RequestMapping("/poi")
public class PoiController {

    @Autowired
    private PoiService service;

    @GetMapping("/init")
    public ModelAndView init() {

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("poiTest");
        return modelAndView;
    }
	
	// 处理上传到后台的文件
    @PostMapping("/excel")
    public ResponseEntity<Void> handleExcel(MultipartHttpServletRequest request) throws Exception {

        // 获取前台传入的文件
        List<MultipartFile> file = request.getMultiFileMap().get("excelFile");
        MultipartFile multipartFile = file.get(0);

        // 将Excel中的文件读取到Entity中
        ExcelEntity excelEntity = new ExcelEntity();
        service.readExcel(multipartFile, excelEntity);

        // 对Excel中的数据进行校验
        service.validateExcelData(excelEntity);
        
        // 告知操作成功
        return ResponseEntity.noContent().build();
    }
}

五. Service层💪💪💪

import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.ConstraintViolation;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Set;

@Service
public class PoiService implements InitializingBean {

    // set方法
    private final static String methodAction = "set";
    // Excel单元格格式化对象
    private final static DataFormatter formatter = new DataFormatter();
    // 日期格式化对象
    private static DateFormat dateformat = null;

    // 输入校验对象
    @Autowired
    private LocalValidatorFactoryBean validator;

    // 进行一些初始化操作
    @Override
    public void afterPropertiesSet() {
        // 指定日期的格式化
        dateformat = new SimpleDateFormat("yyyy-MM-dd");
    }
	
	// 读取Excel中的数据
    public void readExcel(MultipartFile multipartFile, ExcelEntity excelEntity) throws Exception {

        // 获取sheet页对象
        InputStream inputStream = multipartFile.getInputStream();
        XSSFWorkbook sheets = new XSSFWorkbook(inputStream);
        XSSFSheet sheet = sheets.getSheetAt(0);

        // 单元格中的值
        String cellValue = "";

        // 获取类上的所有属性(public和private都可以获取到)
        Field[] fields = excelEntity.getClass().getDeclaredFields();
        for (Field field : fields) {

            // 如果该属性中没有ExcelCellAnnotation注解的话,跳过
            ExcelCellAnnotation annotation = field.getAnnotation(ExcelCellAnnotation.class);
            if (ObjectUtils.isEmpty(annotation)) {
                continue;
            }

            // 根据行列的index,获取当前的单元格对象
            XSSFCell cell = sheet
                    // 获取属性上注解标记的单元格的行index
                    .getRow(annotation.rowIndex())
                    // 获取属性上注解标记的单元格的列index
                    .getCell(annotation.columnIndex());

            // 获取属性上注解标记的单元格的类型
            Class valueType = annotation.type();
            // 根据当前单元格的类型获取单元格的值
            if (Date.class == valueType) {
                cellValue = dateformat.format(cell.getDateCellValue());
            } else if (String.class == valueType) {
                cellValue = formatter.formatCellValue(cell);
            }

            // 通过反射将单元格的值动态封装到实体类中
            String methodName = methodAction + StringUtils.capitalize(field.getName());
            Method setMethod = ReflectionUtils.findMethod(excelEntity.getClass(), methodName, cellValue.getClass());
            ReflectionUtils.invokeMethod(setMethod, excelEntity, cellValue);
        }
    }
	
	// 对Excel中的数据进行校验
    public void validateExcelData(ExcelEntity excelEntity) {

        // 使用自定义注解对excel数据进行校验并打印
        Set<ConstraintViolation<ExcelEntity>> validateResults = validator.validate(excelEntity);
        for (ConstraintViolation<ExcelEntity> validateResult : validateResults) {
            System.out.println(validateResult.getMessage());
        }

        // 打印excel中获取到的数据
        System.out.println(excelEntity);
    }
}

六. 效果

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VBA定位文本框控件中光标位置

实例需求&#xff1a;用户窗体中有如下4个TextBox控件&#xff0c;TextBox1中已经有文字内容&#xff0c;点击【定位】按钮&#xff0c;统计TextBox1中段落数量&#xff0c;并定位TextBox1中光标位置&#xff08;箭头处&#xff09;&#xff0c;如下图所示。 示例代码如下。 P…

谈谈你对ThreadLocal的理解

谈谈你对ThreadLocal的理解 ThreadLocal是Java中的一个线程本地变量&#xff0c;它可以在多线程环境下&#xff0c;为每个线程提供独立的变量副本&#xff0c;保证了线程之间的数据隔离。ThreadLocal通常用于解决多线程共享变量的线程安全问题。 ThreadLocal通过一个ThreadLo…

第03章_基本的SELECT语句

第03章_基本的SELECT语句 &#x1f3e0;个人主页&#xff1a;shark-Gao &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是shark-Gao&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f389;目前状况&#xff1a;23届毕业生&#xff0c;…

【Redis】十大数据类型(上篇)

文章目录概述命令官网Key命令Redis 的过期时间设置有四种形式&#xff1a;redis字符串(String)最最常用 set key value常用命令图示多值设置 mset、mget获取指定区间范围内的值 getrange、setrange数值增减 INCR key、DECR key获取内容长度及内容追加 STRLEN key、APPEND key x…

基于Android的停车场车位预约系统app-动态计算停车时长-公告-反馈

在设计时,用现代多媒体技术对 进行存储、加载智能码、调用、对比及识别,使得进出的车辆同时处于该系统电脑的监控之下&#xff0c;创建车库管理与车牌识别两者完美结合的管理流程。 智能停车场收费管理系统是一种高效快捷、公正准确、科学经济的停车场管理手段&#xff0c;是停…

工具:dumpbin.exe : COFF DLL 动态库依赖库 :VS工具

摘要&#xff1a; 速度快&#xff0c;不会像depend.dll 那样卡顿。但是无法查看调用dll 调用的dll&#xff0c;所以不如depend.exe 好用。查看方式不如depend.exe 直观。 总结&#xff1a;** 可能不怎么用** 介绍&#xff1a; dumpbin.exe是微软二进制文件转储器。显示有关…

字节跳动软件测试岗,前两面过了,第三面被面试官吊打,结局我哭了

阎王易见&#xff0c;小鬼难缠。我一直相信这个世界上好人居多&#xff0c;但是也没想到自己也会在阴沟里翻船。我感觉自己被字节跳动的HR坑了。 在这里&#xff0c;我只想告诫大家&#xff0c;offer一定要拿到自己的手里才是真的&#xff0c;口头offer都是不牢靠的&#xff0…

Uni-Mol: A Universal 3D Molecular Representation Learning Framework

Uni-Mol: 一个通用的三维分子表示学习框架 ICLR 2023 Uni-Mol 论文&#xff1a;Uni-Mol: A Universal 3D Molecular Representation Learning Framework | OpenReview Uni-Mol 代码&#xff1a;&#xff1a;GitHub - dptech-corp/Uni-Mol: Official Repository for the Uni-Mo…

Python:《寻找整数》

问题描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 有一个不超过 1017 的正整数 n&#xff0c;知道这个数除以 2 至 49 后的余数如下表所示&#xff0c;求这个正整数最小是多少。 运行限制 最大运行时间&#xff1a;…

辉煌优配|人民币将可直接买港股 多家港股公司申请 增设人民币柜台

3月以来&#xff0c;多家港股公司发布公告称&#xff0c;已正式提交有关增设人民币货台的请求。这意味着港交所力推的港股“港币-人民币双货台形式”进入实质性推进阶段&#xff0c;离岸人民币行将迎来愈加丰富的出资标的。 多位业内人士表明&#xff0c;树立双货台形式是港交所…

Java设计模式(十七)—— 组合模式

组合模式的定义如下&#xff1a;将对象组合成树形结构以表示“部分-整体”的层次结构&#xff0c;让用户对单个对象和组合对象的使用具有一致性。 适用组合模式的情景如下&#xff1a; 希望表示对象的“部分—整体”层次结构希望用户用一致方式处理个体和组合对象一、问题的提…

这是一篇能够教会你运营阿里巴巴国际站的文章

对于很多跨境人来说&#xff0c;运营真的是一个让人头疼的大事情。不知道要从哪个方面下手&#xff0c;不知道要往哪方面努力等等问题都是很常见的&#xff0c;所以今天龙哥就解剖一下阿里巴巴国际站的运营方法&#xff0c;简单地给大家讲一下要掌握哪些方面的知识。运营这条路…

【数据结构篇C++实现】- 哈希表

文章目录&#x1f680;一、哈希表的原理精讲&#x1f6a2;&#xff08;一&#xff09;概念&#x1f6a2;&#xff08;二&#xff09;常见哈希函数的构造方法1.直接定址法2.数字分析法3.平方取中法4.除留余数法5.随机数法&#x1f6a2;&#xff08;三&#xff09;哈希冲突与处理…

web服务器—nginx

一、nginx介绍Nginx(“engine x”)是一款是由俄罗斯的程序设计师Igor Sysoev所开发高性能的 Web和 反向代理服务器&#xff0c;也是一个 IMAP/POP3/SMTP 代理服务器。和apache一样&#xff0c;都是web服务器软件&#xff0c;因为其性能优异&#xff0c;所以被广大运维喜欢。又因…

【python】【protobuf】逆向还原protobuf结构

文章目录一、前言二、示例三、python demo一、前言 在很多场景&#xff0c;都有一个需求&#xff1a; 得到了一个编码后的protobuf数据&#xff08;比如竞品调研的的数据包&#xff09;&#xff0c;需要逆向还原其proto结构文件。 有3种方案去做这件事情&#xff1a; 从编码入…

Linux常用文件管理命令

Linux常用文件管理命令 目录Linux常用文件管理命令前言常用命令练习题创建文件夹题目代码复制题目代码移动题目代码删除题目代码系列操作题目代码前言 本文将讲解我们在使用Linux操作系统时经常需要使用的命令&#xff0c;也可以当成是一篇笔记的记录&#xff0c;当然光看这些…

Ubuntu安装交叉编译器gcc

1.创建文件并把压缩包复制到文件夹下 2.解压到文件夹下 先找到放置的目录 也可以直接找到文件夹右键-在终端打开 通过-C选项指定解压后的目标目录 tar -jxvf gcc-linaro-arm-linux-gnueabihf-4.9-2014.09_linux.tar.bz2 -C /opt 注意:输入文件名时可以Tab键自动补齐 输入…

计算机网络中端到端与点到点的区别

计算机网络中端到端与点到点的区别 数据传输的可靠性是通过数据链路层和网络层的点对点和传输层的端对端保证的。端到端与点到点是针对网络中传输的两端设备间的关系而言的。 在一个网络系统的不同分层中&#xff0c;可能用到端到端传输&#xff0c;也可能用到点到点传输。如…

限流、熔断、服务降级

在分布式系统中&#xff0c;如果某个服务节点发生故障或者网络发生异常&#xff0c;都有可能导致调用方被阻塞等待&#xff0c;如果超时时间设置很长&#xff0c;调用方资源很可能被耗尽。这又导致了调用方的上游系统发生资源耗尽的情况&#xff0c;最终导致系统雪崩。 举例&a…

[Vulfocus解题系列]Spring WebFlow 远程代码执行漏洞(CVE-2017-4971)

简介 Spring WebFlow 是一个适用于开发基于流程的应用程序的框架&#xff08;如购物逻辑&#xff09;&#xff0c;可以将流程的定义和实现流程行为的类和视图分离开来。在其 2.4.x 版本中&#xff0c;如果我们控制了数据绑定时的field&#xff0c;将导致一个SpEL表达式注入漏洞…