对比 MyBatis 批处理 BATCH 模式与 INSERT INTO ... SELECT ... UNION ALL 进行批量插入

前言

在开发中,我们经常需要批量插入大量数据。不同的批量插入方法有不同的优缺点,适用于不同的场景。本文将详细对比两种常见的批量插入方法:

  • MyBatis 的批处理模式。
  • 使用 INSERT INTO ... SELECT ... UNION ALL 进行批量插入。

MyBatis 批处理模式

实现方式

MyBatis 的批处理模式通过配置 SqlSessionTemplateSqlSessionFactoryExecutorTypeBATCH 来启用。以下是一个示例配置:

public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {return new SqlSessionTemplate(sqlSessionFactory, ExecutorType.BATCH);
}

优点

  1. 易于实现:只需配置 ExecutorType 即可启用批处理模式。
  2. 灵活:支持多种类型的 SQL 操作,包括插入、更新和删除,常规标准 SQL 不因数据库差异而使用不同的写法。
  3. 动态 SQL:支持 MyBatis 的动态 SQL 功能,可以根据条件生成复杂的 SQL 语句。

缺点

  1. 性能限制:虽然批处理可以减少网络往返次数,提高性能,但对于非常大的数据集,性能方面会有一定的影响。
  2. 内存占用:批处理过程中需要在内存中累积大量的数据,可能导致内存溢出。
  3. 数据库支持:批处理的效果取决于数据库驱动的支持程度,某些驱动可能不完全支持批处理。

示例代码

@Autowired
private SqlSessionTemplate sqlSessionTemplate;public void batchInsert(List<Item> items, int batchSize, int commitBatchCount) {try (SqlSession sqlSession = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH)) {ItemMapper mapper = sqlSession.getMapper(ItemMapper.class);int batchCount = 0;for (int i = 0; i < items.size(); i += batchSize) {int end = Math.min(i + batchSize, items.size());for (int j = i; j < end; j++) {mapper.insert(items.get(j));}sqlSession.flushStatements();batchCount++;if (commitBatchCount != -1 && batchCount % commitBatchCount == 0) {sqlSession.commit();batchCount = 0;}}if (batchCount > 0 || commitBatchCount == -1) {sqlSession.commit();}} catch (Exception e) {// 处理异常e.printStackTrace();}
}

解释

  • batchSize:控制每次批处理的条数,即每次调用 mapper.insert 方法的次数。
  • commitBatchCount:控制每执行几次批处理后提交一次事务。如果 commitBatchCount-1,则表示在所有数据插入完成后一次性提交事务。
  • flushStatements:每次处理完一批数据后,手动刷新批处理中的 SQL 语句,确保数据被发送到数据库。
  • commit:根据 commitBatchCount 的值决定何时提交事务。如果 commitBatchCount-1,则在所有数据插入完成后一次性提交事务。

使用 INSERT INTO ... SELECT ... UNION ALL

实现方式

使用 INSERT INTO ... SELECT ... UNION ALL 方法可以通过构建一个包含多个 UNION ALL 子句的 SQL 语句来一次性插入多条记录。以下是一个示例:

INSERT INTO table_name (column1, column2)
SELECT 'value1', 'value2'
UNION ALL
SELECT 'value3', 'value4'
UNION ALL
SELECT 'value5', 'value6';

优点

  1. 高性能:一次性插入多条记录,减少了数据库的 I/O 操作,提高了插入速度。
  2. 内存友好:不需要在内存中累积大量数据,减少了内存占用。毕竟它只是执行了一条字符串比较长的 SQL 语句而已。

缺点

  1. 复杂性:生成包含大量 UNION ALL 子句的 SQL 语句可能非常复杂,容易出错。且不同类型的数据库拼接 SQL 的语法可能有差异。
  2. SQL 限制:SQL 语句长度有限制,如果插入的数据量过大,可能会超过数据库的最大 SQL 长度限制。需要注意当前数据库的最大允许长度。
  3. 错误处理:一旦插入失败,很难定位具体的错误记录,因为所有的插入操作是在一条 SQL 语句中完成的。
  4. 灵活性差:只能用于插入操作,不适用于更新或删除操作。
  5. 动态 SQL 支持差:难以根据条件动态生成 SQL 语句,只适用比较纯粹的 INSERT 语句。

示例代码

MyBatis XML 文件示例

假设我们有一个 Item 对象,包含 column1column2 两个字段。

