IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> 将Excel表信息通过反射导入到数据库 -> 正文阅读

[开发测试]将Excel表信息通过反射导入到数据库

看如下Controller层接收到Excel表路径的操作如下:

  /**
     * 数据导入
     * @return
     */
    @PostMapping("/import")
    public ResultJson importExcel(@RequestParam("file") MultipartFile file) throws IOException {

        //下面路径写死了,可以通过上传到服务器获取到指定路径
        String filepath = "C:\\Users\\xxx\\Desktop\\" + file.getOriginalFilename();
        //通过ExcelUtil的构造器给泛型赋值,也将反射对象赋值给ExcelUtil类
        ExcelUtil<Test> testExcelUtil = new ExcelUtil<>(Test.class);
        //调用对象里的importExcel4方法获取到Excel表里的数据(进行了封装)
        List<Test> list = testExcelUtil.importExcel4(filepath);
        //使用mybatis-plus的批量插入到数据库中
        boolean b = testService.saveBatch(list);



        return ResultJson.ok();
    }

ExcelUtil类的构造器如下:

public class ExcelUtil<T> {
   
    /**
     * 实体对象
     */
    public Class<T> clazz;

    public ExcelUtil(Class<T> clazz) {
        this.clazz = clazz;
    }
}

通过Excel表地址获取到Excel表中的信息,方法如下:

 /**
     * @Description:获取IO流中的数据,组装成List<List<Object>>对象
     * @param in,fileName
     * @return
     * @throws IOException
     */
    public static List<List<Object>> getListByExcel(InputStream in, String fileName) throws Exception{
        List<List<Object>> list = null;

        //创建Excel工作薄
        Workbook work = getWorkbook(in,fileName);
        if(null == work){
            throw new Exception("创建Excel工作薄为空!");
        }
        Sheet sheet = null;  //页数
        Row row = null;  //行数
        Cell cell = null;  //列数

        list = new ArrayList<List<Object>>();
        //遍历Excel中所有的sheet
        for (int i = 0; i < work.getNumberOfSheets(); i++) {
            sheet = work.getSheetAt(i);
            if(sheet==null){continue;}

            //遍历当前sheet中的所有行
            for (int j = sheet.getFirstRowNum(); j <= sheet.getLastRowNum(); j++) {
                row = sheet.getRow(j);
                if(row==null){continue;}

                //遍历所有的列
                List<Object> li = new ArrayList<Object>();
                for (int y = row.getFirstCellNum(); y < row.getLastCellNum(); y++) {
                    cell = row.getCell(y);
                    li.add(getValue(cell));
                }
                list.add(li);
            }
        }

        return list;

    }

    /**
     * @Description:根据文件后缀,自适应上传文件的版本
     * @param inStr,fileName
     * @return
     * @throws Exception
     */
    public static  Workbook getWorkbook(InputStream inStr,String fileName) throws Exception{
        Workbook wb = null;
        String fileType = fileName.substring(fileName.lastIndexOf("."));
        if(excel2003L.equals(fileType)){
            wb = new HSSFWorkbook(inStr);  //2003-
        }else if(excel2007U.equals(fileType)){
            wb = new XSSFWorkbook(inStr);  //2007+
        }else{
            throw new Exception("解析的文件格式有误!");
        }
        return wb;
    }

    /**
     * @Description:对表格中数值进行格式化
     * @param cell
     * @return
     */
    //解决excel类型问题,获得数值
    public static String getValue(Cell cell) {
        String value = "";
        if(null==cell){
            return value;
        }
        switch (cell.getCellType()) {
            //数值型
            case NUMERIC:
                if (DateUtil.isCellDateFormatted(cell)) {
                    //如果是date类型则 ,获取该cell的date值
                    Date date = DateUtil.getJavaDate(cell.getNumericCellValue());
                    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    value = format.format(date);;
                }else {// 纯数字
                    BigDecimal big=new BigDecimal(cell.getNumericCellValue());
                    value = big.toString();
                    //解决1234.0  去掉后面的.0
                    if(null!=value&&!"".equals(value.trim())){
                        String[] item = value.split("[.]");
                        if(1<item.length&&"0".equals(item[1])){
                            value=item[0];
                        }
                    }
                }
                break;
            //字符串类型
            case STRING:
                //如果是string类型进行下划线转驼峰处理
                value = underlineToHump(cell.getStringCellValue());
                break;
            // 公式类型
            case FORMULA:
                //读公式计算值
                value = String.valueOf(cell.getNumericCellValue());
                if (value.equals("NaN")) {// 如果获取的数据值为非法值,则转换为获取字符串
                    value = cell.getStringCellValue();
                }
                break;
            // 布尔类型
            case BOOLEAN:
                value = " "+ cell.getBooleanCellValue();
                break;
            default:
                value = cell.getStringCellValue();
        }
        if("null".endsWith(value.trim())){
            value="";
        }
        return value;
    }

上面调用了下划线转驼峰处理如下:

?

   //下划线转驼峰正则表达式
    private static Pattern UNDERLINE_PATTERN = Pattern.compile("_([a-z])");

/**
     * 根据传入的带下划线的字符串转化为驼峰格式
     * @param str
     * @author mrf
     * @return
     */

    public static  String underlineToHump (String str) {
        //正则匹配下划线及后一个字符,删除下划线并将匹配的字符转成大写
        Matcher matcher = UNDERLINE_PATTERN.matcher(str);
        StringBuffer sb = new StringBuffer(str);
        if (matcher.find()) {
            sb = new StringBuffer();
            //将当前匹配的子串替换成指定字符串,并且将替换后的子串及之前到上次匹配的子串之后的字符串添加到StringBuffer对象中
            //正则之前的字符和被替换的字符
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
            //把之后的字符串也添加到StringBuffer对象中
            matcher.appendTail(sb);
        } else {
            //去除除字母之外的前面带的下划线
            return sb.toString().replaceAll("_", "");
        }
        return underlineToHump(sb.toString());
    }

