1. 问题背景与现象分析
最近在开发一个批量数据导入功能时,遇到了一个棘手的问题:在异步线程中调用MyBatis-Plus的saveBatch方法进行批量插入,方法返回了true,但数据库中却没有新增记录。这个问题让我花费了不少时间排查,最终发现是事务管理不当导致的。下面我将详细记录这个问题的排查过程和解决方案。
1.1 问题现象重现
当时的使用场景是这样的:我们需要处理一个包含164条记录的Excel文件,为了提高处理效率,决定使用线程池异步执行数据保存操作。核心代码如下:
java复制// 在线程池中异步执行
executorService.execute(() -> {
boolean saveResult = service.saveBatch(entityList);
// saveResult返回true,但数据库中没有记录
});
从表面上看,saveBatch方法执行成功了(返回true),但实际检查数据库时却发现没有新增数据。这种"假成功"的现象特别具有迷惑性,也是我们排查问题的难点所在。
1.2 初步排查思路
面对这个问题,我首先列出了所有可能的假设:
- 异步线程事务问题:异步执行是否影响了事务提交?
- 数据构建问题:实体对象字段是否构建正确?
- 关联数据问题:是否存在关联数据查询返回null的情况?
- 必填字段缺失:实体对象是否缺少必填字段?
- saveBatch实现问题:方法返回true但实际未执行插入?
- 事务隔离问题:是否因为事务隔离级别导致查询不到新数据?
- 异常被吞没:异步线程中的异常是否被静默处理了?
- 线程未执行:线程池任务是否真的被执行了?
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 详细排查过程
2.1 添加调试日志
为了验证上述假设,我在关键位置添加了详细的日志记录:
java复制executorService.execute(() -> {
log.info("异步线程开始执行,线程名: {}", Thread.currentThread().getName());
// 记录实体数据状态
entityList.forEach(entity ->
log.info("实体数据,id: {}, status: {}", entity.getId(), entity.getStatus()));
log.info("准备调用saveBatch,listSize: {}", entityList.size());
boolean saveResult = service.saveBatch(entityList);
log.info("saveBatch执行结果,saveResult: {}, listSize: {}", saveResult, entityList.size());
// 立即查询验证
long actualCount = service.count(Wrappers.lambdaQuery(Entity.class)
.eq(Entity::getBatchId, batchId));
log.info("验证插入结果,expectedCount: {}, actualCount: {}", entityList.size(), actualCount);
});
2.2 日志分析结果
通过日志分析,我们发现了几个关键现象:
- 异步线程确实执行了(有开始执行的日志)
- 实体对象的所有字段都构建正确(日志显示所有字段都有值)
- saveBatch方法确实被调用(传入164条记录)
- saveBatch返回true
- 查询结果更奇怪:expectedCount=1
