Mybatis-plus实现批量插入
引言
食用此文章的前提:学习了Spring、学习了Mybatis-plus
正文
使用 mybatis-plus 时在控制台输出SQL语句的配置项是:
# mybatis-plus配置控制台打印完整带参数SQL语句
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
saveBatch
mybatis-plus 的批量插入方法 saveBatch
在实现的时候,似乎是逐条插入的:
// ServiceImpl类中
public boolean saveBatch(Collection<T> entityList, int batchSize) {
String sqlStatement = this.getSqlStatement(SqlMethod.INSERT_ONE);
return this.executeBatch(entityList, batchSize, (sqlSession, entity) -> {
// 逐条插入
sqlSession.insert(sqlStatement, entity);
});
}
网上的博客是这么分析的。我在调用 saveBatch
方法进行批量插入时,通过打印出SQL语句发现它确实是逐条进行插入的:
从图中可以看出实际执行的SQL语句是 INSERT INTO user ( user_name ) VALUES ( ? )
,它只插入了一条数据
InsertBatchSomeColumn
有时候,有必要实现批量插入来提高插入数据的效率,mybatis-plus 预留了一个用以实现真正的批量插入的扩展插件: InsertBatchSomeColumn
,它继承了 AbstractMethod
父类(我们也可以继承此类来实现自定义的SQL插件)
用法如下
①首先我们需要实现一个自定义的 SQL 注入器,注入 InsertBatchSomeColumn
插件:
/**
* SQL注入器
*
* @author spice
* @date 2021/07/12 20:24
*/
@Component
public class SpiceSqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
// 注意:此SQL注入器继承了DefaultSqlInjector(默认注入器),调用了DefaultSqlInjector的getMethodList方法,保留了mybatis-plus的自带方法
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
// 注入InsertBatchSomeColumn
// 在!t.isLogicDelete()表示不要逻辑删除字段,!"update_time".equals(t.getColumn())表示不要字段名为 update_time 的字段
methodList.add(new InsertBatchSomeColumn(t -> !t.isLogicDelete() && !"update_time".equals(t.getColumn())));
return methodList;
}
}
这里稍微暂停一下,我们可能有一个疑惑:为什么 mybatis-plus 没有给我们提供真正的批量插入功能,而是要我们自己注入?
在 InsertBatchSomeColumn
类的类注释上面,官方有说明:**不同的数据库支持度不一样!!! 只在 mysql 下测试过!!! 只在 mysql 下测试过!!! 只在 mysql 下测试过!!!除了主键是数据库自增的未测试外理论上都可以使用!!! 如果你使用自增有报错或主键值无法回写到entity,就不要跑来问为什么了,因为我也不知道!!! **
推测可能是这个批量插入的实现仍不完善,所以官方没有明确支持这个功能,而是要我们自己来注入以了解其中的利害吧
详情可看 -> 传送门
②定义一个自定义的 BaseMapper
接口并添加批量插入方法:
/**
* @author spice
* @date 2021/07/14 10:14
*/
public interface SpiceBaseMapper<T> extends BaseMapper<T> {
/**
* 批量插入
* {@link com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn}
*
* @param entityList 要插入的数据
* @return 成功插入的数据条数
*/
int insertBatchSomeColumn(List<T> entityList);
}
这里的批量插入方法 int insertBatchSomeColumn(List<T> entityList)
的方法名 insertBatchSomeColumn
必须和注入的 InsertBatchSomeColumn
类的id是一样的(在此示例中已经是一样的了),不然在容器中找不到对应实体,会报错:org.apache.ibatis.binding.BindingException: Invalid bound statement (not found)
。不知道我这里的解释各位是否明白,如果不明白可以在评论区问我
③想要使用批量插入方法的dao层mapper接口继承我们自定义的 BaseMapper
:
/**
* @author spice
* @date 2021/07/14 10:18
*/
@Mapper
public interface SpiceMapper extends SpiceBaseMapper<User> {
}
这样就可以通过 SpiceMapper
使用 insertBatchSomeColumn
方法了
在调用这个插入方法并在控制台输出SQL语句如下:
可以看到 insert 语句确实是批量插入语句
本人的不完善测试如下
①针对于插入速度:
循环20次,每次插入20条数据,总计插入400条数据时,saveBatch
平均耗时1000ms左右, insertBatchSomeColumn
平均耗时600ms左右。这个速度上的提升还是挺明显的,也算的上是一个小优化,而且在插入的数据量越大的时候,这个优化更明显
②针对自增主键:
既然官方有说此插入对自增字段的支持不友好,自然是要测试一下的。但是经过测试发现,用 insertBatchSomeColumn
进行批量插入时,自增主键是正常自增的,在实体对象中也会回显主键值,应该、可能、大概可以断言对自增主键而言,此插入方法没有问题
最后
我的测试并不完善,各位在使用此批量插入方法之前可以自己测试一下,看看是否有异常、是否满足自己的需要。官方既然说此批量插入方法存在问题,那我们还是要留个心眼才行,当发现插入数据出现问题时,记得tp回来看一看是否是此方法导致的问题
更多推荐
所有评论(0)