You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何抑制MyBatis执行无关查询时自动刷新批处理语句

问题背景

我需要基于Spring和MyBatis实现如下数据处理流程:

  • 逐行读取CSV文件数据
  • 使用CSV中读取到的字段值查询表ACCOUNT
  • 将CSV文件数据与ACCOUNT表的查询结果合并为一条记录
  • 将合并后的记录插入另一张表REGISTRATION

由于该流程需要插入大量记录,我计划使用批处理执行器(配置为<setting name="defaultExecutorType" value="BATCH"/>)提升执行性能。

遇到的问题

我按如下方式实现了该处理流程:

AccountDao accountDao = applicationContext.getBean("accountDao", AccountDao.class);
RegistrationDao registrationDao = applicationContext.getBean("registrationDao", RegistrationDao.class);

List<Registration> list = null;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(path), "UTF-8"))) {

    CsvToBeanBuilder<Registration> builder = new CsvToBeanBuilder<Registration>(reader);
    builder.withType(Registration.class);

    list = builder.build().parse();
    for (Registration reg : list) {
        Account account = accountDao.findAccount(reg.getAccountId());
        reg.setName(account.getName());
        // 其余字段赋值逻辑
        registrationDao.insertRegistration(reg);
    }
}

我原本预期INSERT SQL会通过JDBC的Statement#addBatch()方法加入批处理队列,在事务结束时统一批量执行。

但实际运行时,当我调用AccountDao#findAccount()查询ACCOUNT表时,MyBatis会自动调用BatchExecutor#doFlushStatements()方法刷新所有已缓存的语句,导致所有INSERT SQL被单条执行,批处理未生效。

在当前场景下,ACCOUNT表与REGISTRATION表无关联,完全可以将更新语句留在队列中后续统一批量执行。

咨询问题

当开发者明确确认查询的表与待执行更新的表无关联时,是否有方法可以抑制MyBatis自动刷新已排队的批处理语句?


解答

MyBatis原生BatchExecutor在执行查询前默认刷新待执行批处理语句,本质是为了保证查询能读到当前事务中未提交的更新数据,避免出现同事务内查询不到自己刚写入数据的问题。如果明确查询和批处理更新的表无关联,不需要读取未提交的更新数据,可以通过以下两种方案解决:

方案1:拆分SqlSession隔离查询与批处理操作

这是最稳妥无副作用的方案。不要让ACCOUNT表的查询和REGISTRATION表的批插入共用同一个开启了BATCH模式的SqlSession,给查询操作单独分配一个使用默认SIMPLE执行器的SqlSession,两个会话的操作互不干扰,查询时自然不会触发批处理队列的刷新。
在Spring集成MyBatis的场景下实现成本很低,只需要给accountDao单独配置绑定SIMPLE执行器的SqlSessionTemplate即可,不需要改动现有循环处理的业务逻辑。

方案2:自定义批处理执行器修改默认刷新逻辑

如果不想拆分会话,也可以自定义Executor实现:继承原生BatchExecutor重写查询方法,去掉查询前强制调用doFlushStatements()的逻辑,再把这个自定义Executor配置到MyBatis中,全局替换原生批处理执行器。
注意:该方案会全局改变批处理执行器的行为,必须确保所有批处理场景下的查询都不需要读取同事务内未提交的更新数据,否则会出现查询结果与预期不一致的问题。

额外提一个实践优化:当前实现是先把整个CSV全量解析到内存再循环处理,如果CSV文件体量较大很容易占用过多内存,更合理的做法是逐行解析CSV、逐行组装数据,攒够固定批次(通常500~1000条)就手动调用一次sqlSession.flushStatements(),既能控制内存占用,也能稳定发挥批处理的性能优势。


内容的提问来源于stack exchange,提问作者SATO Yusuke

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 09:51:44