持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第10天,点击查看活动详情
扩展BaseMapper
-
自定义自己的通用方法可以实现接口 ISqlInjector 也可以继承抽象类 AbstractSqlInjector 注入通用方法 SQL 语句 然后继承 BaseMapper 添加自定义方法,全局配置 sqlInjector 注入 MP 会自动将类所有方法注入到 mybatis 容器中。
-
创建通用方法的实现类(InsertIgnore、InsertIgnoreBatch、Replace、updateAllById、insertBatchSomeColumn)
-
InsertIgnore 插入数据实现类,如果中已经存在相同的记录,则忽略当前新数据
-
批量插入数据,如果中已经存在相同的记录,则忽略当前新数据
-
Replace替换数据,如果中已经存在相同的记录,则覆盖旧数据
通过扩展批量解决效率问题
背景
-
由于项目中需要大批量将数据插入数据库,直接使用mybatis-plus中的批量插入方法,结果发现效率奇低无比,线上批量插入一千条数据居然花销八九秒的时间。而我们的目标是想要单次插入一万条数据,这样的效率完全无法接受。
-
继续接着上插入的源码研究,发现底层在SqlHeper类中有个executeBatch的方法有点异常。该方法显示 sqlSession.flushStatements()的调用居然是循环的。也就是说sql层面实际上是一堆insert语句再sqlSession中循环flush,而不是一个大insert一次flush操作完。这就是效率低的本质原因。
##解决方案 因为上述原因,考虑自己写一个批量插入的sql语句,这是最简单的。
但我们此处不采取此方法,而是直接重写sql注入。以下DefaultSqlInjector为mybatis-plus默认的sql注入实现类,继承的是AbstractSqlInjector类。该默认实现类中添加的Insert、Delete、DeleteByMap等等,实际上就是对应Mapper中所调用的各种方法。
public class InsertBatchMethod extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
final String sql = "<script>insert into %s %s values %s</script>";
final String fieldSql = prepareFieldSql(tableInfo);
final String valueSql = prepareValuesSql(tableInfo);
final String sqlResult = String.format(sql, tableInfo.getTableName(), fieldSql, valueSql);
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass, "insertBatch", sqlSource, new NoKeyGenerator(), null, null);
}
private String prepareFieldSql(TableInfo tableInfo) {
StringBuilder fieldSql = new StringBuilder();
fieldSql.append(tableInfo.getKeyColumn()).append(",");
tableInfo.getFieldList().forEach(x -> fieldSql.append(x.getColumn()).append(","));
fieldSql.delete(fieldSql.length() - 1, fieldSql.length());
fieldSql.insert(0, "(");
fieldSql.append(")");
return fieldSql.toString();
}
private String prepareValuesSql(TableInfo tableInfo) {
final StringBuilder valueSql = new StringBuilder();
valueSql.append("<foreach collection="list" item="item" index="index" open="(" separator="),(" close=")">");
valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},");
tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},"));
valueSql.delete(valueSql.length() - 1, valueSql.length());
valueSql.append("</foreach>");
return valueSql.toString();
}
}