RuoYi/ExcelUtil修改(导入excel表时,表中字段没有映射上数据库表字段)

RuoYi/ExcelUtil修改

描述

使用ruoyi框架时,需要使用到导入、导出功能,涉及到ExcelUtil工具类,但是在导入具体实现中,遇到问题
具体问题:

  1. 实体类字段,对应数据库表。
  2. 添加完了注解,没有使用注解中的readConverterExp@Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知"),readConverterExp转换不正确也会导致问题)
  3. 但是在导时老是,表格中有的列有值,但是导入到数据库表中时却没有值

原因

  1. 排除实体类字段值与数据库值不一致
  2. 排除注解问题
  3. 最后想到,第二步中注解中readConverterExp转换不正确会导致表有值但是数据库没有值,是不是数据库的列名读取时又问题(和看到的不一致,导致在工具类中读取到的和注解中的name对不上),于是将表格中的值copy出来,发现问题:注解name中的值是"xx",但是表格中是"xx "或者" xx" (表格列太多了很多缩在一起,并且只是一个空格,看不出来)

解决

发现原因:表格中的列值和注解name对不上,可能会有差距(空格等)

  1. 在系统中设计一个功能,到处具体的规范模版下载给用户使用,再用该模版导入数据
  2. 在代码中处理,想办法在读取时,去除掉前后空格
    我的需求是,导入数据,分析数据,不可能给人家一个模版用,所以采用修改代码

具体代码:修改ExcelUtil中的importExcel方法

java 复制代码
 /**
     * 对excel表单指定表格索引名转换成list
     *
     * @param sheetName 表格索引名
     * @param titleNum 标题占用行数
     * @param is 输入流
     * @return 转换后集合
     */
    public List<T> importExcel(String sheetName, InputStream is, int titleNum) throws Exception
      {
        this.type = Type.IMPORT;
        this.wb = WorkbookFactory.create(is);
        List<T> list = new ArrayList<T>();
        // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet
        Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0);
        if (sheet == null)
        {
            throw new IOException("文件sheet不存在");
        }
        boolean isXSSFWorkbook = !(wb instanceof HSSFWorkbook);
        Map<String, List<PictureData>> pictures = null;
        if (isXSSFWorkbook)
        {
            pictures = getSheetPictures07((XSSFSheet) sheet, (XSSFWorkbook) wb);
        }
        else
        {
            pictures = getSheetPictures03((HSSFSheet) sheet, (HSSFWorkbook) wb);
        }
        // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1
        int rows = sheet.getLastRowNum();
        if (rows > 0)
        {
            // 定义一个map用于存放excel列的序号和field.
            Map<String, Integer> cellMap = new HashMap<String, Integer>();
            // 获取表头
            Row heard = sheet.getRow(titleNum);
            for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++)
            {
                Cell cell = heard.getCell(i);
                if (StringUtils.isNotNull(cell))
                {
                    String value = this.getCellValue(heard, i).toString();
                    //修改点1
                    // 去除表头值前后空格,提高匹配容错性
                    String trimmedValue = StringUtils.trim(value);
                    cellMap.put(trimmedValue, i);
                }
                else
                {
                    cellMap.put(null, i);
                }
            }
            // 有数据时才处理 得到类的所有field.
            List<Object[]> fields = this.getFields();
            Map<Integer, Object[]> fieldsMap = new HashMap<Integer, Object[]>();
            for (Object[] objects : fields)
            {
            	//修改点2
                Excel attr = (Excel) objects[1];
                String fieldName = attr.name();
                Integer column = cellMap.get(fieldName);
                // 如果直接匹配失败,尝试处理表头中的特殊情况
                if (column == null && cellMap.size() > 0)
                {
                    // 遍历cellMap查找可能匹配的表头(如包含空格的情况)
                    for (Map.Entry<String, Integer> entry : cellMap.entrySet())
                    {
                        String headerName = entry.getKey();
                        if (headerName != null && StringUtils.trim(headerName).equals(fieldName))
                        {
                            column = entry.getValue();
                            break;
                        }
                    }
                }
                if (column != null)
                {
                    fieldsMap.put(column, objects);
                }
            }
            for (int i = titleNum + 1; i <= rows; i++)
            {
                // 从第2行开始取数据,默认第一行是表头.
                Row row = sheet.getRow(i);
                // 判断当前行是否是空行
                if (isRowEmpty(row))
                {
                    continue;
                }
                T entity = null;
                for (Map.Entry<Integer, Object[]> entry : fieldsMap.entrySet())
                {
                    Object val = this.getCellValue(row, entry.getKey());

                    // 如果不存在实例则新建.
                    entity = (entity == null ? clazz.newInstance() : entity);
                    // 从map中得到对应列的field.
                    Field field = (Field) entry.getValue()[0];
                    Excel attr = (Excel) entry.getValue()[1];
                    // 取得类型,并根据对象类型设置值.
                    Class<?> fieldType = field.getType();
                    if (String.class == fieldType)
                    {
                        String s = Convert.toStr(val);
                        if (s.matches("^\\d+\\.0$"))
                        {
                            val = StringUtils.substringBefore(s, ".0");
                        }
                        else
                        {
                            String dateFormat = field.getAnnotation(Excel.class).dateFormat();
                            if (StringUtils.isNotEmpty(dateFormat))
                            {
                                val = parseDateToStr(dateFormat, val);
                            }
                            else
                            {
                                val = Convert.toStr(val);
                            }
                        }
                    }
                    else if ((Integer.TYPE == fieldType || Integer.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val)))
                    {
                        val = Convert.toInt(val);
                    }
                    else if ((Long.TYPE == fieldType || Long.class == fieldType) && StringUtils.isNumeric(Convert.toStr(val)))
                    {
                        val = Convert.toLong(val);
                    }
                    else if (Double.TYPE == fieldType || Double.class == fieldType)
                    {
                        val = Convert.toDouble(val);
                    }
                    else if (Float.TYPE == fieldType || Float.class == fieldType)
                    {
                        val = Convert.toFloat(val);
                    }
                    else if (BigDecimal.class == fieldType)
                    {
                        val = Convert.toBigDecimal(val);
                    }
                    else if (Date.class == fieldType)
                    {
                        if (val instanceof String)
                        {
                            val = DateUtils.parseDate(val);
                        }
                        else if (val instanceof Double)
                        {
                            val = DateUtil.getJavaDate((Double) val);
                        }
                    }
                    else if (Boolean.TYPE == fieldType || Boolean.class == fieldType)
                    {
                        val = Convert.toBool(val, false);
                    }
                    if (StringUtils.isNotNull(fieldType))
                    {
                        String propertyName = field.getName();
                        if (StringUtils.isNotEmpty(attr.targetAttr()))
                        {
                            propertyName = field.getName() + "." + attr.targetAttr();
                        }
                        if (StringUtils.isNotEmpty(attr.readConverterExp()))
                        {
                            val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());
                        }
                        else if (StringUtils.isNotEmpty(attr.dictType()))
                        {
                            if (!sysDictMap.containsKey(attr.dictType() + val))
                            {
                                String dictValue = reverseDictByExp(Convert.toStr(val), attr.dictType(), attr.separator());
                                sysDictMap.put(attr.dictType() + val, dictValue);
                            }
                            val = sysDictMap.get(attr.dictType() + val);
                        }
                        else if (!attr.handler().equals(ExcelHandlerAdapter.class))
                        {
                            val = dataFormatHandlerAdapter(val, attr, null);
                        }
                        else if (ColumnType.IMAGE == attr.cellType() && StringUtils.isNotEmpty(pictures))
                        {
                            StringBuilder propertyString = new StringBuilder();
                            List<PictureData> images = pictures.get(row.getRowNum() + "_" + entry.getKey());
                            for (PictureData picture : images)
                            {
                                byte[] data = picture.getData();
                                String fileName = FileUtils.writeImportBytes(data);
                                propertyString.append(fileName).append(SEPARATOR);
                            }
                            val = StringUtils.stripEnd(propertyString.toString(), SEPARATOR);
                        }
                        ReflectUtils.invokeSetter(entity, propertyName, val);
                    }
                }
                list.add(entity);
            }
        }
        return list;
    }

