首頁 > 軟體

MyBatis @Param註解的實現

2022-04-01 13:00:03

先說結論:

當輸入引數只有一個且沒有使用@Param註解時,MyBatis會直接傳遞這個引數;當輸入引數多於一個,或者使用了@Param註解時,MyBatis會將引數封裝在Map中傳遞,這時的Map的key分為以下幾種可能:

  • Map中會有param1, param2這樣的key,其順序對應輸入引數的順序。無論是否有@Param註解。
  • 對於@Param註解的引數,Map中會儲存註解中給定的名字作為key
  • 對於沒有用@Param註解的引數,Map中會用1、2、3 ..這樣的數位作為key,按順序儲存輸入引數。

下面來看一下原始碼。

首先,判斷一個方法中是否有用@Param註解的引數:

    private boolean hasNamedParams(Method method) {
      final Object[][] paramAnnos = method.getParameterAnnotations();
      for (Object[] paramAnno : paramAnnos) {
        for (Object aParamAnno : paramAnno) {
          if (aParamAnno instanceof Param) {
            return true;
          }
        }
      }
      return false;
    }

如果有用@Param註解的引數,取出註解中給出的引數名:

    private String getParamNameFromAnnotation(Method method, int i, String paramName) {
      final Object[] paramAnnos = method.getParameterAnnotations()[i];    // 獲取第i個引數的註解
      for (Object paramAnno : paramAnnos) {
        if (paramAnno instanceof Param) {
          paramName = ((Param) paramAnno).value();
          break;
        }
      }
      return paramName;
    }

注意方法的輸入引數,method表示是哪個方法上,i 表示第幾個引數,  paramName是傳進來的引數名,如果該引數沒有用@Param註解,則返回傳進來的paramName。

下面這個方法返回一個TreeMap(有序),其key表示引數的順序,比如key=0代表第0個引數;value表示引數的名字,如果有用@Param註解標註,則為標註的引數名,否則和key相等,即用引數的序號作為引數的名字。

    private SortedMap<Integer, String> getParams(Method method, boolean hasNamedParameters) {
      final SortedMap<Integer, String> params = new TreeMap<Integer, String>();
      final Class<?>[] argTypes = method.getParameterTypes();
      for (int i = 0; i < argTypes.length; i++) {
        if (!RowBounds.class.isAssignableFrom(argTypes[i]) && !ResultHandler.class.isAssignableFrom(argTypes[i])) {
          String paramName = String.valueOf(params.size());    // 引數名,預設為引數的序號
          if (hasNamedParameters) {    //如果有使用@Param註解,則獲取註解標註的引數名
            paramName = getParamNameFromAnnotation(method, i, paramName);    // 這裡paramName作為引數傳進來,表示預設值
          }
          params.put(i, paramName);
        }
      }
      return params;
    }

其中hasNamedParameters只是從整個方法的維度,給出該方法是否有使用@Param註解的引數;即使其值為true,具體到某一個引數上面,可能沒有使用@Param註解,因此呼叫getParamNameFromAnnotation傳入的paramName就作為預設值返回,即引數的序號。

最後將呼叫方法的引數轉換為MyBatis內部使用的引數:

 public Object convertArgsToSqlCommandParam(Object[] args) {
      final int paramCount = params.size();
      if (args == null || paramCount == 0) {
        return null;
      } else if (!hasNamedParameters && paramCount == 1) {
        return args[params.keySet().iterator().next().intValue()];
      } else {
        final Map<String, Object> param = new ParamMap<Object>();
        int i = 0;
        for (Map.Entry<Integer, String> entry : params.entrySet()) {
          param.put(entry.getValue(), args[entry.getKey().intValue()]);
          // issue #71, add param names as param1, param2...but ensure backward compatibility
          final String genericParamName = "param" + String.valueOf(i + 1);
          if (!param.containsKey(genericParamName)) {
            param.put(genericParamName, args[entry.getKey()]);
          }
          i++;
        }
        return param;
      }
    }

其中args為Dao方法的輸入引數,這裡已經轉換成了陣列,其實就是動態代理的invoke方法傳入的引數。

該方法首先對輸入引數進行計數,使用的params就是前面介紹的getParams方法的返回值。

if (!hasNamedParameters && paramCount == 1)

上面的條件判斷,即方法沒有使用@Param註解,且只有一個引數,這時返回

args[params.keySet().iterator().next().intValue()]

即直接將其作為Object返回。

如果上面的條件不滿足的話,首先新建一個Map作為返回值:

final Map<String, Object> param = new ParamMap<Object>();

然後,設定map的key和value:

param.put(entry.getValue(), args[entry.getKey().intValue()]);

然後為了相容性,做了如下操作

final String genericParamName = "param" + String.valueOf(i + 1);
if (!param.containsKey(genericParamName)) {
    param.put(genericParamName, args[entry.getKey()]);
}

即設定param1、param2這樣的key。

到此為止,需要的引數物件Object就構建完成,其中封裝了Dao傳入的多個引數,並根據引數是否有@Param註解,影響了引數物件的型別(是否是map)。

引數封裝完成之後,下一步將其傳遞給SqlSession。

到此這篇關於MyBatis @Param註解的實現的文章就介紹到這了,更多相關MyBatis @Param註解內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com