导入导出sql脚本文件

业务场景

在复杂系统中,有时候去做数据同步我们需要一次导出导入十几张表甚至更多表的数据,这些数据在不同数据库,也可能分布不同微服务中,三个四个甚至更多,如果使用excel去处理会很复杂,任务复杂度很高,这时候我们就可以使用导出sql脚本文件,然后去其他环境导入sql脚本文件。

使用sql文件导入导出安全吗?

答案是,如果你不做任何安全处理,这样方式是及其不安全的,存在严重的安全隐患,那么我们如何去消除这些隐患呢?这就需要我们对我们导入的sql脚本文件进行完整性校验,即导入和导出的文件内容不允许改变,那么如何做呢?

文件的完整性如何校验?

文件的完整性校验,方式有很多,最常见的摘要算法MD5,假如我们使用MD5算法,我们可以首先对生成的sql原文件进行MD5,然后对MD5值进行AES加密,由于AES秘钥保存在服务端只有我们自己知道,这样即使别人拿到导出的文件而且又知道了我们的幂等性算法的规则,由于其不知道我们私钥,也无法篡改出合规的数据。我们可以把加密后的值作为文件名(加密前使用固定名称,如:export.sql),或者把加密后的值添加到文件的结尾(这时校验的时候需要注意先把追加到文件结尾的幂等值移除,再去做文件完整性校验)。

如何导入导出sql文件?

  1. 首先,需要梳理出N多张表的查询sql,如果是 覆盖更新还需要梳理出N多张表的删除sql;
  2. 遍历查询sql,将每条sql的查询结果转换成insert语句,再组装到一起,得到sql集合;
  3. 将删除sql和insert sql 输入到export.sql中,如果是不同数据库的数据,可以使用use XXdb; 去切换数据库;
  4. 对生成的export.sql做摘要算法然后对其值进行二次加密;
  5. 将二次加密后的值,作为文件名,或者将其追加到文件结尾;
  6. 导入时,首先要对sql文件的完整性进行校验;
  7. 然后直接执行sql文件,而不是逐条读取再插入。

关键代码如下:

通用查询Mapper

/*** 查询* @param sql* @return*/List<Map<String, Object>> selectBySql(String sql);
<select id="selectBySql" resultType="java.util.Map">${sql}
</select>

将查询sql结果转换成insert集合

/*** 生成插入sql语句 --依据查询sql* @param sql* @param db* @return*/
private List<String> getInsertSqlList(String sql, String db) {String tableName = getTableNameBySql(sql, db);List<Map<String, Object>> list = sqlMapper.selectBySql(sql);List<String> sqlList = new ArrayList<>();if(CollUtil.isEmpty(list)) {return sqlList;}for (Map<String, Object> map : list) {StringBuilder column = new StringBuilder();StringBuilder values = new StringBuilder();int size = map.size();int i = 0;for (Map.Entry<String, Object> entry : map.entrySet()) {if(i == 0) {column.append("( ");column.append(entry.getKey());column.append(", ");values.append("( ");values.append(format(entry.getValue()));values.append(", ");} else if (i < size -1) {column.append(entry.getKey());column.append(", ");values.append(format(entry.getValue()));values.append(", ");} else {column.append(entry.getKey());column.append(")");values.append(format(entry.getValue()));values.append(")");}i++;}String insertsSql = "insert into " + tableName + " " + column + " values " + values + ";";log.info("insertsSql: {}", insertsSql);sqlList.add(insertsSql);}return sqlList;
}

生成sql脚本文件

