记录一次Mybatis批量运行多条insert语句所遇到的多条bug

这次代码框架很简单,通过spring定时任务,调用enum里的sql语句,通过c3p0连接oracle数据库,使用Mybatis运行,却产生了几个小bug

1、一个枚举中可能会有多条语句,也有可能只有一条,怎么判断逻辑

2、长文本sql无法写入枚举的String字符串内,报“常量字符串过长”错误

3、多条sql批量执行时,抛出java.sql.BatchUpdateException,报“ORA-17439”,Invalid SQL Type,无效的sql类型错误

4、还是多条sql批量update时,抛出(ORA-01000),最多允许打开的游标数错误

下面是解决方法:

1、一个枚举中可能会有多条语句,也有可能只有一条,怎么判断逻辑

①在使用xml文件的mybatis时,sql语句不能带有分号,否则会报 无效字符 导致编译不通过。
②一条枚举里会有多个delete,insert,select,merge语句,这四种语句都可以通过mybatis标签update运行
③单条sql语句有可能没有结尾的分号
④最后结尾分号以后可能会有误输的空格

根据条件,得出以下解决办法
public void commonAggr(String sql){
        try {
            //排除一条语句无分号的情况
            if(sql.lastIndexOf(";")!=-1){
                //去除最后一个分号后的空格
                sql = sql.substring(0, sql.lastIndexOf(";"));
            }
            //将文本根据分号切割,分次执行
            String[] split = sql.split(";");
            List list = Arrays.asList(split);
            for (String s : list) {
            	//使用update方法,可以适应任意语句
               this.commonMapper.update(s);
            }
        } catch (final Throwable e) {
            e.printStackTrace();
            throw e;
        }
    }

2、长文本sql无法写入枚举的String字符串内,报“常量字符串过长”错误

String类型底层是由char类型数组实现的,在class文件的规范中, CONSTANT_Utf8_info表中使用一个16位的无符号整数来记录字符串的长度的,最多能表示 65536个字节,而java class 文件是使用一种变体UTF-8格式来存放字符的,null值使用两个字节来表示,因此只剩下 65536- 2 = 65534个字节。也正是变体UTF-8的原因,如果字符串中含有中文等非ASCII字符,那么双引号中字符的数量会更少(一个中文字符占用三个字节)。

在核查字符个数后,有160000+个字符,这里网上大部分提出方法是使用StringBuilder存储,之后再toString().spilt(",")。在我这边着实不适用。那么剩下两个方法:

①将多条sql分开存放为多个枚举,保证字节数不超
②网上的另一种方法:https://blog.csdn.net/qq_37954693/article/details/84633338
这个方法只能治标不治本。编译不会出错了,但是在oracle中运行时,也会报错

3、多条sql批量执行时,抛出java.sql.BatchUpdateException,报“ORA-17439”,Invalid SQL Type,无效的sql类型错误

记录一次Mybatis批量运行多条insert语句所遇到的多条bug_第1张图片
根据API中描述,这个报错会同时提供已执行成功的命令条数。
在这里插入图片描述
查看第12条sql语句
记录一次Mybatis批量运行多条insert语句所遇到的多条bug_第2张图片
删了",成功运行,这错误着实弱智

4、还是多条sql批量update时,抛出(ORA-01000),最多允许打开的游标数错误

首先是,游标是什么?
用来查询数据库,获取记录集合(结果集)的指针
通过show parameter open_cursors发现游标数为300
网上大部分的解决办法是
直接修改数据库游标数量:https://blog.csdn.net/tomcat6666712/article/details/73466091,但我没有那个权限
②用jdbc的话,在每一次PreparedStatement执行后关闭连接,释放游标。或者重复使用同一个PreparedStatement,但我用的mybatis
③没有找到c3p0相关释放游标的配置
事务进行提交操作,但是我是使用一个容器管理的事务,则对自由的提交事务恐怕很难办到
调用BatchExecutor刷新操作flushStatements,除非SqlSession是程序手工创建的,否则程序通过创建好的SqlSession是不能获取BatchExecutor对象的,因此没有办法调用它的flushStatements
等到下一下查询语句的到来,BatchExecutor有一个特点,虽然它在执行更新操作(insert, update)的时候会缓存批量的语句,但是一旦它碰到一个查询语句它就会立刻刷新此缓存,将先前缓存的批量语句全部执行.

在翻阅好多资料后,发现,这一段sql均为insert语句,不管怎么分段,只要处于同一事务下,都会执行一条语句就新建一个游标,所以,在一段insert之后,插入一条select 1 from dual 等任意select 语句,就会将之前的游标全部释放掉了

你可能感兴趣的:(小知识点)