天天看點

java mybatis sql 查詢耗時_Mybatis反序列化sql查詢結果異常

java mybatis sql 查詢耗時_Mybatis反序列化sql查詢結果異常

點選上方藍字關注我們

點選下方在在看在走

java mybatis sql 查詢耗時_Mybatis反序列化sql查詢結果異常

一、前言

今天調試分頁查詢代碼的時候遇到一個奇葩的問題,該問題後來排查下來跟

lombok

的使用有關。我們在使用

mybatis

或者

mybatis-plus

的時候一般會定義一個類對應表的每個字段,一個成熟的java程式員喜歡使用

lombok

把代碼簡潔點。這是大前提,我直接說結論吧:

實體類最好都加上

@Data

,

@AllArgsConstructor

,

@NoArgsConstructor

才能避免我現在遇到的問題

二、我的問題

我的表:

我的實體類:

然後一個普通的

select

查詢的時候報了下面這個錯誤:

可以看到異常很奇怪,我的

total_increase_percent

明明是

BigDecimal

,為何要被反序列化為

java.util.Date

呢?\

後面修改為如下代碼就正常了:

二、問題排查

  1. 首先定位到問題出現在哪裡

    java mybatis sql 查詢耗時_Mybatis反序列化sql查詢結果異常

    在這裡插入圖檔描述

    由于

    columnName

    total_increase_percent

    ,該列不是

    timestamp

    ,類型不一緻,是以報異常。是以要看卡

    rs

    是如何擷取到的以及為何使用了

    org.apache.ibatis.type.DateTypeHandler

    轉換該字段。
  2. org.apache.ibatis.type.DateTypeHandler的擷取

    java mybatis sql 查詢耗時_Mybatis反序列化sql查詢結果異常

    在這裡插入圖檔描述

    點選調用棧的紅框出,跳轉到

    rs

    org.apache.ibatis.type.DateTypeHandler

    的擷取處。
    java mybatis sql 查詢耗時_Mybatis反序列化sql查詢結果異常

    在這裡插入圖檔描述

    createUsingConstructor

    方法這裡既取到了

    typeHandler

    ,又擷取到了

    rs

    我們可以推斷出,應該是通過

    columnName

    parameterType

    擷取

    typeHandler

    出錯了。

    我們看看這裡的代碼:

    看看

    parameterType

    ,

    columnName

    ,

    typeHandler

    三個值的類型:
    java mybatis sql 查詢耗時_Mybatis反序列化sql查詢結果異常

    在這裡插入圖檔描述

    ok,這幾個值都吻合了,根據代碼

    1-3行

    我們看出

    parameterType

    columnName

    沒對上導緻的。這裡可以推斷出

    constructor

    出現了問題,為啥不懷疑是

    columnName

    出現了問題了呢?因為

    rsw

    是擷取到sql傳回結果構造的,是

    mybatis

    的代碼,大機率不會出現問題。
  3. constructor分析

    這是

    constructor

    parameterTypes

    :
    java mybatis sql 查詢耗時_Mybatis反序列化sql查詢結果異常

    在這裡插入圖檔描述

    這裡出現了4個字段,但是沒有

    id

    ,

    gmtUpdate

    ,

    gmtCreate

    這幾個字段,而看看

    rsw

    columnNames

    的值:
    java mybatis sql 查詢耗時_Mybatis反序列化sql查詢結果異常

    在這裡插入圖檔描述

    這裡卻多了

    id

    ,

    gmtUpdate

    ,

    gmtCreate

    這幾列,問題有進一步定位到了,原來是構造器的字段和

    rsw

    columnNames

    不一一對應導緻的。可以從上面代碼

    for

    循環得知,都是根據索引一一擷取,這裡個數都對不上,肯定有問題了。

     在這裡基本上已經定位到問題了,就是構造器隻接受了3個參數導緻的。

  4. 如何擷取到的constructor

    把方法棧在往上移一個,就能找到

    constructor

    的具體擷取處:
    java mybatis sql 查詢耗時_Mybatis反序列化sql查詢結果異常
    在這裡插入圖檔描述

    resultType

    就是我的實體類,

    defaultConstructor

    通過

    findDefaultConstructor

    擷取到,這個方法就不細看了,裡面邏輯就是:如果隻有一個構造器,那就使用該構造器,擷取尋找被标記了

    AutomapConstructor

    注解的構造器。由于

    @Data

    注解隻能生成一個構造器

    OutputValueDO(java.math.BigDecimal,java.math.BigDecimal,java.math.BigDecimal,java.util.Date)

    ,沒有

    id

    ,

    gmtUpdate

    ,

    gmtCreate

    此時就有一個解決方案了,去掉

    @Data

    ,自己寫一個完整的構造器,包括繼承的所有字段,但是這樣是不太好的,從上面

    for

    循環代碼可知,要資料庫的表的列的順序要和實體類的構造器的參數的順序一緻,不然還是出現問題。那就在往上個方法棧看看為什麼使用了該處理邏輯,印象中的

    mybatis

    沒這麼坑。
  5. 更優雅的解決方案

    再往上到上一個方法棧,  

    createResultObject

    方法就找到了正主。
    java mybatis sql 查詢耗時_Mybatis反序列化sql查詢結果異常

    在這裡插入圖檔描述

    由于

    !resultType.isInterface() && !metaType.hasDefaultConstructor()

    判斷為真就走到了通過構造器反射得到結果的邏輯。接下來分别分析這個

    if...else...

  • hasTypeHandlerForResultObject

    明顯我們沒有定義自己的

    typeHandler

    ,故忽略
  • constructorMappings

    表示在

    mapper.xml

    中定義了實體類的字段和表字段的映射關系,但是我們沒有定義,忽略
  • 我的實體類

    OutputValueDO

    不是接口,并且沒有無參構造器,我的代碼正好适合這個判斷,是以我要想辦法是這個判斷為假。
  • 最後一個就是預設的,我要改成走到這個方法來。

    解決方案方向:加個預設構造器。加

    @NoArgsConstructor

    即可解決,該注解就能生成無參構造器,由于我加了

    @Builder

    ,是以我還必須加

    @AllArgsConstructor

    ,穩了,解決了。

三、總結

我一般使用

lombok

最好加上這幾個注解:

但是這個代碼一開始不是我寫的,而且該實體類沒有繼承的話也不會出問題,巧了。以前也看過

mybatis

的代碼,但是沒有翻過映射這塊。今天再一次體味到了

mybatis

的代碼:真正牛的代碼不需要注釋。很容易就找到了問題。

  • 我的微信:
    java mybatis sql 查詢耗時_Mybatis反序列化sql查詢結果異常
    在這裡插入圖檔描述
  • 我的微信公衆号:
    java mybatis sql 查詢耗時_Mybatis反序列化sql查詢結果異常
    雲原生玩碼部落