没有设置jdbcType的话Mybatis不能将null设置为sql语句的参数值(Oracle)

今天遇到一个问题,数据库用的oracle,先简单描述一下,如果没有设置参数的jdbcType,Mybatis不能将null值设置为待执行sql语句的参数值,再详细描述一下发现问题的经过:

之前用Mybatis时,在xml文件中写SQL的时候,常会用到<if test="">这样的判断条件,如下:

一大堆,之前对<if>标签的理解本质上就是看条件拼SQL,条件成立了就拼上这一句,不符合条件就不要这句了SQL还短一点。其实不只是这样,如果用oracle的ojdbc包,不写这个条件判断可能会抛出异常。

是这样的,有个场景 ,需要更新字段的值,如下:

 当时想的是字段不加这个<if test="">,如果传进来的是null那就直接更新为null呗,还特意去数据库试了一下update XXX set XXX = null是可以执行的,然后开开心心的一跑居然抛异常了,异常如下,只截取Cause By部分:

 通过异常线程栈信息可以定位到最先抛出异常的地方时OracleStatement.getInternalType()这个方法,而且这个异常信息“无效的列类型1111”似乎有点常见,那就找到这个方法看看,这个方法里主要是switch带了一长串的case,不方便截图直接省略着写出来:

int getInternalType(int var1) throws SQLException {
        boolean var2 = false;
        short var4;
        switch(var1) {
        case -104:
            var4 = 183;
            break;
        case -103:
            var4 = 182;
            break;
        case -102:
            var4 = 231;
            break;
        case -101:
            var4 = 181;
            break;
        case -100:
        case 93:
            var4 = 180;
            break;
        case -16:
        case -1:
            var4 = 8;
            break;
        case -15:
        case -9:
        case 12:
            var4 = 1;
            break;
        case -14:
            var4 = 998;
            break;
        case -13:
            var4 = 114;
            break;
        case -10:
            var4 = 102;
            break;
        case -8:
            var4 = 104;
            break;
        case -7:
        case -6:
        case -5:
        case 2:
        case 3:
        case 4:
        case 5:
        case 6:
        case 7:
        case 8:
            var4 = 6;
            break;
        case -4:
            var4 = 24;
            break;
        case -3:
        case -2:
            var4 = 23;
            break;
        case 0:
            var4 = 995;
            break;
        case 1:
            var4 = 96;
            break;
        case 70:
            var4 = 1;
            break;
        case 91:
        case 92:
            var4 = 12;
            break;
        case 100:
            var4 = 100;
            break;
        case 101:
            var4 = 101;
            break;
        case 999:
            var4 = 999;
            break;
        case 2002:
        case 2003:
        case 2007:
        case 2008:
            var4 = 109;
            break;
        case 2004:
            var4 = 113;
            break;
        case 2005:
        case 2011:
            var4 = 112;
            break;
        case 2006:
            var4 = 111;
            break;
        default:
            SQLException var3 = DatabaseError.createSqlException(this.getConnectionDuringExceptionHandling(), 4, Integer.toString(var1));
            var3.fillInStackTrace();
            throw var3;
        }

        return var4;
    }

 看来是var1没有匹配上case,在最后的default里面抛出了异常,那得知道var1是什么,从异常的线程栈信息里面可以看到是OraclePreparedStatement.setNullCritical()这个方法里调用了这个getInternalType,那就去看看这个setNullCritical()方法:

这个方法也是一长串的case,不过我不关心下面是什么,因为在getInternalType()方法调用这个就已经抛异常了,我只想知道刚刚的var1是什么,也就是这个方法里的var2,继续看异常栈信息往上找:

可以看到到BaseTypeHandler.setParameter()方法,里面部分截图是这样的:

 看调用setNull的地方可以知道我们想要的值就是这个jdbcType.TYPE_CODE,继续往上找,可以看到DefaultParameterHandler.setParameters()方法里面调用了这个setParameter()方法,已经到头了,这个方法得截图全一点:

这里的jdbcType 就是我们在XML中写SQL的时候给字段参数指定的,如下:

 这样这两个参数的jdbcType就被指定成了VARCHAR,如果不指定的话就默认为null,关键来了,看上图红圈中的这句:

如果我们不指定jdbcType,默认为null,同时参数值又为null,这不就是文章一开始说的场景吗?看看这个getJdbcTypeForNull()给了一个什么jdbcType,最后导致匹配不上case抛异常,这个方法:

就这?往上找找这个 jdbcTypeForNull,发现这个变量被初始化成了这个:

但同时又有一个set方法:

看来oracle给我机会改这个 jdbcTypeForNull了,可是我没有珍惜,所以最后是因为jdbcType是OTHER类型导致最后的异常,

点了点这个JdbcType.OTHER果然看到了这个“1111”:

 这些从头到尾都理清楚了,是因为这条SQL语句我传的参数值为null,同时又没有给参数设置jdbcType,oracle默认给了一个OTHER的jdbcType,最后导致在getInternalType()方法一长串的case里没有匹配上“1111”,看了一眼还真没有,最后抛出了异常。

解决办法也很多,要么给参数设置jdbcType,要么加<if test="xxx != null">条件,如果是参数值是null的话就不要设置参数了。以前对Mybatie的这两个地方都是知其然不知其所以然,今天扫干净了,不过这是在oracle的jar包里,其他数据库不知道怎么样,有待去看一看。

发布了76 篇原创文章 · 获赞 57 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/weixin_42447959/article/details/105006410