最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

問(wèn)題描述

最近使用IDEA進(jìn)行開(kāi)發(fā),項(xiàng)目使用SpringBoot+Mybatis3.4.6,同樣的代碼檢出到本地IDEA后運(yùn)行,在一個(gè)業(yè)務(wù)查詢(xún)模塊報(bào)錯(cuò),后臺(tái)打印日志如下:

在這里插入圖片描述

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

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

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

到這里事情看上去好像已經(jīng)解決了,但是并沒(méi)有這么簡(jiǎn)單,我查看了很多mapper發(fā)現(xiàn),有很多具有多個(gè)參數(shù)的mapper方法都沒(méi)有使用這個(gè)注解,按照這種修改方式,我豈不是要把幾乎所有的mapper都修改一遍,并且我是剛剛檢出的最新代碼,代碼不應(yīng)該有問(wèn)題才對(duì),于是詢(xún)問(wèn)同事發(fā)現(xiàn)他們?cè)谧约旱腎DEA運(yùn)行時(shí)并沒(méi)有我這個(gè)錯(cuò)誤,所以說(shuō)并不是@Param注解的問(wèn)題。

尋求解決方案

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

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

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

尋找原因

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

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

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

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

      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則通過(guò)getActualParamName(method, paramIndex)方法獲取name,這些都執(zhí)行完成如果name還是null,那么就是最后的邏輯: name = String.valueOf(map.size());也就是說(shuō)name等于當(dāng)前方法參數(shù)的位置(“0”, “1”, …),源碼的注釋也說(shuō)明了這一點(diǎn):

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

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

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

public boolean isUseActualParamName() {
    return useActualParamName;
  }

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

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

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

①采用 Java 8 編譯。

②編譯時(shí)加上-parameters 選項(xiàng)。

③mybatis在3.4.1以上

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

①老版本(2018.2.2):

在這里插入圖片描述

②新版本(2019.2.3):

在這里插入圖片描述

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

拓展延伸

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

那么為什么不默認(rèn)開(kāi)啟這個(gè)選項(xiàng)呢?可能是為了避免因?yàn)楸A魠?shù)名而導(dǎo)致class文件過(guò)大或者占用更多的內(nèi)存,又或者是有些參數(shù)可能會(huì)泄露安全信息吧。

最后我們親自來(lái)寫(xiě)一段代碼驗(yàn)證一下-parameters這個(gè)選項(xiàng)的作用:

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!");
    }
}

在以上這段代碼中,通過(guò)反射獲取parameterMethodTest的三個(gè)參數(shù)名稱(chēng)并打印出來(lái),首先我們?cè)贗DEA的編譯設(shè)置中去掉-parameters選項(xiàng),運(yùn)行結(jié)果如下:

在這里插入圖片描述

可以看到這個(gè)時(shí)候參數(shù)名稱(chēng)變成了arg0,arg1…

加上-parameters選項(xiàng)后,再運(yùn)行結(jié)果如下:

在這里插入圖片描述

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

相關(guān)文章

  • Spring中@ControllerAdvice注解的用法解析

    Spring中@ControllerAdvice注解的用法解析

    這篇文章主要介紹了Spring中@ControllerAdvice注解的用法解析,顧名思義,@ControllerAdvice就是@Controller 的增強(qiáng)版,@ControllerAdvice主要用來(lái)處理全局?jǐn)?shù)據(jù),一般搭配@ExceptionHandler、@ModelAttribute以及@InitBinder使用,需要的朋友可以參考下
    2023-10-10
  • Mybatis之foreach標(biāo)簽內(nèi)傳入list為空的問(wèn)題

    Mybatis之foreach標(biāo)簽內(nèi)傳入list為空的問(wèn)題

    這篇文章主要介紹了Mybatis之foreach標(biāo)簽內(nèi)傳入list為空的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • springSecurity自定義登錄接口和JWT認(rèn)證過(guò)濾器的流程

    springSecurity自定義登錄接口和JWT認(rèn)證過(guò)濾器的流程

    這篇文章主要介紹了springSecurity自定義登陸接口和JWT認(rèn)證過(guò)濾器的相關(guān)資料,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2024-12-12
  • java web監(jiān)聽(tīng)器統(tǒng)計(jì)在線用戶(hù)及人數(shù)

    java web監(jiān)聽(tīng)器統(tǒng)計(jì)在線用戶(hù)及人數(shù)

    本文主要介紹了java web監(jiān)聽(tīng)器統(tǒng)計(jì)在線用戶(hù)及人數(shù)的方法解析。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧
    2017-04-04
  • 收集的一些常用java正則表達(dá)式

    收集的一些常用java正則表達(dá)式

    收集的一些常用java正則表達(dá)式,需要的朋友可以參考一下
    2013-02-02
  • Java面向?qū)ο笤O(shè)計(jì)原則之迪米特法則介紹

    Java面向?qū)ο笤O(shè)計(jì)原則之迪米特法則介紹

    迪米特法則解決類(lèi)與類(lèi)之間耦合度問(wèn)題,如果類(lèi)A調(diào)用了B類(lèi)的某一個(gè)方法,則這兩個(gè)類(lèi)就形成了一種緊耦合的方式,當(dāng)B類(lèi)這個(gè)方法發(fā)生變化時(shí),一定會(huì)影響A類(lèi)的執(zhí)行結(jié)果。迪米特法則要求每一個(gè)類(lèi)盡可能少的與其他類(lèi)發(fā)生關(guān)系
    2023-02-02
  • 使用java + selenium + OpenCV破解網(wǎng)易易盾滑動(dòng)驗(yàn)證碼的示例

    使用java + selenium + OpenCV破解網(wǎng)易易盾滑動(dòng)驗(yàn)證碼的示例

    這篇文章主要介紹了使用java + selenium + OpenCV破解網(wǎng)易易盾滑動(dòng)驗(yàn)證碼,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-02-02
  • RocketMQ 延時(shí)級(jí)別配置方式

    RocketMQ 延時(shí)級(jí)別配置方式

    這篇文章主要介紹了RocketMQ 延時(shí)級(jí)別配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • 詳解SpringCloud Gateway 2020.0.2最新版

    詳解SpringCloud Gateway 2020.0.2最新版

    這篇文章主要介紹了SpringCloud Gateway 2020.0.2最新版,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • Spring Boot 利用WebUploader進(jìn)行文件上傳功能

    Spring Boot 利用WebUploader進(jìn)行文件上傳功能

    本文的重點(diǎn)是給大家介紹在Spring Boot項(xiàng)目中利用WebUploader如何進(jìn)行文件上傳,本文通過(guò)示例代碼給大家介紹,需要的朋友參考下吧
    2018-03-03

最新評(píng)論

金华市| 榕江县| 旌德县| 乌恰县| 阳西县| 华坪县| 上犹县| 昌邑市| 富源县| 贵定县| 楚雄市| 屯昌县| 米林县| 罗江县| 安仁县| 万州区| 南澳县| 上饶市| 灯塔市| 宁夏| 方正县| 崇礼县| 灌南县| 徐汇区| 栾城县| 固原市| 邵东县| 密山市| 大埔区| 汝南县| 嘉祥县| 满洲里市| 靖西县| 安顺市| 当阳市| 任丘市| 綦江县| 清河县| 蓝山县| 广河县| 克什克腾旗|