MyBatis 一般有三种方式可以实现批量更新,分别为:for 循环、动态sql 的 foreach 元素和 ExecutorType.BATCH。下面我们分别来介绍这三种方式以及其各自的优缺点。
1 for 循环实现
在代码中直接使用 for 循环,每一个循环执行一次更新和自动提交。如下:
public void testByFor() {
SqlSession session = FactoryBuildByXML.getFactory().openSession(true);
try {
TagMapper tagMapper = session.getMapper(TagMapper.class);
long start = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
Tag tag = new Tag(null, "tagname" + i, "This is tag" + i);
tagMapper.insertTag(tag);
}
System.out.println("共用时:" + (System.currentTimeMillis() - start));
} finally {
session.close();
}
}
<insert id="insertTag" parameterType="Tag">
insert into tag(
id,
name,
remark
)values (
#{id},
#{name},
#{remark}
)
</insert>
执行结果:
DEBUG [main] - ==> Preparing: insert into tag( id, name, remark )values ( ?, ?, ? )
DEBUG [main] - ==> Parameters: null, tagname0(String), This is tag0(String)
DEBUG [main] - <== Updates: 1
DEBUG [main] - ==> Preparing: insert into tag( id, name, remark )values ( ?, ?, ? )
DEBUG [main] - ==> Parameters: null, tagname1(String), This is tag1(String)
DEBUG [main] - <== Updates: 1
//此处略 2-8
DEBUG [main] - ==> Preparing: insert into tag( id, name, remark )values ( ?, ?, ? )
DEBUG [main] - ==> Parameters: null, tagname9(String), This is tag9(String)
DEBUG [main] - <== Updates: 1
共用时:1040
这种方式的缺点显而易见:每条语句都要进行一次与数据库的会话,性能最低,因此不建议使用。
2 foreach 元素实现
使用 MyBatis 提供的动态 sql 元素 foreach 进行批量更新和自动提交。如下:
public void testByForeach() {
SqlSession session = FactoryBuildByXML.getFactory().openSession(true);
try {
TagMapper tagMapper = session.getMapper(TagMapper.class);
long start = System.currentTimeMillis();
List<Tag> tagList = new ArrayList<Tag>();
for (int i = 0; i < 10; i++) {
Tag tag = new Tag(null, "tagname2-" + i, "This is tag by foreach" + i);
tagList.add(tag);
}
tagMapper.insertBatch(tagList);
System.out.println("共用时:" + (System.currentTimeMillis() - start));
} finally {
session.close();
}
}
<insert id="insertBatch" parameterType="Tag">
insert into tag(
id,
name,
remark
)values
<foreach collection="list" separator="," item="item" >
(#{item.id},#{item.name},#{item.remark})
</foreach>
</insert>
执行结果:
DEBUG [main] - ==> Preparing: insert into tag( id, name, remark )values (?,?,?) , (?,?,?) , (?,?,?) , (?,?,?) , (?,?,?) , (?,?,?) , (?,?,?) , (?,?,?) , (?,?,?) , (?,?,?)
DEBUG [main] - ==> Parameters: null, tagname2-0(String), This is tag by foreach0(String), null, tagname2-1(String), This is tag by foreach1(String), null, tagname2-2(String), This is tag by foreach2(String), null, tagname2-3(String), This is tag by foreach3(String), null, tagname2-4(String), This is tag by foreach4(String), null, tagname2-5(String), This is tag by foreach5(String), null, tagname2-6(String), This is tag by foreach6(String), null, tagname2-7(String), This is tag by foreach7(String), null, tagname2-8(String), This is tag by foreach8(String), null, tagname2-9(String), This is tag by foreach9(String)
DEBUG [main] - <== Updates: 10
共用时:517
从执行时间我们就可以看出,相对于 for 循环,这种方式的执行时间明显少很多(当然最合理的校验对比方式是使用更多的循环次数)。
- 优点:性能高,一个SQL一次性发送到数据库执行,与数据库只进行一次 IO,推荐使用。
- 缺点:因为数据库的限制,有 sql 的长度限制,所以使用时要注意限制批量执行的长度。
在 MySQL 中执行可以看到其对应的长度限制:
SHOW VARIABLES like '%packet%';
SHOW VARIABLES like '%net_buffer%';


3 ExecutorType.BATCH 实现
MyBatis 同样提供另外一种批量更新的方式:ExecutorType.BATCH,它允许我们一次性向数据库发送多条 sql,然后批量执行和提交。使用如下:
public void testByExecutorTypeBatch() {
SqlSession session = FactoryBuildByXML.getFactory().openSession(ExecutorType.BATCH,false);
try {
TagMapper tagMapper = session.getMapper(TagMapper.class);
long start = System.currentTimeMillis();
for (int i = 1; i <= 10; i++) {
Tag tag = new Tag(null, "tagname3-" + i, "This is tag by ExecutorType.Batch" + i);
tagMapper.insertTag(tag);
if (i % 5 == 0){
session.commit();
session.clearCache();
}
}
System.out.println("共用时:" + (System.currentTimeMillis() - start));
} finally {
session.close();
}
}
sql 同 for 循环,此处不再赘述。
执行结果:
DEBUG [main] - ==> Preparing: insert into tag( id, name, remark )values ( ?, ?, ? )
DEBUG [main] - ==> Parameters: null, tagname3-1(String), This is tag by ExecutorType.Batch1(String)
DEBUG [main] - ==> Parameters: null, tagname3-2(String), This is tag by ExecutorType.Batch2(String)
DEBUG [main] - ==> Parameters: null, tagname3-3(String), This is tag by ExecutorType.Batch3(String)
DEBUG [main] - ==> Parameters: null, tagname3-4(String), This is tag by ExecutorType.Batch4(String)
DEBUG [main] - ==> Parameters: null, tagname3-5(String), This is tag by ExecutorType.Batch5(String)
DEBUG [main] - ==> Preparing: insert into tag( id, name, remark )values ( ?, ?, ? )
DEBUG [main] - ==> Parameters: null, tagname3-6(String), This is tag by ExecutorType.Batch6(String)
DEBUG [main] - ==> Parameters: null, tagname3-7(String), This is tag by ExecutorType.Batch7(String)
DEBUG [main] - ==> Parameters: null, tagname3-8(String), This is tag by ExecutorType.Batch8(String)
DEBUG [main] - ==> Parameters: null, tagname3-9(String), This is tag by ExecutorType.Batch9(String)
DEBUG [main] - ==> Parameters: null, tagname3-10(String), This is tag by ExecutorType.Batch10(String)
共用时:560
- 优点:性能高,多个SQL一次性发送到数据库执行和提交。
- 缺点:在 openSession 时我们要关闭自动提交功能(即 openSession(ExecutorType.BATCH,false)),在代码中手动提交。
综上,推荐使用第二种,即动态 sql 的 foreach 元素进行批量更新操作。
附:
当前版本:mybatis-3.5.0
官网文档:MyBatis
项目实践:MyBatis Learn
手写源码:MyBatis 简易实现
网友评论