excel转sql小工具

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 Tair(兼容Redis),内存型 2GB
简介: 该工具用于将Excel数据转换为SQL INSERT语句,便于历史数据迁移到新数据库。通过配置文件定义Excel表头与数据库字段的映射关系,并支持默认值设置及spEL表达式。主要依赖包括EasyExcel读取Excel,以及Lombok、Hutool等辅助工具。项目包含`Excel2SqlUtils.java`和`Excel2SqlListener.java`两个核心类,前者负责加载配置文件,后者实现数据读取与SQL语句生成。配置文件`model.yml`定义了具体的映射规则。

一、说明

  1. 公司要迁移历史数据到另一个数据库中,历史数据只能由Excel导出提供。因此写了这个小工具用于将Excel中的数据拼接为INSERT语句,用于项目初始化时一次性导入。
  2. Excel表头中的字段与表中字段映射关系由配置文件进行匹配,支持spEL表达式。

二、相关代码

  1. 主要的maven依赖

xml

代码解读

复制代码

        <!-- easyexcel框架,用于读取excel中的数据 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.10</version>
        </dependency>
        <!-- lombok减少代码量,业务逻辑中没有使用,可不用 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- hutool一些静态工具,业务逻辑中没有使用,可不用 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.6.5</version>
        </dependency>
  1. Excel2SqlUtils.java //功能入口,包含main函数

java

代码解读

复制代码

import com.alibaba.excel.EasyExcel;
import com.xxx.xx.admin.xx.manager.listener.Excel2SqlListener;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.List;
import java.util.Objects;


@Data
public class Excel2SqlUtils {
    private String configYamlPath;
    private String db;
    private String table;
    private List<Entry> entryList;

    @Data
    public static class Entry {
        private String colName;
        private String sqlField;
        private String defaultValue;
    }

    public static void main(String[] args) throws FileNotFoundException {

        // 历史数据路径,必须是excel文件。可以填写绝对路径
        String excelPath = "doc/config/file/model.xlsx";
        // 配置文件路径,excel表头和数据库字段映射关系。可以填写绝对路径
        String configPath = "doc/config/model.yml";
        // 生成的sql文件路径。可以填写绝对路径
        String outPath = "doc/db/model.sql";

        EasyExcel.read(excelPath, new Excel2SqlListener(configPath, outPath)).sheet().doRead();

    }


    /**
     * 解析配置文件,获取映射关系
     * @param configYamlPath 配置文件路径
     */
    public static Excel2SqlUtils load(String configYamlPath) throws FileNotFoundException {
        Objects.requireNonNull(configYamlPath, "config yaml path can not be empty");
        Yaml yaml = new Yaml(new Constructor(Excel2SqlUtils.class));
        InputStream in = new FileInputStream(configYamlPath);
        Excel2SqlUtils csvMapConfig = yaml.load(in);
        Objects.requireNonNull(csvMapConfig, "yaml load error");
        Objects.requireNonNull(csvMapConfig.getEntryList(), "config entryList can not be empty");
        csvMapConfig.getEntryList().forEach(entry -> {
            Objects.requireNonNull(entry.getSqlField(), "the sql field of entryList item can not be null");
        });
        csvMapConfig.setConfigYamlPath(configYamlPath);
        return csvMapConfig;
    }

    /**
     * 获取默认值、置换spEL表达式的值
     * @param defaultValue 默认值
     */
    public static String getDefaultValue(String defaultValue) {
        if (StringUtils.isBlank(defaultValue)) {
            return "null";
        }

        if ((defaultValue).startsWith("${")) {
            // 创建 SpEL 表达式解析器
            ExpressionParser parser = new SpelExpressionParser();
            defaultValue = defaultValue.replace("${", "").replace("}", "");
            Expression expression = parser.parseExpression(defaultValue);
            return String.valueOf(expression.getValue());
        }
        return defaultValue;
    }
}
  1. Excel2SqlListener.java // 拼接的主要业务逻辑,这里用了easyexcel包

java

代码解读

复制代码

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.xxx.xx.admin.util.Excel2SqlUtils;
import lombok.SneakyThrows;

import java.io.FileWriter;
import java.io.IOException;
import java.util.*;

/**
 * 解析Excel文件,拼接成sql语句
 */
public class Excel2SqlListener extends AnalysisEventListener<Map<Integer, String>> {
    private static final int BATCH_COUNT = 1000;
    List<Map<Integer, String>> list = new ArrayList<>();
    String configPath;
    private Excel2SqlUtils csvMapConfig;
    Map<String, Integer> excelHeadMap = new HashMap<>();
    // 配置文件中的字段映射
    Map<String, Excel2SqlUtils.Entry> col2Entry = new LinkedHashMap<>();
    StringBuilder sqlHeadBuilder = new StringBuilder();
    StringBuilder sqlValueBuilder = new StringBuilder();
    String outPath;
    FileWriter fileWriter;

