一、报错信息
Mybatis + PostgreSQL-JDBC-Driver 42.1.4批量插入24178条数据(每条30字段).报如下错误:
PgSQL 9.6
Cause: org.postgresql.util.PSQLException: An I/O error occurred while sending to the backend.
; SQL []; An I/O error occurred while sending to the backend.; nested exception is org.postgresql.util.PSQLException: An I/O error occurred while sending to the backend.
Tried to send an out-of-range integer as a 2-byte value: 647430
二、报错原因
- 这里是JDBC-Driver 一条SQL参数数量达到了上限,幂增操作的参数=记录数(行)插入的字段数(列)。
- JDBC-Driver 支持的参数数量跟版本(x.y.z)和类型(xx数据库)有关。
三、解决思路
分割成小段
- JDK 8 版本
- 示例代码
/**
* <code>Details determine success.</code>
* by Liang ZC., Phd@Stanford
*
* @author LIANGZHICHENG
* @date 2019-8-9 18:03
* @see http://www.stanford.edu
*/
public class TestUtils {
@Autowired
private SimilarMapper similarMapper;
/**
* 对列表分批执行指定函数。
*
* @param items 待处理列表
* @param batchSize 批量处理数
* @param consumer 处理函数
* @param <T> 处理类型
*/
public static <T> void batchDo(List<T> items,
int batchSize,
Consumer<List<T>> consumer) {
Preconditions.checkState(batchSize > 1, "batch should be greater than 1");
if (CollectionUtils.isEmpty(items) || consumer == null) {
return;
}
List<List<T>> group = ListUtils.partition(items, batchSize);
group.forEach(consumer);
}
@Test
public void testBatchDo() {
List<Integer> ids = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
ids.add(i);
}
// 分批执行,每批次取100条
LangUtil.batchDo(ids, 100, batchIds -> {
similarMapper.update(batchIds);
});
}
}
- JDK 7 版本
@Autowired
private ChPeriodicalThesisMapper thesisDao;
//其他CRUD...
/**
* .
* TODO 批量插入
* @param thesisList
* @return
* @throws Exception
*/
public int insertList(List<ChPeriodicalThesis> thesisList) throws Exception {
return thesisDao.insertList(thesisList);
}
/**
* .
* TODO 递归:分割长List为 subNum/段。
* @param thesisList 论文list(总)
* @param subNum 每段长度 (最小1)
* @return
* @throws Exception
*/
private int recurSub(List<ChPeriodicalThesis> thesisList,int subNum) throws Exception{
//参数合法性判断:
if(thesisList.isEmpty()) return 0;
if(subNum<1) return 0;
//大于subNum,进入分割
if(thesisList.size() > subNum) {// && !(thesisList.isEmpty())
//将前subNum分出来,直接插入到数据库。
List<ChPeriodicalThesis> toInsert = thesisList.subList(0, subNum);
//将subNum至最后 (剩余部分) 继续进行递归分割
List<ChPeriodicalThesis> toRecurSub = thesisList.subList(subNum, thesisList.size());
//将前subNum分出来,直接插入到数据库 && 将subNum至最后 (剩余部分) 继续进行递归分割 。统计数量
return insertList(toInsert) + recurSub(toRecurSub,subNum);
//少于subNum,直接插入数据库 (递归出口)
}else {
//插入到数据库。统计数量
return insertList(thesisList);
}
}
/**
* .
* TODO 将数据流读取并批量插入
* @param in
* @return
* @throws Exception
*/
public int readStreamAndInsertList(InputStream in) throws Exception {
FileUtil fileUtil = new FileUtil();
List<ChPeriodicalThesis> thesisList = fileUtil.importFileOfChPeriodicalThesis(in);
//每1500为一段 插入
return recurSub(thesisList,1500);
}