1.问题分析
项目里有一个从MySQL导入PostgreSQL然后利用GIS相关插件计算空间数据的定时任务,上线某地市没有任何问题,后期上线到一个大城市,定时任务报错 java.io.IOException: Tried to send an out-of-range integer as a 2-byte value: xxxxx,这里贴一下源码:
public void sendInteger2(int val) throws IOException { if (val >= -32768 && val <= 32767) { this.int2Buf[0] = (byte)(val >>> 8); this.int2Buf[1] = (byte)val; this.pgOutput.write(this.int2Buf); } else { throw new IOException( "Tried to send an out-of-range integer as a 2-byte value: " + val ); } }
大白话解释是:试图以2字节值的形式发送一个超出范围的整数 xxxxx,第一时间我没有反应过来,查询了MySQL数据库的记录数,也就1w+条,怎么也超不过32767啊,后来才知道32767是PostgreSQL对于SQL语句的参数数量限制,当时往PostgreSQL入库的SQL类似这种:
<insert id="batchInsertXXX" parameterType="xxx.common.persistence.model.xxxGis"> insert into xxx_gis (id, name, index, geom) values <foreach collection="list" index="index" item="item" separator=","> ( #{item.id}, #{item.name}, #{item.index}, ST_GeomFromText(#{item.geom}) ) </foreach> </insert>
然后一算1w+*4可不超过32767嘛!
2.解决方法代码
我查询了一下网络,遇到这个问题的小伙伴还是不少的,大家的方法就是分批导入,代码如下。
public void insertBatch(List<Object> list){ int numberBatch = 32767; // PostgreSQL每一次插入最大参数量 double number = list.size() * 4.0 / numberBatch; // 4.0是每条插入语句的参数个数 int n = ((Double)Math.ceil(number)).intValue(); for(int i = 0; i < n; i++){ int end = numberBatch * (i + 1); if(end > list.size()){ end = list.size(); } List<Object> insertList = list.subList(numberBatch * i , end); // 这里调用批量插入程程序将insertList保存 } }
这个是临时解决方案,里边把每条插入语句的参数个数值固定了,可以将这个方法封装,然后把参数个数参数化。