伪批量插入

首先我们讲讲为什么会将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 entityList, int batchSize)方法

public interface IService {

/**

* 默认批次提交数量

*/

int DEFAULT_BATCH_SIZE = 1000;

/**

* 插入(批量)

*

* @param entityList 实体对象集合

*/

@Transactional(rollbackFor = Exception.class)

default boolean saveBatch(Collection entityList) {

return saveBatch(entityList, DEFAULT_BATCH_SIZE);

}

/**

* 插入(批量)

*

* @param entityList 实体对象集合

* @param batchSize 插入批次数量

*/

boolean saveBatch(Collection 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, T> implements IService {

...

/**

* 批量插入

*

* @param entityList ignore

* @param batchSize ignore

* @return ignore

*/

@Transactional(rollbackFor = Exception.class)

@Override

public boolean saveBatch(Collection 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 泛型

* @return 操作结果

* @since 3.3.1

*/

protected boolean executeBatch(Collection list, int batchSize, BiConsumer consumer) {

return SqlHelper.executeBatch(this.entityClass, this.log, list, batchSize, consumer);

}

}

 下方源码中我们重点关注executeBatch(Class entityClass, Log log, Collection list, int batchSize, BiConsumer 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 T

* @return 操作结果

* @since 3.4.0

*/

public static boolean executeBatch(Class entityClass, Log log, Collection list, int batchSize, BiConsumer 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 批量插入-优化&效率对比

参考阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: