亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Mybatis?Mapper中多參數(shù)方法不使用@param注解報錯的解決

 更新時間:2022年01月11日 14:48:35   作者:致虛極POLE守靜篤  
這篇文章主要介紹了Mybatis?Mapper中多參數(shù)方法不使用@param注解報錯的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教。

在使用低版本的Mybatis的時候,Mapper中的方法如果有多個參數(shù)時需要使用@param注解,才能在對應(yīng)xml的sql語句中使用參數(shù)名稱獲取傳入方法的參數(shù)值,否則就會報錯。本文結(jié)合自身在真實開發(fā)環(huán)境中使用IDEA開發(fā)時遇到的問題來共同探討一下不使用@Param注解報錯背后的原因以及解決方案。

問題描述

最近使用IDEA進行開發(fā),項目使用SpringBoot+Mybatis3.4.6,同樣的代碼檢出到本地IDEA后運行,在一個業(yè)務(wù)查詢模塊報錯,后臺打印日志如下:

在這里插入圖片描述

mybatis出現(xiàn)該錯誤的原因分析:我們正在調(diào)用一個具有多參數(shù)的mapper接口方法,對這個方法的調(diào)用其實是對mapper對應(yīng)的xml中的一個sql的調(diào)用,并且我們在這個sql語句中使用#{方法參數(shù)名稱}的方式構(gòu)建動態(tài)SQL,但是要想在sql語句中使用參數(shù)名稱獲取參數(shù)值那么需要對mapper接口對應(yīng)方法的每一個參數(shù)使用@Param注解,Param注解非常簡單,源代碼如下:

