逐筆 INSERT 會產生大量網路往返、SQL 解析、事務提交開銷。批量插入可將 N 條語句合併為一次網路互動,效能通常提升 10x~100x。MyBatis 提供兩種主流方案:
<foreach>:單條 SQL 多值插入ExecutorType.BATCH:JDBC 批次處理預編譯語句生成單條 SQL:INSERT INTO t (a,b) VALUES (?,?), (?,?), ...
<insert id="insertBatch" parameterType="java.util.List">
INSERT INTO t_student (name, age)
VALUES
<foreach collection="list" item="stu" separator=",">
(#{stu.name}, #{stu.age})
</foreach>
</insert>
List list = ...;
mapper.insertBatch(list); // 單次呼叫
| 優點 | 缺點 |
|---|---|
| 單次 SQL,網路往返最少 | SQL 長度受限 (max_allowed_packet) |
| 資料庫端單次解析優化 | 超大批次可能超出 SQL 解析器限制 |
| 程式碼簡潔,無需特殊 Session 管理 | 難以獲取自增 ID (需 useGeneratedKeys + keyProperty) |
| 支援所有資料庫標準語法 | 參數綁定數量極大時效能下降 |
重複使用單條 PreparedStatement,累積 addBatch(),定期 executeBatch()。
@Autowired
private SqlSessionTemplate sqlSessionTemplate;
public void batchInsert(List users) {
SqlSession session = sqlSessionTemplate.getSqlSessionFactory()
.openSession(ExecutorType.BATCH, false); // 手動提交
try {
UserMapper mapper = session.getMapper(UserMapper.class);
int batchSize = 1000;
for (int i = 0; i < users.size(); i++) {
mapper.insert(users.get(i));
if (i % batchSize == 0 || i == users.size() - 1) {
session.commit(); // 刷新批次
session.clearCache(); // 防止記憶體溢出
}
}
} catch (Exception e) {
session.rollback();
throw e;
} finally {
session.close();
}
}
public void batchInsert(String statement, List> list) {
SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
try {
for (int i = 0; i < list.size(); i++) {
session.insert(statement, list.get(i));
if (i % 1000 == 0 || i == list.size() - 1) {
session.commit();
session.clearCache();
}
}
} finally {
session.close();
}
}
| 優點 | 缺點 |
|---|---|
| 無 SQL 長度限制,支援百萬級資料 | 需手動管理 Session/事務/快取 |
| JDBC 層面批次,資料庫執行計畫重用 | 提交前無法獲取自增 ID |
| 記憶體可控 (定期 clearCache) | 程式碼較繁瑣 |
| 支援更新/刪除同樣批次化 | 若中途失敗,已提交批次無法回滾 |
jdbc:mysql://host:3306/db?rewriteBatchedStatements=true
開啟後,驅動會將多條 executeBatch() 重寫為多值 INSERT,雙重加速!
session.clearCache() 釋放一級快取| 方案 | 耗時 | 備註 |
|---|---|---|
| 逐筆 Insert | ~120 秒 | 基準線 |
| foreach (1000/批) | ~3 秒 | SQL 重寫開啟 |
| BATCH (1000/批) | ~2.5 秒 | 含手動提交/清快取 |
| BATCH + rewriteBatchedStatements | ~1.8 秒 | 最快 |
批次筆數 < 1000 且 記錄輕量
│
├─ 是 → XML foreach (最簡單)
│
└─ 否 → 需要自增 ID 即時獲取?
│
├─ 是 → foreach + useGeneratedKeys (分批小量)
│
└─ 否 → ExecutorType.BATCH
│
├─ MySQL + rewriteBatchedStatements=true (最快)
└─ 其他資料庫 / 混合操作 → BATCH 標準模式
openSession(..., true) 會每次 addBatch 就提交,失去批次效果。new User(),避免同一對象引用導致批次資料重複。<foreach> 首選。ExecutorType.BATCH 必備。rewriteBatchedStatements=true 發揮極致效能。根據業務量級選對方案,批量插入不再是效能瓶頸。