mybatis批量导入
实现数据批量插入(jdbc\mybatis)
1. 实现方式 – JDBC:
- 普通方式插入for循环
1 private String url = "jdbc:mysql://localhost:3306/test01";2 private String user = "root";3 private String password = "123456";4 @Test5 public void Test(){6 Connection conn = null;7 PreparedStatement pstm =null;8 ResultSet rt = null;9 try {
10 Class.forName("com.mysql.jdbc.Driver");
11 conn = DriverManager.getConnection(url, user, password);
12 String sql = "INSERT INTO userinfo(uid,uname,uphone,uaddress) VALUES(?,CONCAT('姓名',?),?,?)";
13 pstm = conn.prepareStatement(sql);
14 Long startTime = System.currentTimeMillis();
15 Random rand = new Random();
16 int a,b,c,d;
17 for (int i = 1; i <= 1000; i++) {
18 pstm.setInt(1, i);
19 pstm.setInt(2, i);
20 a = rand.nextInt(10);
21 b = rand.nextInt(10);
22 c = rand.nextInt(10);
23 d = rand.nextInt(10);
24 pstm.setString(3, "188"+a+"88"+b+c+"66"+d);
25 pstm.setString(4, "xxxxxxxxxx_"+"188"+a+"88"+b+c+"66"+d);27 pstm.executeUpdate();
28 }
29 Long endTime = System.currentTimeMillis();
30 System.out.println("OK,用时:" + (endTime - startTime));
31 } catch (Exception e) {
32 e.printStackTrace();
33 throw new RuntimeException(e);
34 }finally{
35 if(pstm!=null){
36 try {
37 pstm.close();
38 } catch (SQLException e) {
39 e.printStackTrace();
40 throw new RuntimeException(e);
41 }
42 }
43 if(conn!=null){
44 try {
45 conn.close();
46 } catch (SQLException e) {
47 e.printStackTrace();
48 throw new RuntimeException(e);
49 }
50 }
51 }
52 }
- 使用事务方式插入
先将命令的提交方式设为false,即手动提交conn.setAutoCommit(false);最后在所有命令执行完之后再提交事务conn.commit();
private String url = "jdbc:mysql://localhost:3306/test01";2 private String user = "root";3 private String password = "123456";4 @Test5 public void Test(){6 Connection conn = null;7 PreparedStatement pstm =null;8 ResultSet rt = null;9 try {
10 Class.forName("com.mysql.jdbc.Driver");
11 conn = DriverManager.getConnection(url, user, password);
12 String sql = "INSERT INTO userinfo(uid,uname,uphone,uaddress) VALUES(?,CONCAT('姓名',?),?,?)";
13 pstm = conn.prepareStatement(sql);
14 conn.setAutoCommit(false);
15 Long startTime = System.currentTimeMillis();
16 Random rand = new Random();
17 int a,b,c,d;
18 for (int i = 1; i <= 100000; i++) {
19 pstm.setInt(1, i);
20 pstm.setInt(2, i);
21 a = rand.nextInt(10);
22 b = rand.nextInt(10);
23 c = rand.nextInt(10);
24 d = rand.nextInt(10);
25 pstm.setString(3, "188"+a+"88"+b+c+"66"+d);
26 pstm.setString(4, "xxxxxxxxxx_"+"188"+a+"88"+b+c+"66"+d);
27 pstm.executeUpdate();
28 }
29 conn.commit();
30 Long endTime = System.currentTimeMillis();
31 System.out.println("OK,用时:" + (endTime - startTime));
32 } catch (Exception e) {
33 e.printStackTrace();
34 throw new RuntimeException(e);
35 }finally{
36 if(pstm!=null){
37 try {
38 pstm.close();
39 } catch (SQLException e) {
40 e.printStackTrace();
41 throw new RuntimeException(e);
42 }
43 }
44 if(conn!=null){
45 try {
46 conn.close();
47 } catch (SQLException e) {
48 e.printStackTrace();
49 throw new RuntimeException(e);
50 }
51 }
52 }
53 }
- 批量处理
首先,JDBC连接的url中要加rewriteBatchedStatements参数设为true是批量操作的前提,其次就是检查mysql驱动包时候是5.1.13以上版本(低于该版本不支持)
private String url = "jdbc:mysql://localhost:3306/test01?rewriteBatchedStatements=true";2 private String user = "root";3 private String password = "123456";4 @Test5 public void Test(){6 Connection conn = null;7 PreparedStatement pstm =null;8 ResultSet rt = null;9 try {
10 Class.forName("com.mysql.jdbc.Driver");
11 conn = DriverManager.getConnection(url, user, password);
12 String sql = "INSERT INTO userinfo(uid,uname,uphone,uaddress) VALUES(?,CONCAT('姓名',?),?,?)";
13 pstm = conn.prepareStatement(sql);
14 Long startTime = System.currentTimeMillis();
15 Random rand = new Random();
16 int a,b,c,d;
17 for (int i = 1; i <= 100000; i++) {
18 pstm.setInt(1, i);
19 pstm.setInt(2, i);
20 a = rand.nextInt(10);
21 b = rand.nextInt(10);
22 c = rand.nextInt(10);
23 d = rand.nextInt(10);
24 pstm.setString(3, "188"+a+"88"+b+c+"66"+d);
25 pstm.setString(4, "xxxxxxxxxx_"+"188"+a+"88"+b+c+"66"+d);
26 pstm.addBatch();
27 }
28 pstm.executeBatch();
29 Long endTime = System.currentTimeMillis();
30 System.out.println("OK,用时:" + (endTime - startTime));
31 } catch (Exception e) {
32 e.printStackTrace();
33 throw new RuntimeException(e);
34 }finally{
35 if(pstm!=null){
36 try {
37 pstm.close();
38 } catch (SQLException e) {
39 e.printStackTrace();
40 throw new RuntimeException(e);
41 }
42 }
43 if(conn!=null){
44 try {
45 conn.close();
46 } catch (SQLException e) {
47 e.printStackTrace();
48 throw new RuntimeException(e);
49 }
50 }
51 }
52 }
- 事务操作+批量处理方式
private String url = "jdbc:mysql://localhost:3306/test01?rewriteBatchedStatements=true";2 private String user = "root";3 private String password = "123456";4 @Test5 public void Test(){6 Connection conn = null;7 PreparedStatement pstm =null;8 ResultSet rt = null;9 try {
10 Class.forName("com.mysql.jdbc.Driver");
11 conn = DriverManager.getConnection(url, user, password);
12 String sql = "INSERT INTO userinfo(uid,uname,uphone,uaddress) VALUES(?,CONCAT('姓名',?),?,?)";
13 pstm = conn.prepareStatement(sql);
14 conn.setAutoCommit(false);
15 Long startTime = System.currentTimeMillis();
16 Random rand = new Random();
17 int a,b,c,d;
18 for (int i = 1; i <= 100000; i++) {
19 pstm.setInt(1, i);
20 pstm.setInt(2, i);
21 a = rand.nextInt(10);
22 b = rand.nextInt(10);
23 c = rand.nextInt(10);
24 d = rand.nextInt(10);
25 pstm.setString(3, "188"+a+"88"+b+c+"66"+d);
26 pstm.setString(4, "xxxxxxxxxx_"+"188"+a+"88"+b+c+"66"+d);
27 pstm.addBatch();
28 }
29 pstm.executeBatch();
30 conn.commit();
31 Long endTime = System.currentTimeMillis();
32 System.out.println("OK,用时:" + (endTime - startTime));
33 } catch (Exception e) {
34 e.printStackTrace();
35 throw new RuntimeException(e);
36 }finally{
37 if(pstm!=null){
38 try {
39 pstm.close();
40 } catch (SQLException e) {
41 e.printStackTrace();
42 throw new RuntimeException(e);
43 }
44 }
45 if(conn!=null){
46 try {
47 conn.close();
48 } catch (SQLException e) {
49 e.printStackTrace();
50 throw new RuntimeException(e);
51 }
52 }
53 }
54 }
2 . 实现方式 – mybatis
mybatis中的foreach循环
<insert id="batchInsert" parameterType="java.util.List">insert into USER (id, name) values<foreach collection="list" item="model" index="index" separator=","> (#{model.id}, #{model.name})</foreach>
</insert>
其原理是将传统的单条insert语句
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2");
转换为:
INSERT INTO `table1` (`field1`, `field2`) VALUES ("data1", "data2"),("data1", "data2"),("data1", "data2"),("data1", "data2"),("data1", "data2");
理想情况下,这样可以在单个连接中一次性发送许多新行的数据,并将所有索引更新和一致性检查延迟到最后才进行。
乍看上去这个foreach没有问题,但是经过项目实践发现,当表的列数较多(20+),以及一次性插入的行数较多(5000+)时,整个插入的耗时十分漫长,达到了14分钟,这是不能忍的。
mybatis默认执行器类型为Simple,会为每个语句创建一个新的预处理语句,也就是创建一个PreparedStatement对象。在我们的项目中,会不停地使用批量插入这个方法,而因为MyBatis对于含有的语句,无法采用缓存,那么在每次调用方法时,都会重新解析sql语句。
所以,如果非要使用 foreach 的方式来进行批量插入的话,可以考虑减少一条 insert 语句中 values 的个数,最好能达到上面曲线的最底部的值,使速度最快。一般按经验来说,一次性插20~50行数量是比较合适的,时间消耗也能接受。
重点来了。上面讲的是,如果非要用的方式来插入,可以提升性能的方式。而实际上,MyBatis文档中写批量插入的时候,是推荐使用另外一种方法。(可以看 http://www.mybatis.org/mybatis-dynamic-sql/docs/insert.html 中 Batch Insert Support 标题里的内容)
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {SimpleTableMapper mapper = session.getMapper(SimpleTableMapper.class);List<SimpleTableRecord> records = getRecordsToInsert(); // not shownBatchInsert<SimpleTableRecord> batchInsert = insert(records).into(simpleTable).map(id).toProperty("id").map(firstName).toProperty("firstName").map(lastName).toProperty("lastName").map(birthDate).toProperty("birthDate").map(employed).toProperty("employed").map(occupation).toProperty("occupation").build().render(RenderingStrategy.MYBATIS3);batchInsert.insertStatements().stream().forEach(mapper::insert);session.commit();
} finally {session.close();
}
即基本思想是将 MyBatis session 的 executor type 设为 Batch ,然后多次执行插入语句。就类似于JDBC的下面语句一样。
Connection connection = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mydb?useUnicode=true&characterEncoding=UTF-8&useServerPrepStmts=false&rewriteBatchedStatements=true","root","root");
connection.setAutoCommit(false);
PreparedStatement ps = connection.prepareStatement("insert into tb_user (name) values(?)");
for (int i = 0; i < stuNum; i++) {ps.setString(1,name);ps.addBatch();
}
ps.executeBatch();
connection.commit();
connection.close();
经过试验,使用了 ExecutorType.BATCH 的插入方式,性能显著提升,不到 2s 便能全部插入完成。
总结一下,如果MyBatis需要进行批量插入,推荐使用 ExecutorType.BATCH 的插入方式,如果非要使用 的插入的话,需要将每次插入的记录控制在 20~50 左右。
3. 方案
因为要考虑JVM的GC所以数据应该限制一下,但鉴于Mybatis大数据量的批量插入效率不高,所以根据数据大小分段治理。
3.1 小于1W使用:Mybatis批量插入方案
对JVM进行调优,但主要的性能瓶颈在批量插入操作。鉴于mybatis在项目开发方面的优势,数据量很小的情况下还是建议使用Mybatis。
3.2 大于1W小于10W使用:JDBC批量+事务处理
对JVM进行调优(设置Stack和GC等)。一般操作30秒以内是可以容忍的性能耗时。
3.3 10W以上数据使用:数据分批+JDBC批量+事务处理
对JVM进行调优(设置Stack和GC等),通过数据分批处理。对于分批处理需要借鉴前面的测试数据来定义分批量的大小,主要是对操作时间调优。
mybatis批量导入相关推荐
- java list分批_Java实用笔记——mybatis批量导入
1.使用Statement 批量插入 public void batchInsertJdbc1() throws Exception {DataSource ds = (DataSource) Spr ...
- vue表单中批量导入功能_spring boot mybatis+ vue 使用POI实现从Excel中批量导入数据
一.前端vue+element 1.前端使用element的upload组件来实现文件的上传 style="display: inline-flex;margin-right: 8px&qu ...
- 【Springboot+mybatis】 解析Excel并批量导入到数据库
[Springboot+mybatis] 解析Excel并批量导入到数据库 置顶 2018年01月16日 20:05:52 冉野丶 阅读数:4060 标签: excel导入数据库 文件上传 excel ...
- 关联表多数据的批量insert (批量导入,测试19W条数据用时46秒)
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 一.业务需求 :作多个批量导入 ,根据业务不同,每条数据导入前作各种验证, ...
- Mybatis框架 导入/导出功能的实现
导出功能: 1.dao层的mapper(写方法名) 2.xml里用sql语句实现mapper中的方法 注意: 映射时对比数据库段名和bean变量名 映射的property和sql语句需要查询的所有变 ...
- Java:实现文件批量导入导出实践(兼容xls,xlsx)
点击上方"Java知音",选择"置顶公众号" 技术文章第一时间送达! 作者:小卖铺的老爷爷 cnblogs.com/laoyeye/p/6938889.html ...
- XX健康:预约管理-预约设置日历插件文件简单下载Excel文件解析Excel表数据批量导入
1. 需求分析 前面我们已经完成了检查项管理.检查组管理.套餐管理等.接下来我们需要进行预约设置,其实就是设置每一天的体检预约最大数量.客户可以通过微信端在线预约,在线预约时需要选择体检的时间,如果客 ...
- springboot 导入excel(数据批量导入)
springboot excel数据批量导入 1.pom.xml 引入poi依赖 <!--poi--><dependency><groupId>org.apache ...
- 通过jsp向mysql批量导入数据_对大数据的批量导入MySQL数据库
自己的库里有索引在用insert导入数据时会变慢很多 使用事务+批量导入 可以配置使用spring+mybatis整合的方式关闭自动提交事务(地址),选择批量导入每一百条导入使用list存储值传入到m ...
- java使用POI实现Excel批量导入数据。
1.背景 项目中有使用easypoi,处理常规excel问题,但是现在有个需求,需要动态生成导出的报表字段.同时,根据导入的excel,增加数据信息.(有可能会出现,导入的报表是几天前下载的,不会最新 ...
最新文章
- 助力AI腾飞,深度学习走向何方?
- Kubernetes 最佳安全实践指南
- tomcat如何增大并发_Tomcat 7最大并发连接数的正确修改方法
- Red5 简单安装指南
- 生成jar文件的方法
- 计算机网络——速率相关的性能指标
- ldap和kerberos整合大数据账号
- ...is public, should be declared in a file named “ScresourcesApplic.java“---springcloud工作笔记164
- Google是否投资你,得看AI支持不支持
- PHP 数据库中的模糊查询
- JSTL使用总结(2) fmt标签库和fn标签库
- Xcode 模拟器复制解决方案
- Hibernate Annotation 设置字段的默认值
- java面试题1 牛客:A派生出子类B,B派生出子类C,并且在java源代码中有如下声明:
- SpringBoot实现文件在线预览
- 单基因gsea_单基因突变+肿瘤突变负荷+免疫细胞浸润文章套路
- Dextran-PEG2000-Conjugate,葡聚糖聚乙二醇Conjugate,葡聚糖聚乙二醇,属于葡聚糖缀合物
- JS gkb转utf8(fetch gbk网页是乱码)
- 【AD22】设置原理图纸张大小
- LS,MMSE,LMMSE,ML,MAP,LMS,AR,MSE误差介绍
热门文章
- c语言头文件malloc.h,#includemalloc.h,sys/types.h ,stdlib.h,string.h及C语言头文件
- 计算机考研复试难,艰难与快乐:2008年重庆邮电大学计算机考研复试经历
- 安卓软件改名器_安卓歌词适配V3.9.5 无损音乐下载器(软件篇)
- 三星980 NVMe SSD推出:首款不含 DRAM 的消费类硬盘
- yandex安装插件教程,研究了一下午终于可以用了
- 小米盒子共享电脑文件 服务器,小米盒子3S访问局域网电脑共享安装软件和观看视频的方法...
- 青岛自然人税收管理系统服务器地址,青岛市自然人税收管理系统扣缴客户端
- Kafka的常用命令(包括:下载安装、后台启动)
- linux命令行大全第2版,Linux命令行大全(第2版)
- 【效率工具合集】Win10镜像迅雷快速安装