持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天,点击查看活动详情
mybatisPlus批量插入源码解析--探究批量插入如何快:
1、接口类实际就一个批量插入方法方法:public interface IService
`
/**
* 插入(批量)
*
* @param entityList 实体对象集合
*/
@Transactional(rollbackFor = Exception.class)
default boolean saveBatch(Collection<T> entityList) {
return saveBatch(entityList, 1000);
}
/**
* 插入(批量)
*
* @param entityList 实体对象集合
* @param batchSize 插入批次数量
*/
boolean saveBatch(Collection<T> entityList, int batchSize);
2、ServiceImpl实现类,这里注意代码片段,下面一一分析
/**
* 批量插入
*
* @param entityList ignore
* @param batchSize ignore
* @return ignore
*/
@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveBatch(Collection<T> entityList, int batchSize) {
String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
try (SqlSession batchSqlSession = sqlSessionBatch()) {
int i = 0;
for (T anEntityList : entityList) {
batchSqlSession.insert(sqlStatement, anEntityList);
if (i >= 1 && i % batchSize == 0) {
batchSqlSession.flushStatements();
}
i++;
}
batchSqlSession.flushStatements();
}
return true;
}
3、使用SQL的方法--枚举方法列举各种情况--我们探究的是插入
String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
3.1、
---------包和枚举类---------start
package com.baomidou.mybatisplus.core.enums;
public enum SqlMethod
---------包和枚举类---------end
INSERT_ONE("insert", "插入一条数据(选择字段插入)", "<script>\nINSERT INTO %s %s VALUES %s\n</script>"),
4、SqlSession batchSqlSession = sqlSessionBatch()这里使用了ExecutorType.BATCH批量
/**
* 批量操作 SqlSession
*
* @param clazz 实体类
* @return SqlSession
*/
public static SqlSession sqlSessionBatch(Class<?> clazz) {
// TODO 暂时让能用先,但日志会显示Closing non transactional SqlSession,因为这个并没有绑定.
return GlobalConfigUtils.currentSessionFactory(clazz).openSession(ExecutorType.BATCH);
}
5、batchSqlSession.flushStatements();这里其实就是刷数据,也就是在最开始代码当 i 满足条件,并且for循环执行完毕了,才将数据批量刷入到数据库中,否则回滚数据
@Override
public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
try {
List<BatchResult> results = new ArrayList<>();
if (isRollback) {
return Collections.emptyList();
}
for (int i = 0, n = statementList.size(); i < n; i++) {
Statement stmt = statementList.get(i);
applyTransactionTimeout(stmt);
BatchResult batchResult = batchResultList.get(i);
try {
batchResult.setUpdateCounts(stmt.executeBatch());
MappedStatement ms = batchResult.getMappedStatement();
List<Object> parameterObjects = batchResult.getParameterObjects();
KeyGenerator keyGenerator = ms.getKeyGenerator();
if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) {
Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator;
jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);
} else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { //issue #141
for (Object parameter : parameterObjects) {
keyGenerator.processAfter(this, ms, stmt, parameter);
}
}
// Close statement to close cursor #1109
closeStatement(stmt);
} catch (BatchUpdateException e) {
StringBuilder message = new StringBuilder();
message.append(batchResult.getMappedStatement().getId())
.append(" (batch index #")
.append(i + 1)
.append(")")
.append(" failed.");
if (i > 0) {
message.append(" ")
.append(i)
.append(" prior sub executor(s) completed successfully, but will be rolled back.");
}
throw new BatchExecutorException(message.toString(), e, results, batchResult);
}
results.add(batchResult);
}
return results;
} finally {
for (Statement stmt : statementList) {
closeStatement(stmt);
}
currentSql = null;
statementList.clear();
batchResultList.clear();
}
}
6、原理
如何实现批量操作
1.在sql文件中使用进行sql的拼接。这种比较适宜少量的数据插入。
2.使用ExecutorType.BATCH创建SqlSession(源代码也是),但是不能使用自增ID,原因事务未提交。
例:SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
设置ExecutorType.BATCH原理:把SQL语句发个数据库,数据库预编译好,数据库等待需要运行的参数,接收到参数后一次运行,ExecutorType.BATCH只打印一次SQL语句,多次设置参数步骤,