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