/*** 生产sql脚本文件* @param appId* @param appTypeId* @param userDomainId* @return*/
private String generateFile(Long id) {List<String> sqlList = new ArrayList<>();// 切换至db1库String db1Sql = "use " + db1 + ";";sqlList.add("\n");sqlList.add("-- 切换到" + db1Sql );sqlList.add(db1Sql);// 获取删除sql(删除quec_end_user库历史数据)sqlList.add("\n");sqlList.add("-- 删除老的数据 id=" + id);List<String> deleteSqlList = getDb1DeleteSqlList(id);sqlList.addAll(deleteSqlList);sqlList.add("\n");sqlList.add("-- 插入新的数据 id=" + id);List<String> selectSql1List = getDbSqlList(id);for (String sql : selectSql1List) {List<String> list = getInsertSqlList(sql, null);if(CollUtil.isNotEmpty(list)) {sqlList.addAll(list);}}// 切换至db2库String db2Sql = "use " + db2Db + ";";sqlList.add("\n");sqlList.add("-- 切换到" + db2Db);sqlList.add(northSql);sqlList.add("\n");sqlList.add("-- 删除老的数据 id=" + id);deleteSqlList = getDb2DeleteSqlList(id);sqlList.addAll(deleteSqlList);sqlList.add("\n");sqlList.add("-- 插入新的数据 id=" + id);List<String> selectSql2List = getDb2SqlList(id);for (String sql : selectSql2List) {List<String> list = getInsertSqlList(sql, db2Db);if(CollUtil.isNotEmpty(list)) {sqlList.addAll(list);}}try {String path = System.getProperty("user.dir") + filePath + "export_" + appId + ".sql";log.info("path: {}", path);export(sqlList, path);return path;} catch (IOException e) {log.error("导出sql文件异常,appId = {}", appId);}return "";
}
private void export(List<String> sqlList, String filePath) throws IOException {// 写入文件FileWriter writer = new FileWriter(filePath);for (String sql: sqlList) {writer.write(sql + "\n");}writer.close();
}
/*** 获取表名* @param sql* @param db* @return*/public static String getTableNameBySql(String sql, String db) {if(StrUtil.isEmpty(sql)) {return null;}sql = sql.toLowerCase();String patternStr = "from\\s+([\\w\\.]*)";Pattern pattern = Pattern.compile(patternStr, Pattern.CASE_INSENSITIVE);Matcher matcher = pattern.matcher(sql);if (matcher.find()) {String name = matcher.group(1);if(StrUtil.isNotEmpty(db) && StrUtil.isNotEmpty(name)) {db = db.toLowerCase();String target = db + ".";name = name.replace(target, "");}return name;}return null;}
/*** XXXXXX --查询sql* @param appId* @return*/
private List<String> getDb1SqlList(Long id) {List<String> sqlList = new ArrayList<>();// termsString terms_sql = "select * from  "  + db1 + ".terms where app_id = %d and is_delete = 0";sqlList.add(String.format(terms_sql, id));// terms_fileString terms_file_sql = "select * from "  + db1 + ".terms_file where tos_id in (select id from "  + db1 + ".terms where id = %d and is_delete = 0)";sqlList.add(String.format(terms_file_sql , id));return sqlList;
}
/*** 字段值格式化* @param obj* @return*/
private Object format(Object obj) {if(obj.getClass().equals(String.class)) {return "'" + obj + "'";}if(obj.getClass().equals(LocalDateTime.class)) {LocalDateTime localDateTime = (LocalDateTime) obj;Date date = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());return  "'" + DateUtil.formatDateTime(date) + "'";}return obj;
}
/*** 执行sql脚本* @param path* @throws SQLException*/
private void executeSqlScript(String path) throws SQLException {ResourceDatabasePopulator resourceDatabasePopulator = new ResourceDatabasePopulator();resourceDatabasePopulator.addScript(new ClassPathResource(path));resourceDatabasePopulator.execute(dataSource);
}

注意:这个path相对于/src/ain/resources目录,如:/src/ain/resources/export.sql, path=“export.sql”.

导出的sql文件截图:
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

电脑出现错误代码0x00000709一键修复的几种方法

错误代码0x00000709通常在 Windows 系统中与打印机设置有关&#xff0c;特别是当尝试设置默认打印机时。这个问题可能是由于权限不足、注册表问题或系统文件损坏导致的。虽然没有官方的“一键修复”工具&#xff0c;但您可以通过以下步骤尝试解决此问题。 解决方案 1: 使用管理…

Linux LVM扩容方法

问题描述 VMware Centos环境&#xff0c;根分区为LVM&#xff0c;大小50G&#xff0c;现在需要对根分区扩容。我添加了一块500G的虚拟硬盘(/dev/sdb)&#xff0c;如何把这500G扩容到根分区&#xff1f; LVM扩容方法 1. 对新磁盘分区 使用fdisk /dev/sdb命令&#xff0c;进…

免费的ssh工具

1.Quickstart - kitty 2 Download Termius for Windows 3. MobaXterm Xserver with SSH, telnet, RDP, VNC and X11 - Download

24年Hvv准备,6大方向,33篇技战法

进去不少小伙伴后台留言说需要技战法&#xff0c;因此小编对市面上的技战法进行了收集和总结&#xff0c;并对收集来的技战法进行了分类&#xff0c;总共分了6大类&#xff0c;共计33篇&#xff1a; 有需要的小伙伴关注我&#xff0c;点击在看&#xff0c;并私信回复“技战法”…

无缝集成第三方应用,加速数据驱动决策

摘要&#xff1a; 在数据为王的时代&#xff0c;快速准确地整合并分析来自不同源头的数据成为了企业制胜的关键。本文将深入探讨如何通过Python编程语言&#xff0c;无缝集成第三方应用&#xff0c;构建高效的数据采集与分析流程&#xff0c;从而加速企业的数据驱动决策过程。…

C语言学习笔记[25]:循环语句for

for循环 for循环的基本语法 for(表达式1;表达式2;表达式3)循环语句; 表达式1为初始化部分&#xff0c;用于初始化循环变量的。 表达式2为条件判断部分&#xff0c;用于判断循环何时终止。 表达式3为调整部分&#xff0c;用于循环条件的调整。 例如用for循环实现打印1~10的数字…

spring事件发布器ApplicationEventPublisher的使用

1、前言 spring中有一个事件发布器,使用了观察者模式,当有事件发布的时候,事件监听者会立刻收到发布的事件。今天我们来介绍下这个事件发布器怎么使用。 2、简单使用 2.1、创建事件实体类 事件实体类需要继承ApplicationEvent。我们模拟老师发布事件的诉求。 public class T…