//修改点1

在获取Excel表头值并存入映射表时,增加了trim()操作,自动去除表头值前后的空格,确保表头名称标准化。

//修改点2

增强了字段映射逻辑,如果直接匹配失败,会遍历所有表头进行容错处理,通过trim()后比较表头名称,实现了对带空格表头的智能匹配。

这两处修改共同作用,使得即使Excel表格中的表头前后包含空格(如"工单编号 "),也能正确匹配到实体类中定义的@Excel(name = "工单编号")注解的字段,有效提高了Excel导入功能的健壮性和用户体验。

仓库地址

不清楚可以看仓库,dev分支下提交

相关推荐
十五年专注C++开发3 小时前
Drogon: 一个开源的C++高性能Web框架
linux·c++·windows·后端开发·服务器开发
m0_674031433 小时前
GitHub等平台形成的开源文化正在重塑林语堂
windows·mysql·spring
李小白663 小时前
Redis常见指令
数据库·redis·缓存
搬砖的小码农_Sky4 小时前
如何从Windows 操作系统登录Linux(Ubuntu)操作系统
linux·windows·ubuntu·远程工作
weixin_438077494 小时前
langchain_neo4j 以及 neo4j (windows-community) 的学习使用
windows·langchain·neo4j
雨奔4 小时前
Flask 学习路线图
数据库·学习·flask
TDengine (老段)4 小时前
从“事后抢险”到“事前防控”:江西水投用 TDengine 时序数据库重塑防汛模式
大数据·数据库·物联网·时序数据库·tdengine·涛思数据·1024程序员节
友友马5 小时前
『 QT 』QT控件属性全解析 (二)
开发语言·数据库·qt
像风一样!10 小时前
MySQL Galera Cluster部署如何实现负载均衡和高可用
数据库·mysql