这里的代码是通过传入输入流和文件位置获取到Excel表中的数据,并对数据进行一些处理,最后获取到的Excel表中的数据都会转换为String类型,想进行插入到数据库我们还要对获取到的数据进行类型转换。

在转换前我们需要先将表头信息和我们的实体类进行绑定再对实体类进行赋值,如下图Excel表

?

如上表头信息,也就是第一行数据我们可以对下划线进行转驼峰处理,通过反射在实体类中找到属性和表头信息一致的进行绑定,在对其他数据进行赋值到实体类中。

但是一般用户都会使用中文作为表头信息如下图:

?

我们可以 给实体类添加注解,在注解的name属性中写上对应的中文,可以通过反射获取到注解信息,和里面的值进行匹配。

实体类如下所示:

@Data
@TableName("t_test")
@Excel(name = "测试")
public class Test {

        @TableId(type = IdType.AUTO)
        @Excel(name = "序号", cellType = Excel.ColumnType.NUMERIC)
        private Long id;

        @Excel(name = "姓名")
        private String name;

        @ApiModelProperty(value = "创建时间")
        @TableField(fill = FieldFill.INSERT)
        @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
        private LocalDateTime createTime;

        @TableField(exist=false)
        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
        private LocalDateTime createTimeBegin;
        @TableField(exist=false)
        @JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss")
        private LocalDateTime createTimeEnd;

}

给各个属性添加了Excel注解

然后我最上面Controller调用的方法是将获取到的?Excel表的信息赋值给实体类,再把这些类放到List集合中,再对它们进行数据库的批量插入操作。

而我们如何知道Excel表中每列信息都对应哪个类中的哪个属性呢?

我们再Controller调用ExcelUtil类时已经对泛型进行了赋值,告诉它要处理哪个实类,我们在ExcelUtil表中获取到实体类后我们就可以对它进行反射获取到类中的属性名和注解信息,而我们Excel表中第一行和它相等的我们就认为这一列数据属于这个属性,对它进行赋值,代码如下:

    /**
     * 对获取到的Excel表的数据进行封装,返回一个可批量插入的集合
     */
    public List<T> importExcel4(String filepath){
        //创建一个可以返回的集合,最后返回它
        List<T> data = new ArrayList<>();
        FileInputStream inputStream = null;
        try {
            //创建一个输入流,调用获取Excel表内信息的方法获取到Excel表中的数据
            inputStream = new FileInputStream(new File(filepath));
            List<List<Object>> list = ExcelUtil.getListByExcel(inputStream, filepath);
            //获取到的Excel表中的信息第一行是和实体类属性或注解进行校验是否相等的,单独拿出来
            List<Object> firstRows = null;
            if(list != null && list.size() > 0){
                firstRows = list.get(0);
            }
            //对其他每行数据进行赋值给实体类并添加到data集合中
            for (int i = 1; i < list.size(); i++) {
                List<Object> rows = list.get(i);
                //通过反射实例化要进行处理的类
                T object = clazz.newInstance();
                //对每行的每列数据进行处理
                for (int j = 0; j < rows.size(); j++) {
                    String cellVal = (String) rows.get(j);
                    //调用此方法进行给实体类属性赋值
                    setFieldValueByFieldNameOrAnnotation(object, firstRows.get(j).toString().trim(), cellVal);
                }
                data.add(object);
                object = null;
                System.out.println(data);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return data;
    }

    /**
     * 通过反射判断注解或属性名给实体类赋值,
     * 对获取到的数据String类型进行类型转换,类型要和实体类属性类型一致
     * @param fieldName
     * @param val
     */
    private Object setFieldValueByFieldNameOrAnnotation(Object object, String fieldName, Object val) {
        try {
            //通过反射获取到类中全部属性
            Field[] fields = clazz.getDeclaredFields();
            //遍历对每个属性进行判断
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                //通过反射获取到属性中注解,如果没有此注解直接进入下一个属性的判断
                Excel excel= field.getAnnotation(Excel.class);
                if(excel == null) continue;
                //判断Excel表的第一行的某一列是否和获取到属性或注解信息一致,一致进行封装处理
                //将String类型装换为其相对应的类型
                if(fieldName.equals(field.getName()) || fieldName.equals(excel.name())){
                    Object typeVal = null;
                    String type = field.getType().getName();
                    if(type.equals("long") || type.equals("java.lang.Long")){
                        typeVal = Long.parseLong((String) val);
                    }else if(type.equals("java.time.LocalDateTime")){
                        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                        typeVal = LocalDateTime.parse((CharSequence) val,df);
                    }else if(type.equals("int") ||type.equals("java.lang.Integer")){
                        typeVal = Integer.parseInt((String) val);
                    }else if(type.equals("boolean") || type.equals("java.lang.Boolean")) {
                        typeVal = (Boolean) val;
                    }else if(type.equals("java.lang.String")){
                        typeVal = val;
                    }
                    //反射对属性进行赋值
                    field.setAccessible(true);
                    field.set(object, typeVal);
                    return object;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();

        }
        return clazz;
    }

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2022-05-05 11:49:55  更:2022-05-05 11:51:05 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/17 22:15:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码