本章主要讲解几种批量处理的用法及对别,批量处理一般用法包含以下几种:

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 几种批量操作的对比的更多相关文章

  1. MyBatis的几种批量操作

    MyBatis中批量插入 方法一: <insert id="insertbatch" parameterType="java.util.List"> ...

  2. 160520、MyBatis的几种批量操作

    MyBatis中批量插入 方法一: <insert id="insertbatch" parameterType="Java.util.List"> ...

  3. 数据库--MyBatis的(insert,update,delete)三种批量操作

    转自:http://blog.csdn.net/starywx/article/details/23268465 前段时间由于项目赶期没顾上开发过程中的性能问题,现对部分代码进行优化的过程中发现在数据 ...

  4. mybatis与hibernate的区别持久层对比【面试题】

    Mybatis技术特点: 好处: 通过直接编写SQL语句,可以直接对SQL进行性能的优化: 学习门槛低,学习成本低.只要有SQL基础,就可以学习mybatis,而且很容易上手: 由于直接编写SQL语句 ...

  5. mybatis批量更新两种方式:1.修改值全部一样 2.修改每条记录值不一样

    Mybatis批量更新数据 mybatis批量更新两种方式:1.修改值全部一样 2.修改每条记录值不一样 mybatis批量更新两种方式:1.修改值全部一样 2.修改每条记录值不一样 mybatis批 ...

  6. 【Mybatis】MyBatis之配置多数据源(十)

    在做项目的过程中,有时候一个数据源是不够,那么就需要配置多个数据源.本例介绍mybatis多数据源配置 前言 一般项目单数据源,使用流程如下: 单个数据源绑定给sessionFactory,再在Dao ...

  7. Hello Mybatis 02 mybatis generator

    接着上一篇文章通过Mybatis完成了一个User的CRUD的功能之后,这篇开始还需要建立一个Blog类,这样就可以模拟一个简单的微博平台的数据库了. 数据库准备 首先我们,还是需要在数据库中新建一个 ...

  8. 8种Nosql数据库系统对比

    导读:Kristóf Kovács 是一位软件架构师和咨询顾问,他最近发布了一片对比各种类型NoSQL数据库的文章. 虽然SQL数据库是非常有用的工具,但经历了15年的一支独秀之后垄断即将被打破.这只 ...

  9. 数据库 --> 8种NoSQL数据库对比

    8 种 NoSQL 数据库对比 NoSQL是一项全新的数据库革命性运动,NoSQL的拥护者们提倡运用非关系型的数据存储.现今的计算机体系结构在数据存储方面要求具备庞大的水平扩展性,而NoSQL致力于改 ...

随机推荐

  1. CSS之盒子居中的方法

    一.盒子垂直居中的方法 1.先让盒子的上下边缘和父盒子的水平中心线重叠,,然后再让子盒子往回移动自身一半的距离 <div class="father"> // 结构 & ...

  2. day 39

    ORM 对象关系映射 表 ---> 类 字段 ---> 属性 记录 ---> 对象 优点: ​ 使用者无需关心具体的SQL命令如何编写. ​ 直接通过调用方法,来执行相对应的SQL命 ...

  3. 关于mysql数据库涉及的一些规范

    tips:如果本文对你有用,请爱心点个赞,提高排名,让这篇文章帮助更多的人.谢谢大家!比心❤~ 如果解决不了,可以在文末加我微信,进群交流. 设计规范,在分工协作的工作场景中尤其重要,否则团队之间互相 ...

  4. Golang: 读写之外的其他文件操作

    在上一篇文章中,我们介绍了常用的文件读写操作,今天接着来研究一下,除了读写以外的其他常见文件操作. 一.创建目录: package main import ( "fmt" &quo ...

  5. IDEA编码时卡顿问题

    当代码行数超过2000行,甚至更多时会出现编码时卡顿到无法编译的情况,解决方法如下: 方法1:修改IDEA配置参数 找到D:\ideaIU-2019.1.1.win\bin\idea64.exe.vm ...

  6. jmap错误:unknown CollectedHeap type : class sun.jvm.hotspot.gc_interface.CollectedHeap

    原文:https://www.jianshu.com/p/0e5d8db8ed5e 错误场景 今天使用jmap -heap命令查看堆的详细信息报错. [root@instance-2gak1pfv d ...

  7. position 的absolute会使display变成inline-block

    position:absolute和float会隐式地改变display类型,不论之前什么类型的元素(display:none除外), 只要设置了position:absolute. float中任意 ...

  8. Elasticsearch Date类型,时间存储相关说明

    资料 网址 Elasticsearch 插入时间字段时数据格式问题 https://segmentfault.com/a/1190000016296983 Elasticsearch Date类型,时 ...

  9. hive创建表

    一.为什么要创建分区表 1.select查询中会扫描整个表内容,会消耗大量时间.由于相当多的时候人们只关心表中的一部分数据, 故建表时引入了分区概念. 2.hive分区表:是指在创建表时指定的part ...

  10. React Virtual DOM Explained in Simple English

    If you are using React or learning React, you must have heard of the term “Virtual DOM”. Now what is ...