Bootstrap

通过java将Excel表格导入数据到数据库

一、首先。我们使用到的是apache的开源框架《poi》

1、导入相应的依赖包
2、连接数据库的框架可自行选择,下文使用的是mybatis-plus
Maven

<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.1.2</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-schemas</artifactId>
    <version>4.1.2</version>
</dependency>

Gradle

    compile 'org.apache.poi:poi:4.1.2'
    compile 'org.apache.poi:poi-ooxml:4.1.2'
    compile 'org.apache.poi:poi-ooxml-schemas:4.1.2'

excel导入数据到数据库原理。

  1. 当我们拿到excel表格,我们第一步应该先读取excel里面的数据信息。
  2. 通过特定的方法将读出来的数据封装到对象中。
  3. 每一行表示一个对象,每一列表示一个对象中的每一个字段属性。
  4. 当有多行时我们需要使用List<Object>(Object是对应excel表格数据的实体类对象)来存取对象
  5. 拿到对象数组后,我们可以通过遍历得到,每一个对象,再通过mybatis将对象导入到数据库

知道了原理,有了思路。我们就可以开始干活了。
1、先拿到excel文件,我们使用MultipartFile接受文件。
2、拿到文件后。通过文件得到一个输入流
3、通过poi对excel进行解析,将表格中的每个数据通过for循环得到。

demo案例:(内附注释)

先了解到我们需要保存哪些字段。
根据字段。创建对应的实体类。

实体类

public class BuiPatientInfo{

private static final long serialVersionUID = 1L;


    /**
     * 患者姓名
     */
    @TableField("patient_name")
        private String patientName;


    /**
     * 加密存储,不可反编译、列表不显示
     */
    @TableField("patient_identity")
        private String patientIdentity;


    /**
     * 就诊卡号
     */
    @TableField("healing_Id")
        private String healingId;


    /**
     * (性别)自动从身份证中截取
     */
    @TableField("patient_sex")
        private Integer patientSex;


    /**
     * (出生年月)自动从身份证中截取
     */
    @TableField("patient_birthDate")
        private LocalDate patientBirthdate;


    /**
     * 其他信息说明
     */
    @TableField("else_Info")
        private String elseInfo;


    /**
     * 创建人ID
     */
    @TableField("create_id")
        private Long createId;


    /**
     * 创建时间
     */
    @TableField("create_time")
        private LocalDateTime createTime;


    /**
     * 最后修改时间
     */
    @TableField("update_time")
        private LocalDateTime updateTime;



    @TableField("patient_state")
    private Integer patientState;

}
  • 以下代码包含了对excel文件的检验是否合法。excel是哪年版的。

数据导入解析工具类

package com.mosukj.util;

import com.mosukj.manage.entity.BuiPatientInfo;
import com.spire.ms.System.DateTime;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.text.DecimalFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

/**
 * @author xjt
 * @version 1.0
 */
public class ReadPatientExcelUtil {
    //总行数
    private static int totalRows = 0;
    //总条数
    private static int totalCells = 0;
    //错误信息接收器
    private static String errorMsg;