【51项目】基于51单片机protues交通灯的设计(完整资料源码)

基于51单片机protues交通灯的设计 一、 项目背景 1.1背景 随着科技的不断发展&#xff0c;LED技术在交通领域的应用越来越广泛。LED模拟交通灯作为一种新型的交通信号控制设备&#xff0c;以其高效、节能、环保等优点&#xff0c;逐渐取代了传统的交通信号灯。近年来&#xff…

【jenkins+cmake+svn管理c++项目】msbuild: command not found

一、前言 jenkins中配置cmakeVS的编译构建过程&#xff0c;需要用到MSBuild这个工具来完成VS工作&#xff0c;MSBuild的安装配置方法见&#xff1a;windows编译环境和工具配置 MSBuildCMAKE的编译可以用脚本来完成&#xff0c;我在jenkins的构建步骤中添加了一个ExecuteShell…

【人工智能】Transformers之Pipeline(三):文本转音频(text-to-audio/text-to-speech)

​​​​​​​ 一、引言 pipeline&#xff08;管道&#xff09;是huggingface transformers库中一种极简方式使用大模型推理的抽象&#xff0c;将所有大模型分为音频&#xff08;Audio&#xff09;、计算机视觉&#xff08;Computer vision&#xff09;、自然语言处理&#x…

数据库如何简单入手学习

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 作为一个后端开发人员&#xff0c;应该没有不接触数据库的&#xff0c;数据库操作优化也是后端面试人员的重点面…

2024辽宁省数学建模B题【钢铁产品质量优化】原创论文分享

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了2024 年辽宁省大学数学建模竞赛B题钢铁产品质量优化完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成品论文。 B题论文…

Visual Studio 智能代码插件:Fitten Code

Fitten Code 是由非十大模型驱动的AI编程助手&#xff0c;它可以自动生成代码&#xff0c;提升开发效率&#xff0c;协助调试 Bug&#xff0c;节省时间。还可以对话聊天&#xff0c;解决编程碰到的问题。 Fitten Code 免费且多种编程语言&#xff0c;包括 Python、C、Javascri…

在VS2022中通过Nuget将vcpkg环境集成/卸载到c++项目

在VS2022中通过Nuget将vcpkg环境集成/卸载到c项目 vcpkg是微软和C社区维护的免费开源C/C包管理器。利用它&#xff0c;可以一条命令编译安装用户所需的库&#xff1b;提供CMake配置文件&#xff1b;并且对于Windows开发者&#xff0c;在Visual Studio中集成后还可以自动链接静…

鸿蒙仓颉语言【类型class】

类与结构&#xff08;class & struct&#xff09; 面向对象的编程语言&#xff0c;必不可少的基础元素&#xff0c;类或者叫类型&#xff0c;在仓颉中类可以抽象(abstract)、继承&#xff08;<:&#xff09;&#xff0c;公开&#xff08;Public&#xff09;或者私有&am…

redis数据库(下)

集合键值对 集合的每一个元素也是字符串格式数据,是无序集合,并且元素不可重复(自动去重) 1.集合的创建和添加命令 sadd命令:无责创建有责添加 sadd 键名 元素1 元素2......... 注意:再次添加元素时,如果触发了集合的唯一性,那么命令执行结果就为0,表示执行失败…

windows edge自带的pdf分割工具(功能)

WPS分割pdf得会员&#xff0c;要充值&#xff01;网上一顿乱找&#xff0c;发现最简单&#xff0c;最好用&#xff0c;免费的还是回到Windows。 Windows上直接在edge浏览器打开PDF&#xff0c;点击 打印 按钮,页面下选择对应页数 打印机 选择 另存为PDF&#xff0c;然后保存就…

CE入门教程

【半小时搞懂《CE官方教程》2—9关】https://www.bilibili.com/video/BV1et4y1J75o?vd_source7ad69e0c2be65c96d9584e19b0202113 CE修改器使用教程 [入门篇] - lyshark - 博客园 (cnblogs.com) 第一关 附加进程 第二关 静态地址的值更改 就是找到数值,更改 首次扫描 SM他,变…

LeetCode-随机链表的复制

. - 力扣&#xff08;LeetCode&#xff09; 本题思路&#xff1a; 首先注意到随机链表含有random的指针&#xff0c;这个random指针指向是随机的&#xff1b;先一个一个节点的拷贝&#xff0c;并且把拷贝的节点放在拷贝对象的后面&#xff0c;再让拷贝节点的next指向原链表拷贝…

基于QEMU-aarch64学习UEFI(EDK2)-8QEMU固件变量存储

1 基于QEMU-aarch64学习UEFI(EDK2)-8QEMU固件变量存储 文章目录 1 基于QEMU-aarch64学习UEFI(EDK2)-8QEMU固件变量存储1.1 fd固件分析1.2 QEMU_VARS.fd分析1.2.1 QEMU_VARS.fd与QEMU_EFI.fd二合一1.2.2 错误分析1.2.2.1 gEdkiiNvVarStoreFormattedGuid1.2.2.2 SupportFvb1.2.2.…