Analytical analysis of Mybatis parameters

This example is very simple, daily call interface, pass the parameter, is Mybatis how to parse, why in XML, idea will have a hint, param1, param2. Sometimes there are default parameters, so let’s analyze them now

I’m not going to give you an example. It’s too simple. Straight flush

Assembly parameters

Those of you who have read the previous article will be familiar with the following code.

When talking about a tip, keep an eye on arGS while analyzing assembly parameters. Since the args is passed in when the reflection is called, focus on the ARgs and you are sure to find the corresponding code

 // This is a query that returns Map. Everything else is pretty much the same.
  private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
    Map<K, V> result;
    // Do the transformation here. This method is called 'MethodSignature', so just look at what's in there
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      result = sqlSession.selectMap(command.getName(), param, method.getMapKey(), rowBounds);
    } else {
      result = sqlSession.selectMap(command.getName(), param, method.getMapKey());
    }
    return result;
  }
Copy the code

MethodSignature.convertArgsToSqlCommandParam(args)

  public Object convertArgsToSqlCommandParam(Object[] args) {
      return paramNameResolver.getNamedParams(args);
    }
Copy the code

Oops, the code above is plain vanilla. It calls the ParamNameResolver method

ParamNameResolver.getNamedParams

The code here is not hard, just look at it, pay attention to the comments

Don’t look at this. Look at this after this

public ParamNameResolver(Configuration config, Method method) {
  // This value defaults to true,
    this.useActualParamName = config.isUseActualParamName();
  // Get an array of parameter types
    finalClass<? >[] paramTypes = method.getParameterTypes();// Get an annotation of the parameters
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
  
  //treeMap is used for sorting
    final SortedMap<Integer, String> map = new TreeMap<>();
  / / the number of
    int paramCount = paramAnnotations.length;
     // Parse @param annotations. It's time to traverse
    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
      // Determine whether the method argument has RowBounds and ResultHandler, skip these
      if (isSpecialParameter(paramTypes[paramIndex])) {
        // skip special parameters
        continue;
      }
      String name = null;
      // Iterate over the list of comments, if there is one for Param. Assign attributes and get values to name. , and notice that the interesting thing here is that paramAnnotations are a two-dimensional array.
      for (Annotation annotation : paramAnnotations[paramIndex]) {
        if (annotation instanceof Param) {
          hasParamAnnotation = true;
          name = ((Param) annotation).value();
          break; }}// No name found
      if (name == null) {
        // This parameter means whether the actual parameter name is required
        if (useActualParamName) {
          // This is not the name of the field, this is the value of the annotation above, this is the JDK method, and then I'll see what's going on here
          name = getActualParamName(method, paramIndex);
        }
        // If not,
        if (name == null) {
         // Just give string.valueof (map.size());name = String.valueOf(map.size()); }}// So map (Names) is sorted by treemap, and name is @param, arg0, arg1......
      map.put(paramIndex, name);
    }
    names = Collections.unmodifiableSortedMap(map);
  }



 private String getActualParamName(Method method, int paramIndex) {
    return ParamNameUtil.getParamNames(method).get(paramIndex);
  }
Copy the code

Now that you know where the values in names come from, move on.

Looking at the first line of code, you see that there is a name that was created when MethodSignature was created. So let’s look at how he built it and what he did. See the code above for MethodSignature.

public Object getNamedParams(Object[] args) {
  // Where is this name used? We'll do it the old-fashioned way, just click on it, see where you can quote him. Finally found out that ParamNameResolver is created when MethodSignature is created and Param annotations are parsed. Look at the code analysis above
    final int paramCount = names.size();
  
  // If neither of them exists, the query has no parameters. There are no arguments on the method. In fact, I feel that arGS check is not meaningful, if the method has parameters, but there is still no method call, that idea compilation can not pass.
    if (args == null || paramCount == 0) {
      return null;
      
      // If there is no param annotation and the number of arguments is one, the default value will be increased for collection or array types.
    } else if(! hasParamAnnotation && paramCount ==1) { 
      Object value = args[names.firstKey()]; 
      return wrapToMapIfCollection(value, useActualParamName ? names.get(0) : null);
    } else {
      
      // Otherwise, Names are traversed. Note that Names is Treemap and its traversal is middle order. If the key was a number, it would be 1,2,3,4,5
      // 
      final Map<String, Object> param = new ParamMap<>();
      int i = 0;
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        // Put the previous value in, value is the annotation value, or arg1, arg2, value is the actual value
        param.put(entry.getValue(), args[entry.getKey()]);
        
        // A key beginning with param will also be added
        // public static final String GENERIC_NAME_PREFIX = "param";

        final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
        // ensure not to overwrite parameter named with @Param
        if(! names.containsValue(genericParamName)) { param.put(genericParamName, args[entry.getKey()]); } i++; }returnparam; }}//
 // Name is a collection, list is a list, and array is an array.
  public static Object wrapToMapIfCollection(Object object, String actualParamName) {
    if (object instanceof Collection) {
      ParamMap<Object> map = new ParamMap<>();
      map.put("collection", object);
      if (object instanceof List) {
        map.put("list", object);
      }
      Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
      return map;
    } else if(object ! =null && object.getClass().isArray()) {
      ParamMap<Object> map = new ParamMap<>();
      map.put("array", object);
      Optional.ofNullable(actualParamName).ifPresent(name -> map.put(name, object));
      return map;
    }
    return object;
  }

Copy the code

In summary, there are two stages: one node parses Param and the other stage assembles the Map from Param

  1. When ParamNameResolver is created, @param is parsed on the method, and if not, the getActualParamName method is called (with the value arg1, arg2) to get the name. And the method parameter with the subscript key and the name value is placed in treeMap.

  2. Assemble arguments. If the current method is not annotated by @param and the argument has a value of one, name has a collection if it is collection, list is list if it is array, and array is array if it is array. If not. The key is the value in the treeMap, the value is the actual value, and the parameter starting with param will be placed, and the map will be assembled by retrieving the value by subscript during treeMap traversal. return

Analysis on Mybatis parameter analysis is here. Please point out any inaccuracies. thank you