引言

食用此文章的前提:学习了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语句发现它确实是逐条进行插入的:
saveBatch
从图中可以看出实际执行的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回来看一看是否是此方法导致的问题

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