ExecutorType 批量执行
MyBatis 提供三种执行器类型,合理选择执行器可以显著提升批量操作性能。本文对比三种执行器的差异,重点讲解 BATCH 批量执行器的使用。
三种 ExecutorType 对比
| 执行器 | 内部行为 | 适用场景 | 性能 |
|---|---|---|---|
| SIMPLE | 每次执行都创建新的 PreparedStatement | 默认,单次操作 | 基准 |
| REUSE | 复用 PreparedStatement,相同 SQL 不重复编译 | 同一条 SQL 执行多次 | 中 |
| BATCH | 将 SQL 加入批处理队列,统一 executeBatch() | 批量 INSERT/UPDATE | 高 |
注意:默认执行器是 SIMPLE。可通过
defaultExecutorType全局设置,或在获取 SqlSession 时单独指定。
SIMPLE 执行器
Java
// 默认使用 SIMPLE
SqlSession session = sqlSessionFactory.openSession();
for (int i = 0; i < 1000; i++) {
mapper.insertUser(new User("user" + i, "user" + i + "@test.com"));
}
session.commit();
session.close();
SIMPLE 模式下,每次调用 insertUser 都会:
- 创建新的 PreparedStatement
- 设置参数
- 执行
executeUpdate() - 释放资源
1000 次插入 = 1000 次网络往返 + 1000 次语句编译。
REUSE 执行器
Java
SqlSession session = sqlSessionFactory.openSession(ExecutorType.REUSE);
for (int i = 0; i < 1000; i++) {
mapper.insertUser(new User("user" + i, "user" + i + "@test.com"));
}
session.commit();
session.close();
REUSE 模式下,相同的 SQL 语句只编译一次,复用 PreparedStatement。减少了编译开销,但每次执行仍然是独立的网络往返。
BATCH 执行器
基本用法
Java
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
UserMapper mapper = session.getMapper(UserMapper.class);
for (int i = 0; i < 1000; i++) {
mapper.insertUser(new User("user" + i, "user" + i + "@test.com"));
}
session.commit();
} finally {
session.close();
}
BATCH 模式下:
mapper.insertUser()不会立即发送 SQL 到数据库- SQL 被加入批处理队列
commit()时统一调用executeBatch()
1000 次插入 = 1 次网络往返 + 批量执行。
Mapper XML 配置
BATCH 模式下的 Mapper 写法与普通插入相同:
XML
<insert id="insertUser" parameterType="User">
INSERT INTO user (username, email) VALUES (#{username}, #{email})
</insert>
注意:BATCH 模式使用的是
executeBatch(),不需要在 XML 中使用 foreach 拼接 SQL。
批量更新
Java
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
UserMapper mapper = session.getMapper(UserMapper.class);
for (User user : users) {
mapper.updateUser(user);
}
session.commit();
} finally {
session.close();
}
分批提交
当数据量极大时,避免在 BATCH 队列中堆积过多语句:
Java
public void batchInsertInChunks(List<User> users) {
int batchSize = 500;
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
UserMapper mapper = session.getMapper(UserMapper.class);
for (int i = 0; i < users.size(); i++) {
mapper.insertUser(users.get(i));
// 每 500 条执行一次 commit
if ((i + 1) % batchSize == 0) {
session.commit();
session.clearCache();
}
}
// 提交剩余数据
session.commit();
} finally {
session.close();
}
}
注意:BATCH 模式下调用
commit()后必须clearCache()清理缓存,避免内存泄漏。
全局配置 ExecutorType
MyBatis 配置文件
XML
<settings>
<setting name="defaultExecutorType" value="BATCH"/>
</settings>
Spring 集成配置
XML
<bean id="batchSqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg ref="sqlSessionFactory"/>
<constructor-arg value="BATCH"/>
</bean>
Java
@Configuration
public class MyBatisConfig {
@Bean
public SqlSessionTemplate batchSqlSessionTemplate(SqlSessionFactory factory) {
return new SqlSessionTemplate(factory, ExecutorType.BATCH);
}
}
性能对比
| 场景 | SIMPLE | REUSE | BATCH |
|---|---|---|---|
| 单次查询 | 基准 | 相同 | 相同 |
| 同 SQL 查询 1000 次 | 慢 | 中(复用编译) | 不适用 |
| 1000 次 INSERT | 慢 | 中 | 快 |
| 1000 次 UPDATE | 慢 | 中 | 快 |
| 需要获取自增主键 | 支持 | 支持 | 部分支持 |
注意:BATCH 模式下,MySQL 默认无法获取每条记录的自增主键,因为
executeBatch()不返回 individual generated keys。如需要回填主键,考虑使用 foreach 方式。
BATCH 模式的限制
| 限制 | 说明 |
|---|---|
| 无法获取自增主键 | MySQL BATCH 模式下 executeBatch() 不返回单条主键 |
| 动态 SQL 不生效 | BATCH 使用 PreparedStatement,<if>、<choose> 等动态标签无法在运行时判断 |
| 内存堆积 | 不手动 commit 时,所有 SQL 在内存中排队,可能导致 OOM |
| 无法立即看到结果 | SQL 在 commit() 时才真正执行,无法中途判断某条是否失败 |
要点总结
- SIMPLE:每次执行创建新 PreparedStatement,适合单次操作
- REUSE:复用 PreparedStatement,减少重复编译
- BATCH:批处理模式,SQL 加入队列后统一 executeBatch(),批量写入性能最高
- BATCH 模式注意每批 commit 后 clearCache(),避免内存泄漏
- BATCH 模式无法获取自增主键,动态 SQL 不生效
- 大批量数据分批提交,控制单次 BATCH 队列大小
📝 发现内容有误?点击此处直接编辑