mybatisplus-伪批量插入saveBatch

文章探讨了MiBatis中的伪批量插入机制,通过分析`saveBatch`方法和源码,解释了为何称为伪批量,并揭示了其内部逻辑,即当插入数量达到一定阈值时一次性提交,以减少数据库连接的频繁建立和断开。
摘要由CSDN通过智能技术生成

伪批量插入

首先我们讲讲为什么会将saveBatch称之为伪批量插入?

我们现在启动项目,批量插入10条数据看一下,具体的sql输出

我们可以看到日志的输出是一条一条执行出来的,和我们熟知的批量插入的方式并不一致。

批量插入:INSERT INTO table ( col1, col2) VALUES (val1,val2),(val1,val2)...,(val1,val2);

现在我们就有了第二个疑问,既然是单挑执行那么和for循环一条条插入的区别是什么呢?

接下来我们带着这个疑问去查看一下saveBatch的源码

 源码解析

我们进入com.baomidou.mybatisplus.extension.service.IService这个类,下方我粘贴了部分源码

我们可以着重关注一下DEFAULT_BATCH_SIZE 这个参数,也就是默认提交数量;当每次插入数量达到1000的时候就会执行一次就不会进行频繁地建立断开连接,减少耗时。

我们也可以根据实际的场景自定义条数,使用saveBatch(Collection<T> entityList, int batchSize)方法


public interface IService<T> {

    /**
     * 默认批次提交数量
     */
    int DEFAULT_BATCH_SIZE = 1000;

    /**
     * 插入(批量)
     *
     * @param entityList 实体对象集合
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean saveBatch(Collection<T> entityList) {
        return saveBatch(entityList, DEFAULT_BATCH_SIZE);
    }
    
    /**
     * 插入(批量)
     *
     * @param entityList 实体对象集合
     * @param batchSize  插入批次数量
     */
    boolean saveBatch(Collection<T> entityList, int batchSize);
}

下方源码就是循环执行的方法,我们可以看到先获取了一个sqlStatement,那么这是什么呢根据断点可以看到,是这个对象com.cabbage.learn.dynamicdata.dao.mapper.OutboundOrderItemMapper.insert

/**
 * IService 实现类( 泛型:M 是 mapper 对象,T 是实体 )
 *
 * @author hubin
 * @since 2018-06-23
 */
@SuppressWarnings("unchecked")
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {

                            ...
    /**
     * 批量插入
     *
     * @param entityList ignore
     * @param batchSize  ignore
     * @return ignore
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveBatch(Collection<T> entityList, int batchSize) {
        //获取插入语句的 SQL 语句标识;sqlMethod.INSERT_ONE 表示单条插入的 SQL
        //com.cabbage.learn.dynamicdata.dao.mapper.OutboundOrderItemMapper.insert
        String sqlStatement = getSqlStatement(SqlMethod.INSERT_ONE);
        //循环执行,进行插入的方法
        //com.cabbage.learn.dynamicdata.dao.mapper.OutboundOrderItemMapper.insert
        return executeBatch(entityList, batchSize, (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));
    }
    
    /**
     * 获取mapperStatementId
     *
     * @param sqlMethod 方法名
     * @return 命名id
     * @since 3.4.0
     */
    protected String getSqlStatement(SqlMethod sqlMethod) {
        return SqlHelper.getSqlStatement(mapperClass, sqlMethod);
    }
    
    /**
     * 执行批量操作
     *
     * @param list      数据集合
     * @param batchSize 批量大小
     * @param consumer  执行方法
     * @param <E>       泛型
     * @return 操作结果
     * @since 3.3.1
     */
    protected <E> boolean executeBatch(Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
        return SqlHelper.executeBatch(this.entityClass, this.log, list, batchSize, consumer);
    }

}

 下方源码中我们重点关注executeBatch(Class<?> entityClass, Log log, Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer)这个方法

我们可以看到这个方法中有for循环,可以验证我们之前说的,当满足我们设定的条数后会调用sqlSession.flushStatements()这个方法进行插入

/**
 * SQL 辅助类
 *
 * @author hubin
 * @since 2016-11-06
 */
public final class SqlHelper {

    /**
     * 获取mapperStatementId
     *
     * @param sqlMethod 方法名
     * @return 命名id
     * @since 3.4.0
     */
    public static String getSqlStatement(Class<?> mapper, SqlMethod sqlMethod) {
        return mapper.getName() + StringPool.DOT + sqlMethod.getMethod();
    }
    
    /**
     * 执行批量操作
     *
     * @param entityClass 实体类
     * @param log         日志对象
     * @param list        数据集合
     * @param batchSize   批次大小
     * @param consumer    consumer
     * @param <E>         T
     * @return 操作结果
     * @since 3.4.0
     */
    public static <E> boolean executeBatch(Class<?> entityClass, Log log, Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
        Assert.isFalse(batchSize < 1, "batchSize must not be less than one");
        //
        return !CollectionUtils.isEmpty(list) && executeBatch(entityClass, log, sqlSession -> {
            int size = list.size();
            int i = 1;
            for (E element : list) {
                consumer.accept(sqlSession, element);
                if ((i % batchSize == 0) || i == size) {
                    sqlSession.flushStatements();
                }
                i++;
            }
        });
    }    
    
}

想了解更多的请关注博主另一篇文章MyBatis 批量插入-优化&效率对比

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

常有理_

老板们赏点元子吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值