JDBC 批量插入:MyBatis、PostgreSQL
当一次插入数据很多时,使用批量插入可以显著提升性能,在此以 PostgreSQL 为例介绍几种批量插入的方式。
JDBC batch execute
使用 JDBC 时,可以使用 或 方法来将SQL语句加入批量列表,然后再通过 方法来批量执行。
reWriteBatchedInserts=true
PostgreSQL JDBC 驱动支持 连接参数,可以将多条插入/更新语句修改成单条语句执行,如: 修改为 。这可提供2到3倍的性能提升。
注意:executeBatch 返回值
@Test
public void batchInsert() {
int[] rets = jdbcTemplate.batchUpdate("insert into test(id, name) values (?, ?)", Arrays.asList(
new Object[]{1, "羊八井"},
new Object[]{2, "杨景"},
new Object[]{3, "yangbajing"}
));
System.out.println(Arrays.toString(rets));
}
Mybatis
使用
INSERT INTO test (name, content) VALUES
(#{item.name}, ${item.content})
使用 mybatis-plus 的 IService
通过 的 方法可实现批量插入功能,默认将按每 1000 条记录进行提交执行(非事物提交,如:3700 条记录将分 4 次执行 ,但仍在一个事物里)。
自定义 ,获得批处理影响的行数
mybatis-plus 的 默认返回 ,可以自定义实现一个 函数返回批量执行影响的行数
DataIService
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
public interface DataIService extends IService {
int insertBatch(List entityList, int batchSize);
default boolean insert(T entity) {
return save(entity);
}
}
DataIServiceImpl
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.session.SqlSession;
import java.sql.Statement;
import java.util.*;
import java.util.function.BiConsumer;
public class DataIServiceImpl, T>
extends ServiceImpl
implements DataIService {
@Override
public int insertBatch(List entityList, int batchSize) {
if (CollectionUtils.isEmpty(entityList)) {
return 0;
}
String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
List rets =
batchExecute(entityList,
batchSize,
(sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));
return rets.stream()
.mapToInt(result -> Arrays.stream(result.getUpdateCounts())
.map(n -> n == Statement.SUCCESS_NO_INFO ? 1 : n).sum())
.sum();
}
protected List batchExecute(Collection list,
int batchSize,
BiConsumer consumer) {
Assert.isFalse(batchSize < 1, "batchSize must not be less than one");
if (list.isEmpty()) {
return Collections.emptyList();
}
final List results = new LinkedList<>();
executeBatch(sqlSession -> {
int size = list.size();
int i = 1;
for (E element : list) {
consumer.accept(sqlSession, element);
if ((i % batchSize == 0) || i == size) {
List rets = sqlSession.flushStatements();
results.addAll(rets);
}
i++;
}
});
return results;
}
}
对 进行聚合计数获得受影响的行数时需要注意判断 返回的 元素值是否为 。