mybatisplus-伪批量插入saveBatch

伪批量插入

首先我们讲讲为什么会将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 批量插入-优化&效率对比

### 关于 SaveBatch 的概念 在编程和数据处理上下文中,`SaveBatch` 通常指的是批量保存操作。这种技术用于高效地将多个记录或数据项一次性存储到数据库或其他持久化介质中,而不是逐条执行保存操作。这种方式可以显著提高性能并减少资源消耗。 #### 批量保存的核心特点 批量保存的主要目标是优化 I/O 操作效率。通过一次性的事务提交,减少了多次单独调用所带来的开销[^1]。具体来说,在多线程环境中使用 `synchronized` 可以确保对共享资源的操作具备原子性和可见性特性,从而避免并发冲突问题的发生[^2]。 #### 实现方式举例 以下是基于 Java 编写的简单示例代码片段展示如何实现基本的批处理逻辑: ```java import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class BatchProcessor { public void saveBatch(Connection connection, List<MyData> dataList) throws SQLException { String sql = "INSERT INTO my_table (column1, column2) VALUES (?, ?)"; try (PreparedStatement pstmt = connection.prepareStatement(sql)) { for (MyData data : dataList) { pstmt.setString(1, data.getField1()); pstmt.setInt(2, data.getField2()); pstmt.addBatch(); // 控制每批次大小 if (dataList.indexOf(data) % 50 == 0 && dataList.indexOf(data) != 0){ pstmt.executeBatch(); } } // 提交剩余部分 pstmt.executeBatch(); } catch (SQLException e) { throw new RuntimeException("Error during batch saving", e); } } } ``` 此方法展示了利用 JDBC API 进行大批量插入的过程,并且包含了必要的错误处理机制以及合理的分组策略以便更好地管理内存占用情况。 ### 数据一致性保障措施 为了维持跨平台开发灵活性的同时还能保持良好的用户体验,像 Processing 工具所提供的多种模式选项就显得尤为重要了[^3]。当涉及到复杂的业务场景时,则可能还需要额外考虑诸如分布式锁之类的高级解决方案来进一步增强系统的稳定性和可靠性。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

常有理_

老板们赏点元子吧

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

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

打赏作者

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

抵扣说明:

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

余额充值