baomidou/mybatis-plus · error · RuntimeException
Batch execute failed.
Error message
Batch execute failed.
What it means
SqlHelper.executeBatch flushes JDBC batch statements and inspects each BatchResult's updateCounts. A count of Statement.EXECUTE_FAILED (-3) means the driver reports that an individual batch member failed, and mybatis-plus throws RuntimeException('Batch execute failed.'). Note the loop already treats SUCCESS_NO_INFO (-2) as ok; -3 is a genuine per-statement failure surfaced by the driver after the batch was flushed.
Source
Thrown at mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/toolkit/SqlHelper.java:226
int row = 0;
int size = list.size();
int idxLimit = Math.min(batchSize, size);
int i = 1;
for (E element : list) {
// 执行处理函数
execBiFunc.apply(sqlSession, element);
if (i == idxLimit) {
List<BatchResult> results = sqlSession.flushStatements();
for (BatchResult result : results) {
for (int count : result.getUpdateCounts()) {
if (count > 0) {
row += count;
} else if (count == Statement.SUCCESS_NO_INFO) {
// JDBC 返回 -2,表示执行成功但不知道影响行数
row++;
} else if (count == Statement.EXECUTE_FAILED) {
// JDBC 返回 -3,可根据业务决定是否抛异常
throw new RuntimeException("Batch execute failed.");
}
}
}
idxLimit = Math.min(idxLimit + batchSize, size);
}
i++;
}
return row;
});
}
/**
* 批量更新或保存
*
* @param entityClass 实体
* @param log 日志对象
* @param list 数据集合
* @param batchSize 批次大小View on GitHub (pinned to bf67d90747)
Solutions
- Inspect the underlying BatchUpdateException (usually chained by the sqlSession flush) for the driver's error and the failing statement index.
- Fix the data or the constraint conflict (dedupe input, upsert semantics via ON DUPLICATE KEY / MERGE, or pre-validate FKs).
- Reduce the batch size so a single failure affects a smaller unit, and make the operation transactional so it rolls back cleanly.
- If partial success is acceptable, chunk the batch and handle per-chunk failures rather than one large executeBatch.
Example fix
// before: raw list may contain duplicate keys -> EXECUTE_FAILED at flush
userMapper.insertBatchSomeColumn(allUsers);
// after: dedupe by natural key before batching
List<User> unique = allUsers.stream()
.collect(Collectors.toMap(User::getEmail, Function.identity(), (a, b) -> a))
.values().stream().collect(Collectors.toList());
userMapper.insertBatchSomeColumn(unique); Defensive patterns
Strategy: try-catch
Validate before calling
// pre-validate uniqueness before batching inserts
Set<Object> keys = new HashSet<>();
for (User u : batch) {
if (!keys.add(u.getEmail())) {
throw new IllegalArgumentException("duplicate natural key in batch: " + u.getEmail());
}
} Try / catch
try {
SqlHelper.executeBatch(sqlSessionFactory, log, list, batchSize, (ss, elem) -> ss.update(...));
} catch (RuntimeException e) {
Throwable t = e;
while (t.getCause() != null && !(t instanceof BatchUpdateException)) t = t.getCause();
if (t instanceof BatchUpdateException) {
int[] counts = ((BatchUpdateException) t).getUpdateCounts();
// identify the failing member index from counts
}
throw e;
} Prevention
- Wrap batch operations in a transaction so EXECUTE_FAILED rolls back the whole chunk.
- Pre-validate constraint-relevant data (unique keys, FK targets) before batching.
- Use moderate batch sizes so failures localize to a small chunk.
When it happens
Trigger: Calling saveOrUpdateBatch/deleteByIds-style batch operations (any path through SqlHelper.executeBatch) where one member statement fails at flush time: constraint violation (duplicate key, FK), data too long, or deadlocks, on drivers that continue past errors in a batch and mark counts as -3.
Common situations: Batch inserts hitting unique-key duplicates; batch updates violating FK/check constraints; MySQL rewriteBatchedStatements=true setups where a mid-batch failure marks later counts EXECUTE_FAILED; concurrent modification causing row-level conflicts in batched updates.
Related errors
AI-assisted analysis of baomidou/mybatis-plus@bf67d90747 (2026-08-14).
Data as JSON: /api/errors/48ee4199cf6f586a.
Report an issue: GitHub.