    /**
     * 读EXCEL文件,获取信息集合
     * @return
     */
    public static List<BuiPatientInfo> getExcelInfo(MultipartFile mFile) {
        String fileName = mFile.getOriginalFilename();//获取文件名
        try {
            if (!validateExcel(fileName)) {// 验证文件名是否合格
                return null;
            }
            boolean isExcel2003 = true;// 根据文件名判断文件是2003版本还是2007版本
            if (isExcel2007(fileName)) {
                isExcel2003 = false;
            }
            List<BuiPatientInfo> userList = createExcel(mFile.getInputStream(), isExcel2003);
            return userList;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 根据excel里面的内容读取客户信息
     * @param is 输入流
     * @param isExcel2003 excel是2003还是2007版本
     * @return
     * @throws IOException
     */
    public static List<BuiPatientInfo> createExcel(InputStream is, boolean isExcel2003) {
        try{
            Workbook wb = null;
            if (isExcel2003) {// 当excel是2003时,创建excel2003
                wb = new HSSFWorkbook(is);
            } else {// 当excel是2007时,创建excel2007
                wb = new XSSFWorkbook(is);
            }
            List<BuiPatientInfo> userList = readExcelValue(wb);// 读取Excel里面客户的信息
            return userList;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 读取Excel里面客户的信息
     * @param wb
     * @return
     */
    private static List<BuiPatientInfo> readExcelValue(Workbook wb) {
    //默认会跳过第一行标题
        // 得到第一个shell
        Sheet sheet = wb.getSheetAt(0);
        // 得到Excel的行数
        totalRows = sheet.getPhysicalNumberOfRows();
        // 得到Excel的列数(前提是有行数)
        if (totalRows > 1 && sheet.getRow(0) != null) {
            totalCells = sheet.getRow(0).getPhysicalNumberOfCells();
        }
        List<BuiPatientInfo> userList = new ArrayList<BuiPatientInfo>();
        // 循环Excel行数
        for (int r = 1; r < totalRows; r++) {
            Row row = sheet.getRow(r);
            if (row == null){
                continue;
            }
            BuiPatientInfo user = new BuiPatientInfo();
            // 循环Excel的列
            for (int c = 0; c < totalCells-1; c++) {
                Cell cell = row.getCell(c);
                if (null != cell) {
                    if (c == 0) {           //第一列
                        //如果是纯数字,将单元格类型转为String
                        if(cell.getCellTypeEnum()  == CellType.NUMERIC){
                            cell.setCellType(CellType.STRING);
                        }
                        user.setPatientName(cell.getStringCellValue());//将单元格数据赋值给user
                    }
                    else if (c == 1){
                        if(cell.getCellTypeEnum()  == CellType.NUMERIC){
                            cell.setCellType(CellType.STRING);
                        }
                        user.setPatientIdentity(cell.getStringCellValue());
                    }
                    else if (c == 2){
                        if(cell.getCellTypeEnum()  == CellType.NUMERIC){
                            cell.setCellType(CellType.STRING);
                        }
                        String stringCellValue = cell.getStringCellValue();
                        user.setHealingId(stringCellValue);
                    }
                    else if (c == 3){
                        if(cell.getCellTypeEnum()  == CellType.NUMERIC){
                            cell.setCellType(CellType.STRING);
                        }
                        user.setElseInfo(String.valueOf(cell.getStringCellValue()));
                    }
                }
            }
            //将excel解析出来的数据赋值给对象添加到list中
            user.setUpdateTime(LocalDateTime.now());
            user.setPatientBirthdate(IdentityUtil.getPatientBirth(user.getPatientIdentity()));		//拿到身份中好通过已经写好的通过身份证信息获取出生年月工具类
            user.setPatientSex(IdentityUtil.getPatientSex(user.getPatientIdentity()));			//通过省份证号,获取男女信息工具类
            user.setPatientState(1);			//当前实体类字段是固定的,不是excel数据中的。每个对象都可以再遍历完后增加固定属性值
            user.setCreateId(2L);
            user.setCreateTime(LocalDateTime.now());
            // 添加到list
            userList.add(user);
        }
        return userList;
    }
    /**
     * 验证EXCEL文件
     *
     * @param filePath
     * @return
     */
    public static boolean validateExcel(String filePath) {
        if (filePath == null || !(isExcel2003(filePath) || isExcel2007(filePath))) {
            errorMsg = "文件名不是excel格式";
            return false;
        }
        return true;
    }
    // @描述:是否是2003的excel,返回true是2003
    public static boolean isExcel2003(String filePath)  {
        return filePath.matches("^.+\\.(?i)(xls)$");
    }
    //@描述:是否是2007的excel,返回true是2007
    public static boolean isExcel2007(String filePath)  {
        return filePath.matches("^.+\\.(?i)(xlsx)$");
    }
}

调用getExcelInfo可以得到一个对象数组。

之后就方便导入数据库了

我拿到数组对象后的处理方式:

实例业务接口如下

@PostMapping("/excelExport")
    public void test(HttpServletRequest request,HttpServletResponse response,@RequestParam(value="file",required = false) MultipartFile file){
        long startTime = System.currentTimeMillis();
        List<String> list = new ArrayList();
        Map<String,Object> res = new HashMap<>();
        int row=1;
        int rowSuccess=0;
        Integer errorCount=0;
        QueryWrapper<BuiPatientInfo> wrapper;
//        ReadExcel readExcel = new ReadExcel();
        List<BuiPatientInfo> excelInfo = ReadPatientExcelUtil.getExcelInfo(file);
        for (BuiPatientInfo patientInfo : excelInfo) {
//            System.out.println(patientInfo);
            wrapper=new QueryWrapper<>();
            row++;
            patientInfo.setHealingId(patientInfo.getHealingId());
            wrapper.eq("healing_Id", patientInfo.getHealingId());
            wrapper.ne("patient_state", 9);
            //业务代码,通过患者id查询数据库中式否有该患者,如果已存在,则不保存,跳过该患者
            int patientCount = buiPatientInfoService.getBaseMapper().getPatientCount(wrapper);
            if (patientCount>0){
                list.add("在第"+row+"行的<"+patientInfo.getHealingId()+">患者已存在!!");
                errorCount++;
            }else {
                boolean save = buiPatientInfoService.save(patientInfo);
                if (save){
                    rowSuccess++;
                }
            }
        }
        if (list.size()>0){
            res.put("log", list);
        }
        res.put("success", "导入数据成功条数:"+rowSuccess);
        res.put("error", "导入数据失败条数:"+errorCount);
        long endTime = System.currentTimeMillis();
        String time = String.valueOf((endTime - startTime) / 1000);
        res.put("time", "导入数据用时:"+time+"秒");
        renderResult(response, res);
    }

renderResult()是封装好的返回前端数据的一个工具类,如需了解,请异步我的另一篇文章
封装好的返回前端数据工具类详情(传送门)

excel模板
在这里插入图片描述

以上从excel获取数据导入到数据库为静态的导入,动态获取字段导入到数据库请移步笔者其他文章链接:动态导入数据到数据库。(注意,excel模板为固定字段,每一列对应工具类里面的获取第1/2/3…)

  1. 调用成功后返回。导入失败数据所在的行号,并注释姓名(根据需要可修改)。
  2. 成功导入条数。
  3. 导入用时
  4. 导入成功数、导入失败数

有疑问欢迎大家评论区讨论交流!乐意解答。

;