近期在某项目中在使用mybatis 的foeach标签向gbase8s数据库插入数据库报错
Caused by: java.sql.SQLException: 主变量号无效。
at com.gbasedbt.util.IfxErrMsg.getSQLException(IfxErrMsg.java:409)
at com.gbasedbt.jdbc.IfxSqli.a(IfxSqli.java:3576)
at com.gbasedbt.jdbc.IfxSqli.D(IfxSqli.java:3856)
at com.gbasedbt.jdbc.IfxSqli.dispatchMsg(IfxSqli.java:2746)
at com.gbasedbt.jdbc.IfxSqli.receiveMessage(IfxSqli.java:2671)
at com.gbasedbt.jdbc.IfxSqli.executePrepare(IfxSqli.java:1292)
at com.gbasedbt.jdbc.IfxPreparedStatement.f(IfxPreparedStatement.java:490)
at com.gbasedbt.jdbc.IfxPreparedStatement.a(IfxPreparedStatement.java:471)
at com.gbasedbt.jdbc.IfxPreparedStatement.<init>(IfxPreparedStatement.java:273)
at com.gbasedbt.jdbc.IfxSqliConnect.h(IfxSqliConnect.java:6348)
at com.gbasedbt.jdbc.IfxSqliConnect.prepareStatement(IfxSqliConnect.java:2552)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.ibatis.datasource.pooled.PooledConnection.invoke(PooledConnection.java:255)
at com.sun.proxy.$Proxy7.prepareStatement(Unknown Source)
...
其原因为在使用 foreach 标签做数据插入时,本质是将集合中的数据拼接成一个大sql,如果sql中使用了#{},则超出 32765 报错主机变量不足,oracle 的上限为65535,mysql中 当整体 sql长度超出范围后一样会报错
解决方案
1减少一次传入集合的数量,保证整体sql 绑定变量不超过32765
2使用 ${}代替#{}
3使用mybatis 推荐的ExecutorType.BATCH 方式去批量插入
InputStream inputStream = Resources.getResourceAsStream(resource);
sf = new SqlSessionFactoryBuilder().build(inputStream);
sqlsession = sf.openSession(ExecutorType.BATCH);
TableOneMapper tableOneMapper = sqlsession.getMapper(TableOneMapper.class);
List<TableTwo> list =new ArrayList<>();
for (int i=1;i<= 20000;i++){
list.add(new TableTwo(i, UUID.randomUUID().toString(),UUID.randomUUID().toString()));
}
list.stream().forEach(tableTwo -> tableOneMapper.instTab3(tableTwo));
sqlsession.commit();
sqlsession.close();
在 gbase8s 某些版本中,使用foreach 标签做插入时不支持 byte,text,clob,blob,若使用方案3 则没有问题,并且ExecutorType.BATCH 的效率要远远高于其他方式
因为ExecutorType.BATCH 底层使用的是PreparedStatement.addBatch(),先将部分数据缓存起来,一起批量提交,极大的减少了客户端与数据库的交互
从数据库协议中观察不难发现
使用ExecutorType.BATCH 时 只做一次PREPARE,多次 SQ_BIND,并且返回结果也是批量一起返回,而不使用批量插入则每一条数据都需要先PREPARE,SQ_BIND,返回结果