如何在MyBatis中抑制自动语句刷新



背景

我想用Spring和MyBatis实现以下过程:

  1. 从CSV文件中读取一行
  2. 使用从CSV文件中读取的值查询表(表ACCOUNT)
  3. 将CSV文件和表ACCOUNT中的值合并到一条记录中
  4. 将合并后的记录插入另一个表(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);
}
}

我期望INSERTSQL与JDBCStatement#addBatch()一起排队,并在事务结束时以批处理方式执行。

但实际上,当我用AccountDao#findAccount()查询ACCOUNT表时,MyBatis会自动用BatchExecutor#doFlushStatements()刷新每条语句,所以所有的INSERTSQL都是单独执行的。

在这种情况下,ACCOUNTREGISTRATION没有关系,因此您可以安全地将更新语句排队并批量执行。

问题

当开发人员知道查询的表与更新的表没有关系时,是否有任何方法可以抑制排队语句的自动刷新?

行为解释

批处理不起作用,因为只有在执行相同查询时才能执行批处理。但在您的代码中,有两个查询相继执行:selectinsert

因此,每个select都会中断批处理,因为MyBatis对您试图查询的内容一无所知。它能做的最好的事情是刷新所有以前的插入,这样查询就可以在实际的数据库状态上执行

解决方案

只有一种方法可以选择:在循环中选择一批帐户,在嵌套循环中插入

AccountDao accountDao = applicationContext.getBean("accountDao", AccountDao.class);
RegistrationDao registrationDao = applicationContext.getBean("registrationDao", RegistrationDao.class);
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(path), "UTF-8"))) {
List<Registration> registrations = new CsvToBeanBuilder<Registration>(reader)
.withType(Registration.class)
.build()
.parse();
for (List<Registration> batch : Lists.partition(registrations, 500)) {
Map<Integer, Account> accounts = Maps.uniqueIndex(
accountDao.getAccount(batch.stream()
.map(Registration::getAccountId)
.toList()),
Account::getId
);
for (Registration registration : batch) {
Account account = accounts.get(reg.getAccountId());
reg.setName(account.getName());
...
registrationDao.insertRegistration(reg);
}
}
}

为什么没有这样的机制

IMO,这是非常危险的,因为作为一个原始开发人员,你可能肯定知道它是安全的,但另一个开发人员可能不知道。他甚至可能没有注意到这种配置。然后添加一些代码,打破你的不变量。而且很难意识到错误在哪里

最新更新