<!-- src/main/resources/mapper/ItemMapper.xml -->
<mapper namespace="com.example.mapper.ItemMapper"><!-- 插入单个记录的映射 --><insert id="insert" parameterType="com.example.model.Item">INSERT INTO table_name (column1, column2) VALUES (#{column1}, #{column2})</insert><!-- 批量插入记录的映射 --><insert id="batchInsertWithUnionAll" parameterType="java.util.List">INSERT INTO table_name (column1, column2)<foreach collection="list" item="item" separator="UNION ALL">SELECT #{item.column1}, #{item.column2}</foreach></insert></mapper>

Java 方法示例

@Autowired
private SqlSessionTemplate sqlSessionTemplate;public void batchInsertWithUnionAll(List<Item> items) {try (SqlSession sqlSession = sqlSessionTemplate.getSqlSessionFactory().openSession()) {ItemMapper mapper = sqlSession.getMapper(ItemMapper.class);mapper.batchInsertWithUnionAll(items);sqlSession.commit();} catch (Exception e) {// 处理异常e.printStackTrace();}
}

解释

  • <foreach> 标签:遍历传入的 List<Item> 列表,生成多个 SELECT 子句,每个子句对应一条记录。
  • separator="UNION ALL":指定每个子句之间用 UNION ALL 分隔。

性能测试

为了更好地理解这两种方法的性能差异,我们可以进行一些基准测试。以下是一个简单的测试示例:

@Autowired
private SqlSessionTemplate sqlSessionTemplate;@Test
public void testBatchInsertPerformance() {int itemCount = 10000;List<Item> items = generateItems(itemCount);// 测试 MyBatis 批处理模式long startTime = System.currentTimeMillis();batchInsert(items, 1000, 10); // 每10次批处理提交一次事务long endTime = System.currentTimeMillis();System.out.println("MyBatis 批处理模式耗时: " + (endTime - startTime) + " ms");// 清空表数据clearTable();// 测试 INSERT INTO ... SELECT ... UNION ALLstartTime = System.currentTimeMillis();batchInsertWithUnionAll(items);endTime = System.currentTimeMillis();System.out.println("INSERT INTO ... SELECT ... UNION ALL 耗时: " + (endTime - startTime) + " ms");
}private List<Item> generateItems(int count) {List<Item> items = new ArrayList<>(count);for (int i = 0; i < count; i++) {items.add(new Item("value1_" + i, "value2_" + i));}return items;
}private void clearTable() {sqlSessionTemplate.getSqlSessionFactory().openSession().getMapper(ItemMapper.class).truncateTable();
}

解释

  • generateItems 方法:生成指定数量的 Item 对象。
  • clearTable 方法:清空表中的数据,以便进行下一次测试。

总结

选择哪种批量插入方法取决于你的具体需求和应用场景:

  • MyBatis 批处理模式
    • 数据量适中:适用于数据量不是特别大,但需要频繁插入、更新或删除的场景。
    • 需要灵活的 SQL 操作:需要支持多种类型的 SQL 操作,如插入、更新和删除。
    • 需要细粒度的错误处理:需要在批处理完成后检查每个操作的结果,以便发现和处理错误。
  • INSERT INTO ... SELECT ... UNION ALL
    • 大数据量插入:适用于需要一次性插入大量数据的场景,尤其是数据量非常大时。
    • 性能要求高:对插入性能有较高要求,需要尽可能减少数据库的 I/O 操作。
    • 简单的插入操作:只涉及插入操作,不需要支持更新或删除。

至此本文主要内容已经结束。


补充

针对单纯 INSERT 的超大数量级场景,可以结合两种方式实现高效插入。你可以先使用 INSERT INTO … SELECT … UNION ALL 构建批量插入的 SQL 语句,然后在适当的时候提交事务。这样既可以利用 UNION ALL 的高性能,又可以通过控制提交频率来避免事务过大导致的问题。

下面是主要代码片段,可以按需选择并使用。

<mapper namespace="com.example.mapper.ItemMapper"><!-- 批量插入记录的映射 --><insert id="batchInsertWithUnionAll" parameterType="java.util.List">INSERT INTO table_name (column1, column2)<foreach collection="list" item="item" separator="UNION ALL">SELECT #{item.column1}, #{item.column2}</foreach></insert>
</mapper>
@Autowired
private SqlSessionTemplate sqlSessionTemplate;public void batchInsertCombined(List<Item> items, int batchSize, int commitBatchCount) {try (SqlSession sqlSession = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH)) {ItemMapper mapper = sqlSession.getMapper(ItemMapper.class);int batchCount = 0;for (int i = 0; i < items.size(); i += batchSize) {int end = Math.min(i + batchSize, items.size());List<Item> batchItems = items.subList(i, end);mapper.batchInsertWithUnionAll(batchItems);sqlSession.flushStatements();batchCount++;if (commitBatchCount != -1 && batchCount % commitBatchCount == 0) {sqlSession.commit();batchCount = 0;}}if (batchCount > 0 || commitBatchCount == -1) {sqlSession.commit();}} catch (Exception e) {// 处理异常e.printStackTrace();}
}

希望这篇文章对你有所帮助!如果有任何进一步的问题或需要更多细节,请随时告诉我。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue中路由缓存

vue中路由缓存 问题描述及截图解决思路关键代码及打印信息截图 问题描述及截图 在使用某一平台时发现当列表页码切换后点击某一卡片进入详情页后&#xff0c;再返回列表页时页面刷新了。这样用户每次看完详情回到列表页都得再重新输入自己的查询条件&#xff0c;或者切换分页到…

第N8周:使用Word2vec实现文本分类

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 本周任务&#xff1a; 结合Word2Vec文本内容预测文本标签 加载数据 import torch import torch.nn as nn import torchvision from torchvision import tra…

如何在 UniApp 中实现 iOS 版本更新检测

随着移动应用的不断发展&#xff0c;保持应用程序的更新是必不可少的&#xff0c;这样用户才能获得更好的体验。本文将帮助你在 UniApp 中实现 iOS 版的版本更新检测和提示&#xff0c;适合刚入行的小白。我们将分步骤进行说明&#xff0c;每一步所需的代码及其解释都会一一列出…

FreeRTOS之vTaskDelete实现分析

这里写自定义目录标题 1 函数接口1.1 函数接口1.2 函数参数简介 2 vTaskDelete的调用关系2.1 调用关系2.2 调用关系示意图 3 函数源码分析3.1 vTaskDelete3.2 uxListRemove 1 函数接口 1.1 函数接口 void vTaskDelete( TaskHandle_t xTaskToDelete )1.2 函数参数简介 TaskHa…

移动充储机器人“小奥”的多场景应用(上)

一、高速公路服务区应用 在高速公路服务区&#xff0c;新能源汽车的充电需求得到“小奥”机器人的及时响应。该机器人配备有储能电池和自动驾驶技术&#xff0c;能够迅速定位至指定充电点&#xff0c;为待充电的新能源汽车提供服务。得益于“小奥”的机动性&#xff0c;其服务…

C语言实例_5之根据输入年月日,计算属于该年的第几天

1. 题目 输入某年某月某日&#xff0c;判断这一天是这一年的第几天&#xff1f; 2. 分析 步骤1:得先判断年份是否是闰年&#xff0c;是的话&#xff0c;当月份大于3时&#xff0c;需多加一天&#xff1b; 步骤2:还需根据输入月份&#xff0c;判断输入天数是否合理&#xff0…

Semaphore 信号量

文章目录 基本概念工作原理Semaphore 与 ReentrantLockSemaphore常用场景1. 限制并发线程数&#xff08;最常见场景&#xff09;2. 公平模式的信号量&#xff08;保证按顺序访问资源&#xff09;3. 限制数据库连接数&#xff08;模拟数据库连接池&#xff09;4. 限制 API 请求次…

Redis 的代理类注入失败,连不上 redis

在测试 redis 是否成功连接时&#xff0c;发现 bean 没有被创建成功&#xff0c;导致报错 根据报错提示&#xff0c;需要我们添加依赖&#xff1a; <dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId>&l…

桌面怎么快速添加便签?适合桌面记事的便签小工具

在数字化时代&#xff0c;我们每天面对电脑处理大量任务&#xff0c;无论是工作计划、会议纪要还是个人生活琐事&#xff0c;都需要一个可靠的桌面记事工具来帮助我们记录和整理。因此&#xff0c;一款适合桌面使用的便签软件成为了我们不可或缺的助手。 敬业签就是这样一款功…

UE5 腿部IK 解决方案 footplacement

UE5系列文章目录 文章目录 UE5系列文章目录前言一、FootPlacement 是什么&#xff1f;二、具体实现 前言 在Unreal Engine 5 (UE5) 中&#xff0c;腿部IK&#xff08;Inverse Kinematics&#xff0c;逆向运动学&#xff09;是一个重要的动画技术&#xff0c;用于实现角色脚部准…

KLV6008固态继电器:高压应用的理想紧凑方案

在当今快节奏的电子领域&#xff0c;找到平衡性能、可靠性和安全性的组件至关重要。CRIA Semiconductor的KLV6008固态继电器(SSR)正是满足了这一要求。这款紧凑型继电器专为高压、低电流切换而设计&#xff0c;是适用于各种应用的多功能解决方案。 为什么选择KLV6008&#xff1…

如何在 React 项目中应用 TypeScript?应该注意那些点?结合实际项目示例及代码进行讲解!

在 React 项目中应用 TypeScript 是提升开发效率、增强代码可维护性和可读性的好方法。TypeScript 提供了静态类型检查、自动补全和代码提示等功能&#xff0c;这对于 React 开发者来说&#xff0c;能够帮助早期发现潜在的 bug&#xff0c;提高开发体验。 1. 项目初始化 在现…

解锁生成式AI的真实价值:衡量ROI的12步框架

在当今快速发展的技术环境中,生成式AI正逐渐成为企业创新和增长的重要驱动力。然而,随着数十亿美元的投资涌入生成式AI项目,一个严峻的问题浮出水面:如何衡量这些投资的回报(ROI)?本文将探讨生成式AI ROI衡量的挑战,并提供一个12步框架,帮助公司有效地评估和最大化其生…

【网络云计算】2024第48周-每日【2024/11/20】小测-理论题-计算机网络概述

文章目录 1、计算机常见的网络设备有哪些&#xff1f;2、进制换算3、写出你认为的如何才能学好网络知识4、写出你知道的网络相关的求职岗位有哪些&#xff1f; 【网络云计算】2024第48周-每日【2024/11/20】小测-理论题- 1、计算机常见的网络设备有哪些&#xff1f; 2、进制换…

在 Swift 中实现字符串分割问题:以字典中的单词构造句子

文章目录 前言摘要描述题解答案题解代码题解代码分析示例测试及结果时间复杂度空间复杂度总结 前言 本题由于没有合适答案为以往遗留问题&#xff0c;最近有时间将以往遗留问题一一完善。 LeetCode - #140 单词拆分 II 不积跬步&#xff0c;无以至千里&#xff1b;不积小流&…

HarmonyOs鸿蒙开发实战(21)=>组件间通信@ohos/liveeventbus

1.简介 LiveEventBus是一款消息总线&#xff0c;具有生命周期感知能力&#xff0c;支持Sticky&#xff0c;支持跨进程&#xff0c;支持跨APP发送消息。 2.下载安装 ohpm install ohos/liveeventbus 3.订阅&#xff0c;注册监听 4.发送事件 5. 完成 > 记得关注博主&#xff…

OpenCV和Qt坐标系不一致问题

“ OpenCV和QT坐标系导致绘图精度下降问题。” OpenCV和Qt常用的坐标系都是笛卡尔坐标系&#xff0c;但是细微处有些不同。 01 — OpenCV坐标系 OpenCV是图像处理库&#xff0c;是以图像像素为一个坐标位置&#xff0c;即一个像素对应一个坐标&#xff0c;所以其坐标系也叫图像…

单片机结合OpenCV

目录 一、引言 二、单片机结合 OpenCV 的优势 1. 图像识别与处理 2. 目标检测 3. 用户界面开发 4. Linux 在嵌入式系统中的作用 5. 多线程优势 6. 网络编程作用 7. 文件编程功能 三、OpenCV 在单片机上的实现难点 1. 处理能力限制 2. 通信与优化挑战 四、单片机如…

nohup java -jar supporterSys.jar --spring.profiles.active=prod

文章目录 1、ps -ef | grep java2、kill 13713、ps -ef | grep java4、nohup java -jar supporterSys.jar --spring.profiles.activeprod &5、ps -ef | grep java1. 启动方式进程 1371进程 19994 2. 主要区别3. 可能的原因4. 建议 1、ps -ef | grep java rootshipper:~# p…

Ubuntu上安装MySQL并且实现远程登录

目录 下载网络工具 查看网络连接 更新系统软件包&#xff1b; 安装mysql数据库 查看mysql数据库状态 以数字ip形式显示mysql的监听状态。&#xff08;默认监听端口是3306&#xff09; 查看安装mysql数据库时系统创建的目录信息。 根据查询到的系统用户名以及随机密码&a…