/**
 * @author Clinton Begin
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Param {
  String value();
}

它只有一個value屬性,這里的value就等于mapper對應(yīng)的xml文件中獲取參數(shù)值時要使用的key。于是我找到了對應(yīng)報錯的代碼發(fā)現(xiàn)正是因為多參數(shù)方法沒有使用@Param注解,在我加上該注解后便沒有錯誤了。        

到這里事情看上去好像已經(jīng)解決了,但是并沒有這么簡單,我查看了很多mapper發(fā)現(xiàn),有很多具有多個參數(shù)的mapper方法都沒有使用這個注解,按照這種修改方式,我豈不是要把幾乎所有的mapper都修改一遍,并且我是剛剛檢出的最新代碼,代碼不應(yīng)該有問題才對,于是詢問同事發(fā)現(xiàn)他們在自己的IDEA運行時并沒有我這個錯誤,所以說并不是@Param注解的問題。

尋求解決方案

同樣的代碼,在不同的機器上運行出現(xiàn)了不同的結(jié)果,那么肯定有什么不一樣的地方,首先JDK都一樣,系統(tǒng)環(huán)境也一樣,運行方式也一樣,下來就是運行環(huán)境IDEA,那么IDEA是否有區(qū)別呢?

詢問同事發(fā)現(xiàn)他們用的是比較新的版本2019.2.3,而我用的是2018.2.2版本,所以初步懷疑是IDEA的版本問題,但是好像按理來說不應(yīng)該是IDEA的問題,真正運行JAVA字節(jié)碼的是本地的JRE環(huán)境,貌似和IDEA關(guān)系不大,但是這是目前唯一的線索,無論如何都要試一下。

于是我下載了最新版本的IDEA,然后導(dǎo)入代碼,運行,結(jié)果發(fā)現(xiàn)竟然真的沒有報錯!這時候問題雖然解決了,但是為什么會這樣,背后的原因是什么,和IDEA版本有什么關(guān)系呢?這些問題如鯁在喉,讓我茶不思,飯不想…

尋找原因

當(dāng)一個問題無法知道背后的真正原因時,那么就算解決了也只是暫時的。為了尋求真正的答案,我決定使用調(diào)試代碼的方式看一下mybatis執(zhí)行查詢過程中是如何處理mapper接口方法的參數(shù)名稱的,最終找到了org.apache.ibatis.reflection.ParamNameResolver這個類,看類名就可以知道這是處理參數(shù)名稱的類,主要邏輯集中在它的構(gòu)造方法:

  public ParamNameResolver(Configuration config, Method method) {
    final Class<?>[] paramTypes = method.getParameterTypes();
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
    final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
    int paramCount = paramAnnotations.length;
    // get names from @Param annotations
    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
      if (isSpecialParameter(paramTypes[paramIndex])) {
        // skip special parameters
        continue;
      }
      String name = null;
      for (Annotation annotation : paramAnnotations[paramIndex]) {
        if (annotation instanceof Param) {
          hasParamAnnotation = true;
          name = ((Param) annotation).value();
          break;
        }
      }
      if (name == null) {
        // @Param was not specified.
        if (config.isUseActualParamName()) {
          name = getActualParamName(method, paramIndex);
        }
        if (name == null) {
          // use the parameter index as the name ("0", "1", ...)
          // gcode issue #71
          name = String.valueOf(map.size());
        }
      }
      map.put(paramIndex, name);
    }
    names = Collections.unmodifiableSortedMap(map);
  }

接下來分析一下主要邏輯,首先看到的是需要獲取Param注解中的Value值:

String name = null;
      for (Annotation annotation : paramAnnotations[paramIndex]) {
        if (annotation instanceof Param) {
          hasParamAnnotation = true;
          name = ((Param) annotation).value();
          break;
        }
      }

這里的name變量就是后面構(gòu)造動態(tài)sql時,用于獲取方法參數(shù)值的key,也就是你在xml文件中通過#{ }的方式獲取動態(tài)參數(shù)時的參數(shù)key。接下來看到的代碼是:

      if (name == null) {
        // @Param was not specified.
        if (config.isUseActualParamName()) {
          name = getActualParamName(method, paramIndex);
        }
        if (name == null) {
          // use the parameter index as the name ("0", "1", ...)
          // gcode issue #71
          name = String.valueOf(map.size());
        }
      }

這里可以看到再次判斷name是否為null,如果為null則判斷config.isUseActualParamName()是否為true,如果是true則通過getActualParamName(method, paramIndex)方法獲取name,這些都執(zhí)行完成如果name還是null,那么就是最后的邏輯: name = String.valueOf(map.size());也就是說name等于當(dāng)前方法參數(shù)的位置(“0”, “1”, …),源碼的注釋也說明了這一點:

use the parameter index as the name (“0”, “1”, …)

那么getActualParamName(method, paramIndex)方法獲取name是什么邏輯呢?接下來繼續(xù)看:

首先要進入這個方法的前提是config.isUseActualParamName()為true:

public boolean isUseActualParamName() {
    return useActualParamName;
  }

config其實是mybatis的配置對象,這里面的配置項目可以影響mybatis的行為,具體配置項目可以從mybatis官方文檔查詢,這里我們就看一下useActualParamName參數(shù)的含義,官方文檔 是這樣描述的:

設(shè)置名描述有效值默認(rèn)值
useActualParamName允許使用方法簽名中的名稱作為語句參數(shù)名稱。 為了使用該特性,你的項目必須采用 Java 8 編譯,并且加上 -parameters 選項。(新增于 3.4.1)true 或者 falsetrue

所以說這個屬性其實就是允許我們使用mapper接口方法的參數(shù)名稱當(dāng)作sql語句的參數(shù)名稱,而且也不需要@Param注解,這個屬性默認(rèn)是開啟的,使用這個特性還有以下幾個要求:

①采用 Java 8 編譯。

②編譯時加上-parameters 選項。

③mybatis在3.4.1以上

到這里基本上可以確定真正的原因了,首先我和同事的JDK都是1.8,Mybatis的版本在文章開頭也說過了是3.4.6,所以只剩下-parameters選項,所以我懷疑是低版本的IDEA沒有這個選項,高版本的IDEA在編譯時可能默認(rèn)加了這個選項。于是對比兩個版本的編譯設(shè)置如下:

①老版本(2018.2.2):

在這里插入圖片描述

②新版本(2019.2.3):

在這里插入圖片描述

果然如我們所料,新版本的IDEA編譯設(shè)置里面默認(rèn)添加了-parameters選項,所以在mybatis的配置項useActualParamName為true的時候,對于多參數(shù)的mapper接口方法,可以不使用@Param注解,而在低版本的IDEA時并沒有添加這個選項,所以會出錯。

拓展延伸

在Java8之前,JAVA代碼編譯為class文件后,方法參數(shù)的類型固定,但是參數(shù)名稱會丟失,所以當(dāng)通過反射去獲取方法參數(shù)名稱的時候是不能夠得到原本源代碼中的參數(shù)名稱的,Java編譯器會丟掉這部分信息。從JDK1.8開始可以通過在編譯時添加-parameters這個選項來明確告訴編譯器我們需要保留方法參數(shù)的原本名稱。

那么為什么不默認(rèn)開啟這個選項呢?可能是為了避免因為保留參數(shù)名而導(dǎo)致class文件過大或者占用更多的內(nèi)存,又或者是有些參數(shù)可能會泄露安全信息吧。

最后我們親自來寫一段代碼驗證一下-parameters這個選項的作用:

public class Main {
    public static void main(String[] args) {
        Method[] methods = Main.class.getMethods();
        for (Method method:methods) {
            if ("parameterMethodTest".equals(method.getName())){
                Parameter[] parameters = method.getParameters();
                for (Parameter parameter:parameters) {
                    System.out.println(parameter.getName());
                }
            }
        }
    }
    public static void parameterMethodTest(int parameterOne,String parameterTwo,Object parameterThree){
        System.out.println("Hello World!");
    }
}

在以上這段代碼中,通過反射獲取parameterMethodTest的三個參數(shù)名稱并打印出來,首先我們在IDEA的編譯設(shè)置中去掉-parameters選項,運行結(jié)果如下:

在這里插入圖片描述

可以看到這個時候參數(shù)名稱變成了arg0,arg1…

加上-parameters選項后,再運行結(jié)果如下:

在這里插入圖片描述

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java 判斷字符串中是否包含中文的實例詳解

    Java 判斷字符串中是否包含中文的實例詳解

    這篇文章主要介紹了Java 判斷字符串中是否包含中文的實例詳解的相關(guān)資料,這里提供實例來說明該如何實現(xiàn)這樣的功能,需要的朋友可以參考下
    2017-08-08
  • java代碼抓取網(wǎng)頁郵箱的實現(xiàn)方法

    java代碼抓取網(wǎng)頁郵箱的實現(xiàn)方法

    下面小編就為大家?guī)硪黄猨ava代碼抓取網(wǎng)頁郵箱的實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-06-06
  • springboot項目如何引用公共模塊的bean

    springboot項目如何引用公共模塊的bean

    這篇文章主要介紹了springboot項目如何引用公共模塊的bean問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • 多個版本JAVA切換的簡單步驟記錄

    多個版本JAVA切換的簡單步驟記錄

    在工作中或者學(xué)習(xí)過程中,有一些特殊情況我們需要來切換java版本來做比較,比如一些新特性等等的相關(guān)資料,這篇文章主要介紹了多個版本JAVA切換的相關(guān)資料,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2024-09-09
  • Java注釋代碼執(zhí)行方法解析

    Java注釋代碼執(zhí)行方法解析

    這篇文章主要介紹了Java注釋代碼執(zhí)行方法解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-05-05
  • Java?Lambda表達(dá)式常用的函數(shù)式接口

    Java?Lambda表達(dá)式常用的函數(shù)式接口

    這篇文章主要介紹了Java?Lambda表達(dá)式常用的函數(shù)式接口,文章基于Java?Lambda表達(dá)式展開對常用的函數(shù)式接口的介紹,具有一的的參考價值需要的小伙伴可以參考一下
    2022-04-04
  • 使用Java7的Files工具類和Path接口來訪問文件的方法

    使用Java7的Files工具類和Path接口來訪問文件的方法

    下面小編就為大家分享一篇使用Java7的Files工具類和Path接口來訪問文件的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2017-11-11
  • MyBatis-Plus工具使用之EntityWrapper解析

    MyBatis-Plus工具使用之EntityWrapper解析

    這篇文章主要介紹了MyBatis-Plus工具使用之EntityWrapper解析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • springboot實現(xiàn)防重復(fù)提交和防重復(fù)點擊的示例

    springboot實現(xiàn)防重復(fù)提交和防重復(fù)點擊的示例

    這篇文章主要介紹了springboot實現(xiàn)防重復(fù)提交和防重復(fù)點擊的示例,幫助大家更好的理解和學(xué)習(xí)springboot框架,感興趣的朋友可以了解下
    2020-09-09
  • Java從內(nèi)存角度帶你理解數(shù)組名實質(zhì)是個地址的論述

    Java從內(nèi)存角度帶你理解數(shù)組名實質(zhì)是個地址的論述

    這篇文章主要介紹了Java如何從內(nèi)存解析的角度理解“數(shù)組名實質(zhì)是一個地址”,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-09-09

最新評論