    public Excel2SqlListener(String configPath, String outPath) {
        this.configPath = configPath;
        this.outPath = outPath;
    }

    /**
     * 获取表头,读取excel数据之前执行一次
     * @param headMap 表头信息
     * @param context 上下文
     */
    @SneakyThrows
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        // 表头封装
        for (Map.Entry<Integer, String> entry : headMap.entrySet()) {
            String key = entry.getValue();
            Integer value = entry.getKey();
            excelHeadMap.put(key, value);
        }

        // 获取配置文件中的字段映射
        this.csvMapConfig = Excel2SqlUtils.load(configPath);

        // 输出文件流
        fileWriter = new FileWriter(outPath);
        String path = csvMapConfig.getConfigYamlPath();
        fileWriter.write("-- 初始化 " + path.split("/")[2].split("\\.")[0] + " 数据");

        // 配置文件中的字段映射关系封装
        for (Excel2SqlUtils.Entry entry : csvMapConfig.getEntryList()) {
            col2Entry.put(entry.getColName(), entry);
        }

        // 拼接INSERT INTO语句前部分
        sqlHeadBuilder.append("INSERT INTO ")
                // 数据库名,是否添加看业务逻辑
//                .append(sqlKeywordWrap(dbName)).append(".")
                .append(sqlKeywordWrap(this.csvMapConfig.getTable()));
        sqlHeadBuilder.append(" (");
        for (Excel2SqlUtils.Entry fieldEntry : col2Entry.values()) {
            sqlHeadBuilder.append(sqlKeywordWrap(fieldEntry.getSqlField()));
            sqlHeadBuilder.append(",");
        }
        sqlHeadBuilder.deleteCharAt(sqlHeadBuilder.length() - 1);
        sqlHeadBuilder.append(") VALUES");
    }

    /**
     * 按照行读取excel数据,每行执行一次
     * @param data 每行数据
     * @param context 上下文
     */
    @Override
    public void invoke(Map<Integer, String> data, AnalysisContext context) {
        sqlValueBuilder.append("\n (");

        // 根据配置文件中的字段,去获取excel文档中的值
        for (Map.Entry<String, Excel2SqlUtils.Entry> configEntry : col2Entry.entrySet()) {
            String keyConfig = configEntry.getKey();
            Excel2SqlUtils.Entry valueConfig = configEntry.getValue();

            // excel文档中的字段值
            String valueExcel = data.get(excelHeadMap.get(keyConfig));

            // 配置文档中的默认值
            if (valueExcel == null) {
                valueExcel = Excel2SqlUtils.getDefaultValue(valueConfig.getDefaultValue());
            } else if (valueExcel.contains("'")) {
                // 如果字符串中包含单引号,要进行转义
                valueExcel = valueExcel.replace("'", "''");
            }

            sqlValueBuilder.append(sqlValWrap(valueExcel));
            sqlValueBuilder.append(",");
        }
        sqlValueBuilder.deleteCharAt(sqlValueBuilder.length() - 1);
        sqlValueBuilder.append("),");

        // 批量INSERT,一千条封装为一组
        list.add(data);
        if (list.size() >= BATCH_COUNT) {
            saveData();
            list.clear();
            sqlValueBuilder.setLength(0);
        }
    }

    /**
     * 读取excel数据结束执行一次
     * @param context 上下文
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 防止最后一批数据不满1000条,需要单独处理
        saveData();
        System.out.println("所有数据解析完成!解析个数为:" + context.readRowHolder().getRowIndex());
        System.out.println("所有数据解析完成!行号为:" + (context.readRowHolder().getRowIndex() + 1));
        try {
            fileWriter.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static String sqlValWrap(String val) {
        return "'" + val + "'";
    }

    private static String sqlKeywordWrap(String keyword) {
        return '`' + keyword.trim() + '`';
    }

    /**
     * 输出
     */
    private void saveData() {
        StringBuilder sqlBuilder = new StringBuilder();
        sqlValueBuilder.deleteCharAt(sqlValueBuilder.length() - 1);
        sqlValueBuilder.append(";");

        sqlBuilder.append(sqlHeadBuilder);
        sqlBuilder.append(sqlValueBuilder);

        try {
            fileWriter.write("\n");
            fileWriter.write(String.valueOf(sqlBuilder));
            fileWriter.write("\n");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
  1. model.yml // 配置文件,配置excel表头和表字段的映射关系

yml

代码解读

复制代码

# 数据库名
db: cs_linux
# 表名
table: cs_user
# 数据库字段配置
entryList:
  # excel中的表头列名
  - colName: 职员工号
    # 数据库字段名,不能为空
    sqlField: personnel_id
  - colName: 部门代码
    sqlField: dept_id
  - colName: 工作类型代码
    sqlField: work_type_cd
  - colName: 创建人
    sqlField: created_by
    defaultValue: 'system'
  - colName: 创建时间
    sqlField: created_at
#    默认值,当表中数据为空,或者字段不存在时,填入默认值。支持spEl表达式
    defaultValue: "${T(cn.hutool.core.date.DateUtil).now()}"
  - colName: 修改人
    sqlField: updated_by
    defaultValue: 'system'
  - colName: 修改日期
    sqlField: updated_at
    defaultValue: "${T(cn.hutool.core.date.DateUtil).now()}"
  - colName: UID
    sqlField: uuid
    defaultValue: "${T(java.util.UUID).randomUUID().toString()}"

三、注意

  1. 我这边的需求只是初始化执行一次,因此没有写很复杂的功能。可以根据自身需求进行修改。


转载来源:https://juejin.cn/post/7396930610624954406

相关文章
|
6月前
|
SQL 数据库连接 数据库
【SQL Server】2. 将数据导入导出到Excel表格当中
【SQL Server】2. 将数据导入导出到Excel表格当中
145 0
|
6月前
|
SQL 数据可视化 数据处理
使用SQL和Python处理Excel文件数据
使用SQL和Python处理Excel文件数据
172 0
|
5月前
|
SQL 存储 数据库
excel导入sql数据库
将Excel数据导入SQL数据库是一个相对常见的任务,可以通过多种方法来实现。以下是一些常用的方法: ### 使用SQL Server Management Studio (SSMS) 1
|
4月前
|
存储 关系型数据库 MySQL
Excel 导入 sql3
【7月更文挑战第18天】
43 2
|
5月前
|
SQL 存储 数据可视化
excel表格sql数据库
Excel表格和SQL数据库是两种不同的数据管理工具,它们各自有自己的特点和用途。下面我将分别介绍它们,并探讨它们之间的关系和互操作性。 一、Excel表格 Excel是微软公司推出的一款
|
SQL 数据采集 数据挖掘
像Excel一样使用SQL进行数据分析(下)
Excel是数据分析中最常用的工具 ,利用Excel可以完成数据清洗,预处理,以及最常见的数据分类,数据筛选,分类汇总,以及数据透视等操作,而这些操作用SQL一样可以实现。
|
SQL Oracle 关系型数据库
根据Excel接口文档生成不同类型的数据创建表的sql
根据Excel接口文档生成不同类型的数据创建表的sql
|
SQL 数据库
Excel导入SQL数据库的相关问题
Excel导入SQL数据库的相关问题
Excel导入SQL数据库的相关问题
|
SQL Python
python读取错误excel记录生成sql,避免手工维护数据
python读取错误excel记录生成sql,避免手工维护数据
112 0
python读取错误excel记录生成sql,避免手工维护数据
|
SQL 存储 数据挖掘
Python批量处理Excel数据后,导入SQL Server
Python批量处理Excel数据后,导入SQL Server
459 0
Python批量处理Excel数据后,导入SQL Server

热门文章

最新文章

  • 1
    非阻塞 I/O:异步编程提升 Python 应用速度
    2
  • 2
    2024 Mysql基础与进阶操作系列之MySQL触发器详解(20)作者——LJS[你个小黑子这都还学不会嘛?你是真爱粉嘛?真是的 ~;以后请别侮辱我家鸽鸽]
    4
  • 3
    2024 RedisAnd Mysql基础与进阶操作系列(16-1)作者——LJS[你个小黑子这都还学不会嘛?你是真爱粉嘛?真是的 ~;以后请别侮辱我家鸽鸽]
    4
  • 4
    2024Mysql And Redis基础与进阶操作系列(13)作者——LJS[你个小黑子这都还学不会嘛?你是真爱粉嘛?真是的 ~;以后请别侮辱我家鸽鸽]
    6
  • 5
    2024Mysql And Redis基础与进阶操作系列(10)作者——LJS[你个IKUN还学不会嘛?你是真爱粉嘛?真是的 ~;以后别侮辱我家鸽鸽]
    5
  • 6
    2024 RedisAnd Mysql基础与进阶操作系列(15)作者——LJS[你个小黑子这都还学不会嘛?你是真爱粉嘛?真是的 ~;以后请别侮辱我家鸽鸽]
    4
  • 7
    2024Mysql And Redis基础与进阶操作系列(9)作者——LJS[含MySQL存储过程之局部、系统变量、参数传递、流程控制-判断/case具体详步骤;注意点及常见报错问题所对应的解决方法]
    7
  • 8
    2024 Mysql基础与进阶操作系列之MySQL触发器详解(21)作者——LJS[你个小黑子这都还学不会嘛?你是真爱粉嘛?真是的 ~;以后请别侮辱我家鸽鸽]
    6
  • 9
    2024 RedisAnd Mysql基础与进阶操作系列(19)作者——LJS[你个小黑子这都还学不会嘛?你是真爱粉嘛?真是的 ~;以后请别侮辱我家鸽鸽]
    6
  • 10
    2024 RedisAnd Mysql基础与进阶操作系列(18)作者——LJS[你个小黑子这都还学不会嘛?你是真爱粉嘛?真是的 ~;以后请别侮辱我家鸽鸽]
    5