MyBatis(十):Mybatis 几种批量操作的对比
本章主要讲解几种批量处理的用法及对别,批量处理一般用法包含以下几种:
1)普通foreach处理(没循环一次执行一次与mysql服务器交互操作),实际上也是采用的ExecutorType.SIMPLE;
2)使用ExecutorType.BATCH批量处理方法;
3)拼接SQL,一次批量提交给Mysql多个插入语句到mysql服务器端,执行批量操作。
下边针对这几种方案分别进行示例展示用法,以及优缺点对比。
新建maven项目具体工作参考:《MyBatis(九):Mybatis Java API批量操作(增、删、改、查)》
在spring-config.xml中添加sqlSessionTemplate bean:
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory" />
<!--<constructor-arg index="1" value="BATCH" />-->
</bean>
新建测试类:com.dx.test.TestBatchInsert.java
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random; import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.dx.test.mapper.ArticleCategoryMapper;
import com.dx.test.model.ArticleCategory;
import com.dx.test.model.enums.DataStatus; @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:spring-config.xml" })
public class TestBatchInsert {
@Autowired
private SqlSessionTemplate sqlSessionTemplate; // 引入注入的sqlSessionTemplate bean实例
@Autowired
private ArticleCategoryMapper articleCategoryMapper; // 参考上篇文章该类的定义。 }
mybatis 批量处理测试:
方案1)采用mybatis foreach循环插入方案:
@Test
public void testWithSimple() {
long start = System.currentTimeMillis();
SqlSession sqlSession = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.SIMPLE, false);
ArticleCategoryMapper articleCategoryMapper=sqlSession.getMapper(ArticleCategoryMapper.class); List<ArticleCategory> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
ArticleCategory articleCategory = new ArticleCategory();
articleCategory.setTitle("title_" + new Random().nextInt(1000) + "_" + new Random().nextInt(1000));
articleCategory.setDescription("category description");
articleCategory.setImgSrc("http://www.test.com/img/category/img-" + new Random().nextInt(1000) + ".gif");
articleCategory.setState(DataStatus.Living);
articleCategory.setCreateTime(new Date());
articleCategory.setCreateUser("create user");
articleCategory.setCreateUserId("user-" + new Random().nextInt(1000)); articleCategoryMapper.insert(articleCategory);
}
sqlSession.commit();
sqlSession.clearCache();
sqlSession.close(); long stop = System.currentTimeMillis();
System.out.println("testWithExecutorType.SIMPLE:" + (stop - start) + "ms");
}
执行该测试方法:
==> Preparing: INSERT INTO article_category (title, img_src, description, state, create_user, create_user_id, create_time, update_user, update_user_id, update_time, version) VALUES (?, ?, ?, ?, ?, ?, now(), ?, ?, now(), 0)
==> Parameters: title_159_754(String), http://www.test.com/img/category/img-606.gif(String), category description(String), 0(Integer), create user(String), user-740(String), null, null
<== Updates: 1
。。。
==> Preparing: INSERT INTO article_category (title, img_src, description, state, create_user, create_user_id, create_time, update_user, update_user_id, update_time, version) VALUES (?, ?, ?, ?, ?, ?, now(), ?, ?, now(), 0)
==> Parameters: title_416_561(String), http://www.test.com/img/category/img-72.gif(String), category description(String), 0(Integer), create user(String), user-98(String), null, null
<== Updates: 1
==> Preparing: INSERT INTO article_category (title, img_src, description, state, create_user, create_user_id, create_time, update_user, update_user_id, update_time, version) VALUES (?, ?, ?, ?, ?, ?, now(), ?, ?, now(), 0)
==> Parameters: title_967_656(String), http://www.test.com/img/category/img-897.gif(String), category description(String), 0(Integer), create user(String), user-620(String), null, null
<== Updates: 1
==> Preparing: INSERT INTO article_category (title, img_src, description, state, create_user, create_user_id, create_time, update_user, update_user_id, update_time, version) VALUES (?, ?, ?, ?, ?, ?, now(), ?, ?, now(), 0)
==> Parameters: title_399_676(String), http://www.test.com/img/category/img-819.gif(String), category description(String), 0(Integer), create user(String), user-889(String), null, null
<== Updates: 1
testWithExecutorType.SIMPLE::8163ms
测试结果发现耗时为:8163ms,而且从打印日志,可以看出该类执行是一条一条的插入的,而且每次插入前都要Preparing插入sql,这个是比较耗时的。
方案2)采用mybatis batch方案(使用ExecutorType.BATCH):
@Test
public void testWithBatch() {
long start = System.currentTimeMillis();
SqlSession sqlSession = sqlSessionTemplate.getSqlSessionFactory().openSession(ExecutorType.BATCH, false);
ArticleCategoryMapper articleCategoryMapper=sqlSession.getMapper(ArticleCategoryMapper.class);
List<ArticleCategory> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
ArticleCategory articleCategory = new ArticleCategory();
articleCategory.setTitle("title_" + new Random().nextInt(1000) + "_" + new Random().nextInt(1000));
articleCategory.setDescription("category description");
articleCategory.setImgSrc("http://www.test.com/img/category/img-" + new Random().nextInt(1000) + ".gif");
articleCategory.setState(DataStatus.Living);
articleCategory.setCreateTime(new Date());
articleCategory.setCreateUser("create user");
articleCategory.setCreateUserId("user-" + new Random().nextInt(1000)); articleCategoryMapper.insert(articleCategory);
}
sqlSession.commit();
sqlSession.clearCache();
sqlSession.close(); long stop = System.currentTimeMillis();
System.out.println("testWithExecutorType.BATCH:" + (stop - start) + "ms");
}
执行该测试方法:
==> Preparing: INSERT INTO article_category (title, img_src, description, state, create_user, create_user_id, create_time, update_user, update_user_id, update_time, version) VALUES (?, ?, ?, ?, ?, ?, now(), ?, ?, now(), 0)
==> Parameters: title_345_236(String), http://www.test.com/img/category/img-688.gif(String), category description(String), 0(Integer), create user(String), user-337(String), null, null
。。。
==> Parameters: title_534_211(String), http://www.test.com/img/category/img-572.gif(String), category description(String), 0(Integer), create user(String), user-139(String), null, null
==> Parameters: title_292_271(String), http://www.test.com/img/category/img-8.gif(String), category description(String), 0(Integer), create user(String), user-547(String), null, null
testWithExecutorType.BATCH:5283ms
测试结果发现耗时为:5283ms,而且从打印日志可以看出该方案只一致性一次预编译,之后插入都不需要再次预编译。
方案3)采用拼接SQL方案:
用法1:
@Test
public void testWithBatchScript() {
long start = System.currentTimeMillis(); List<ArticleCategory> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
ArticleCategory articleCategory = new ArticleCategory();
articleCategory.setTitle("title_" + new Random().nextInt(1000) + "_" + new Random().nextInt(1000));
articleCategory.setDescription("category description");
articleCategory.setImgSrc("http://www.test.com/img/category/img-" + new Random().nextInt(1000) + ".gif");
articleCategory.setState(DataStatus.Living);
articleCategory.setCreateTime(new Date());
articleCategory.setCreateUser("create user");
articleCategory.setCreateUserId("user-" + new Random().nextInt(1000)); list.add(articleCategory);
}
articleCategoryMapper.batchInsertsWithScript(list); long stop = System.currentTimeMillis();
System.out.println("testWithBatchScript:" + (stop - start) + "ms");
}
该方法执行日志:
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@1b84f475] will not be managed by Spring
==> Preparing: INSERT INTO `article_category` (`title`,`img_src`,`description`,`state`,`create_time`,`create_user`,`create_user_id`,`update_time`,`update_user`,`update_user_id`,`version`)
VALUES (?,?,?,?,now(),?,?,now(),?,?,0) ,
。。。
(?,?,?,?,now(),?,?,now(),?,?,0) ,
(?,?,?,?,now(),?,?,now(),?,?,0)
ON DUPLICATE KEY UPDATE `update_time` = now()
==> Parameters:
title_375_245(String), http://www.test.com/img/category/img-247.gif(String), category description(String), 0(Integer), create user(String), user-930(String), null, null,
。。。
title_275_39(String), http://www.test.com/img/category/img-875.gif(String), category description(String), 0(Integer), create user(String), user-323(String), null, null,
title_743_735(String), http://www.test.com/img/category/img-220.gif(String), category description(String), 0(Integer), create user(String), user-917(String), null, null
<== Updates: 10000
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@65b104b9]
testWithBatchScript:3312ms
测试2:
@Test
public void testWithBatchSQL() {
long start = System.currentTimeMillis(); List<ArticleCategory> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
ArticleCategory articleCategory = new ArticleCategory();
articleCategory.setTitle("title_" + new Random().nextInt(1000) + "_" + new Random().nextInt(1000));
articleCategory.setDescription("category description");
articleCategory.setImgSrc("http://www.test.com/img/category/img-" + new Random().nextInt(1000) + ".gif");
articleCategory.setState(DataStatus.Living);
articleCategory.setCreateTime(new Date());
articleCategory.setCreateUser("create user");
articleCategory.setCreateUserId("user-" + new Random().nextInt(1000)); list.add(articleCategory);
}
articleCategoryMapper.batchInserts(list);
long stop = System.currentTimeMillis();
System.out.println("testWithBatchSQL:" + (stop - start) + "ms");
}
执行该方法打印日志:
Creating a new SqlSession
SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@65b104b9] was not registered for synchronization because synchronization is not active
JDBC Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@31ff43be] will not be managed by Spring
==> Preparing: INSERT INTO `article_category`(`title`,`img_src`,`description`,`state`,`create_time`,`create_user`,`create_user_id`,`update_time`,`update_user`,`update_user_id`,`version`)
VALUES(?,?,?,?,now(),?,?,now(),?,?,0),
。。。
(?,?,?,?,now(),?,?,now(),?,?,0),
(?,?,?,?,now(),?,?,now(),?,?,0)
ON DUPLICATE KEY UPDATE `update_time` = now();
==> Parameters: title_131_603(String), http://www.test.com/img/category/img-831.gif(String), category description(String), 0(Integer), create user(String), user-35(String), null, null,
。。。
title_317_725(String), http://www.test.com/img/category/img-208.gif(String), category description(String), 0(Integer), create user(String), user-968(String), null, null,
title_403_241(String), http://www.test.com/img/category/img-870.gif(String), category description(String), 0(Integer), create user(String), user-483(String), null, null
<== Updates: 10000
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@65b104b9]
testWithBatchSQL:2978ms
上边这两种方法实际上原理相同:拼接SQL,一次提交一批SQL方案。
拼接SQL一次提交方案缺点:当拼接sql超出’max_allowed_packet‘设置大小时,会抛出异常:
com.mysql.jdbc.PacketTooBigException: Packet for query is too large (8346602 > 4194304(4M)). You can change this value on the server by setting the max_allowed_packet’ variable.
备注:
SqlServer 对语句的条数和参数的数量都有限制,分别是 1000 和 2100。
Mysql 对语句的长度有限制,默认是 4M。
Mybatis 对动态语句没有数量上的限制。
解决错误方案:
我安装的mysql版本是:8.0.17 MySQL Community Server - GPL
dx:~ $ mysql -uroot -p123456 -h127.0.0.1
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 356
Server version: 8.0.17 MySQL Community Server - GPL Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
该版本的mysql允许的发送给服务器的最大数据包大小情况如下:
ysql> show variables like '%packet%';
+---------------------------+------------+
| Variable_name | Value |
+---------------------------+------------+
| max_allowed_packet | 67108864 |
| mysqlx_max_allowed_packet | 67108864 |
| slave_max_allowed_packet | 1073741824 |
+---------------------------+------------+
3 rows in set (0.00 sec)
ax_allowed_packet 设置以字节发送给服务器的最大数据包大小. (默认: 64MB),
数据库变更需要:大的插入和更新可能会被max_allowed_packet参数限制,导致失败。
此时,解决方案:
1)在mysql中执行:set global max_allowed_paclet = 128*1024*1024;
2)修改mysql服务器的配置(my.ini 或者 my.cnf 文件)参数: max_allowed_packet = 128M 来解决,
3)需要重启mysql服务器(缺陷)。
备注:
上边‘mysqlx_max_allowed_packet’参数时mysqlx插件的配置参数,起作用针对mysqlx起作用,作用于 max_allowed_packet 相同。
mysql> show plugins;
+---------------------------------+----------+--------------------+---------+---------+
| Name | Status | Type | Library | License |
+---------------------------------+----------+--------------------+---------+---------+
| binlog | ACTIVE | STORAGE ENGINE | NULL | GPL |
| mysql_native_password | ACTIVE | AUTHENTICATION | NULL | GPL |
| sha256_password | ACTIVE | AUTHENTICATION | NULL | GPL |
| caching_sha2_password | ACTIVE | AUTHENTICATION | NULL | GPL |
| sha2_cache_cleaner | ACTIVE | AUDIT | NULL | GPL |
| CSV | ACTIVE | STORAGE ENGINE | NULL | GPL |
| MEMORY | ACTIVE | STORAGE ENGINE | NULL | GPL |
| InnoDB | ACTIVE | STORAGE ENGINE | NULL | GPL |
...
| mysqlx | ACTIVE | DAEMON | NULL | GPL |
| mysqlx_cache_cleaner | ACTIVE | AUDIT | NULL | GPL |
+---------------------------------+----------+--------------------+---------+---------+
44 rows in set (0.01 sec)
性能总结:
+----------------------------------------------+------------+
| 采用方案 | 耗时 |
+----------------------------------------------+------------+
| 普通处理方法(ExecutorType.SIMPLE)逐条插入处理 | 8s |
| 使用ExecutorType.BATCH | 5s |
| 使用拼接SQL,一致性提交一批SQL | 3s |
+-----------------------------------------------+------------+
MyBatis(十):Mybatis 几种批量操作的对比的更多相关文章
- MyBatis的几种批量操作
MyBatis中批量插入 方法一: <insert id="insertbatch" parameterType="java.util.List"> ...
- 160520、MyBatis的几种批量操作
MyBatis中批量插入 方法一: <insert id="insertbatch" parameterType="Java.util.List"> ...
- 数据库--MyBatis的(insert,update,delete)三种批量操作
转自:http://blog.csdn.net/starywx/article/details/23268465 前段时间由于项目赶期没顾上开发过程中的性能问题,现对部分代码进行优化的过程中发现在数据 ...
- mybatis与hibernate的区别持久层对比【面试题】
Mybatis技术特点: 好处: 通过直接编写SQL语句,可以直接对SQL进行性能的优化: 学习门槛低,学习成本低.只要有SQL基础,就可以学习mybatis,而且很容易上手: 由于直接编写SQL语句 ...
- mybatis批量更新两种方式:1.修改值全部一样 2.修改每条记录值不一样
Mybatis批量更新数据 mybatis批量更新两种方式:1.修改值全部一样 2.修改每条记录值不一样 mybatis批量更新两种方式:1.修改值全部一样 2.修改每条记录值不一样 mybatis批 ...
- 【Mybatis】MyBatis之配置多数据源(十)
在做项目的过程中,有时候一个数据源是不够,那么就需要配置多个数据源.本例介绍mybatis多数据源配置 前言 一般项目单数据源,使用流程如下: 单个数据源绑定给sessionFactory,再在Dao ...
- Hello Mybatis 02 mybatis generator
接着上一篇文章通过Mybatis完成了一个User的CRUD的功能之后,这篇开始还需要建立一个Blog类,这样就可以模拟一个简单的微博平台的数据库了. 数据库准备 首先我们,还是需要在数据库中新建一个 ...
- 8种Nosql数据库系统对比
导读:Kristóf Kovács 是一位软件架构师和咨询顾问,他最近发布了一片对比各种类型NoSQL数据库的文章. 虽然SQL数据库是非常有用的工具,但经历了15年的一支独秀之后垄断即将被打破.这只 ...
- 数据库 --> 8种NoSQL数据库对比
8 种 NoSQL 数据库对比 NoSQL是一项全新的数据库革命性运动,NoSQL的拥护者们提倡运用非关系型的数据存储.现今的计算机体系结构在数据存储方面要求具备庞大的水平扩展性,而NoSQL致力于改 ...
随机推荐
- JavaScript 事件(基础)
一.事件 事件:触发-响应机制. 二.事件三要素 1.事件源:触发事件的元素 2.事件名称:发送了什么方式的事件 3.事件处理程序:事件触发后要执行的代码(函数形式) 三.事件的使用方式 1.获取元素 ...
- DataPipeline丨「自定义」数据源,解决复杂请求逻辑外部数据获取难题
A公司专注为各种规模和复杂程度的金融投资机构提供一体化投资管理系统,系统主要由投资组合管理.交易执行管理.实时监控管理.风险管理等功能模块构成.随着企业管理产品数量的不断增多,大量数据分散在各券商系统 ...
- 关于连接不上SVN的部分解决方案——No repository found in svn localhost
今天如往常一样做事,期间发现一个问题,于是就打算将文件与 svn 上的文件进行对比,可谁成想 Eclipse 突然弹框报错,然后我到SVN资源库中直接刷新打开 svn 的地址,又弹框报错:文件夹不存在 ...
- MySQL IFNULL() 函数
MySQL函数 IFNULL() 函数用于判断第一个表达式是否为 NULL,如果为 NULL 则返回第二个参数的值,如果不为 NULL 则返回第一个参数的值. IFNULL() 函数语法格式为: IF ...
- ad批量上传
import pandas as pd import requests import arrow from collections import deque import json campaign_ ...
- MySQL 8.0 的xtrabackup备份
xtrabackup 备份语句: fname=`date +%F_%H-%M-%S` mkdir -p /mnt/dbbak/db_$fname xtrabackup --defaults-file= ...
- 为什么要指定HashMap的容量?HashMap指定容量初始化后,底层Hash数组已经被分配内存了吗?
为什么要指定HashMap的容量? 首先创建HashMap时,指定容量比如1024后,并不是HashMap的size不是1024,而是0,插入多少元素,size就是多少: 然后如果不指定HashMap ...
- Linux:使用awk命令获取文本的某一行,某一列;sed插入指定的内容到指定文件中
awk相关用法: 1.打印文件的第一列(域) : awk '{print $1}' filename2.打印文件的前两列(域) : aw ...
- 20182310 第二周&第三周学习总结
20182310 2019-2020-1 <数据结构与面向对象程序设计>第2周&第3周学习总结 教材学习内容总结 1.首先是String类定义的字符串,然后是print和print ...
- 安装GO
1.中文社区 下载地址 https://studygolang.com/dl 选择自己操作系统版本 2.找到适合你系统的版本下载,本人下载的是windows版本.也可以下载Source自己更 ...