沒有設置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包裏,其他數據庫不知道怎麼樣,有待去看一看。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章