MyBatis踩坑系列1:浅谈MyBatis的三种批量插入方式
序言
每个人在刚刚接触mybatis框架的时候,或多或少会写出一些令程序执行效率变慢的代码。例如:在for循环里写sql。博主刚刚开始写项目的时候,就写过一段双层for循环中做查询、插入、删除、修改的神操作。现在看来当时程序能够顺利上线得亏在于使用人数不多。我们都知道执行一条sql语句就需要访问数据库一次数据库,频繁和数据库进行交互,将是一个十分消耗服务器性能的行为。下面我将带大家带来三种mybatis批量插入的方式,以及各自的优缺点对比。
环境介绍
MySQL5.7+SpringBoot+mybatis+tkmapper
1.我是新手我怕谁——循环体中执行单条sql
在循环体中写入sql是我们新手入门的必经阶段,因为在循环体中写sql可以使得代码更加的直观,让自己的程序跟随自己的思路走下去,从而不增加代码的复杂度。那我们看一看这种方式在100万条插入数据下的表现吧
/**
* 循环体中的插入操作
* @param testList
* @return
*/
public long test1(List<Test> testList){
long start = System.currentTimeMillis();
for (Test test : testList) {
testMapper.insertSelective(test);
}
long end = System.currentTimeMillis();
return end-start;
}
执行结果:

注意:循环体中访问数据库,可以在不追求性能的情况下使用。新手练习也可以多使用来增加对业务的理解,但十分消耗服务器资源切勿上生产使用
2.‘五秒’真男人,谁能有我快——sql拼接
有的人就会说,我才不会在循环体中写sql呢!我可是拥有3年开发经验的老鸟,在xml中写foreach就行了,谁能有我快。那我们看一下他在百万级数据下的表现吧:
@Insert("<script> " +
" insert into test(name) values " +
" <foreach collection=\"list\" item=\"item\" index=\"index\" separator=\",\">\n" +
" (#{item.name}) </foreach>" +
"</script>")
Integer insertList(@Param("list") List<Test> testList);
/**
* 拼接sql
* @param testList
* @return
*/
public long test2(List<Test> testList){
long start = System.currentTimeMillis();
Integer successNum = testMapper.insertList(testList);
System.out.println(successNum);
long end = System.currentTimeMillis();
return end-start;
}
执行结果:

**注意:使用mybatis的foreach 本质上是对sql语句的拼接,你的数据量越大,拼接的sql语句越长,可能超过执行的最长sql语句从而程序报错,所以在不确定数据量的情况下优先使用批处理 **
3.我的内涵你不懂——批处理
在第一次看到批处理代码的时候,你可能会认为是一个新手写的代码。在循环体中写sql,可不是一个新手的吗?那我们来看一下他在百万级数据下的表现吧:
@Autowired
private SqlSessionFactory sqlSessionFactory;
/**
* 批处理
* @param testList
* @return
*/
public long test3(List<Test> testList){
long start = System.currentTimeMillis();
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH,false);
int count=0;
TestMapper mapper = session.getMapper(TestMapper.class);
for (Test test : testList) {
mapper.insertSelective(test);
count++;
if(count%1000==999){//每1000条提交一次防止内存溢出
session.commit();
session.clearCache();
}
}
session.commit();
session.clearCache();
long end = System.currentTimeMillis();
return end-start;
}
执行结果:

注意:使用批处理的前提是已知数据量不大的情况下使用,在数据量尚可的情况下我们还是需要去追求效率。可预知数据量不会超过sql长度下,建议使用mybatis的sql拼接
宏观展示
| 循环体内SQL | 拼接SQL | 批处理 |
|---|---|---|
| 94668毫秒 | 报错 | 45206毫秒 |
总结
我们在写代码的过程中,不能只考虑逻辑的简单性也不能只考虑性能的高低。需要从实际需求入手,在数据过多时我们常用的sql拼接会使程序报错。在数据量可预知的情况下,如果只是少量的可预知数据我们可以用sql拼接,在大的数据量的情况下,又得考虑性能的情况下可以使用批处理来解决。
本文介绍了MyBatis的三种批量插入方式:循环执行单条SQL、SQL拼接(foreach)和批处理。通过案例分析了不同方式在大量数据插入时的性能和限制,强调在实际应用中需根据数据量和性能需求选择合适的方法,避免资源浪费和程序错误。





