mybatis参数封装Map过程的源码解析

过程是这样的:

在MyBatisTest中调用interface mapper

Employee employee = mapper.getEmpByIdAndLastName(1, "tom");

在com.hfi.dao.EmployeeMapper#getEmpByIdAndLastName方法中
 

Employee getEmpByIdAndLastName(@Param("id")Integer id, @Param("lastName")String lastName);

其与mybatis映射文件绑定

EmployeeMapper.xml

为什么这里写#{id} #{lastName}能够获取到呢?

如果我没有使用@Param来指定名称,那么要使用{param1} {param2}的原理是什么?

 

先说结论:

输入参数(可能在interface中带@Param注解),输出是一个map

通过源码debug方式看一下

 

MapperProxy implements InvocationHandler

if (Object.class.equals(method.getDeclaringClass()))

 

如果你的method是Object中的method,我们就直接放行

否则就在后面调用

 

final MapperMethod mapperMethod = cachedMapperMethod(method);

return mapperMethod.execute(sqlSession, args);

 

到ParamNameResolver类中

map是如何构造出来的

在其构造函数中将names赋值

private final SortedMap names;

 

构造函数中,通过对method的反射,获取Annotations

public ParamNameResolver(Configuration config, Method method) {

  final Class[] paramTypes = method.getParameterTypes();

  final Annotation[][] paramAnnotations = method.getParameterAnnotations();

  final SortedMap map = new TreeMap();

  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);

}

得到names为

mybatis参数封装Map过程的源码解析_第1张图片

再调用getNamedParams

  public Object getNamedParams(Object[] args) {

    final int paramCount = names.size();

    // 参数为null或没有参数直接返回

    if (args == null || paramCount == 0) {

      return null;

    // 如果只有一个元素,并且没有Param注解;args[0]:单个参数直接返回

    } else if (!hasParamAnnotation && paramCount == 1) {

      return args[names.firstKey()];

    // 多个元素或者有Param标注

    } else {

      final Map param = new ParamMap<>();

      int i = 0;

      // 遍历names集合;{0=id, 1=lastName}

      for (Map.Entry entry : names.entrySet()) {

        // names集合的value作为key;  names集合的key又作为取值的参考

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

        // add generic param names (param1, param2, ...)

        final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);

        // ensure not to overwrite parameter named with @Param

        // 额外的将每一个参数也保存到map中,使用新的key:param1...paramN

        if (!names.containsValue(genericParamName)) {

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

        }

        i++;

      }

      return param;

    }

  }

}

得到

mybatis参数封装Map过程的源码解析_第2张图片

这样,通过${id}或${param1},就都可以取到1这个参数值了

 

其UML如下:

mybatis参数封装Map过程的源码解析_第3张图片

手写实现上述过程,参考:手写实现mybatis参数封装Map过程

流程:

1.获取每个标了param注解的参数的@Param的值:id,lastName;  赋值给name;

2.每次解析一个参数给map中保存信息:(key:参数索引,value:name的值)

name的值:

         标注了param注解:注解的值

         没有标注:

1.全局配置:useActualParamName(jdk1.8支持):name=参数名

2.name=map.size();相当于当前元素的索引

{0=id, 1=lastName}

 

总结:参数多时会封装map,为了不混乱,我们可以使用@Param来指定封装时使用的key;之后#{key}就可以取出map中的值;

 

例如:

public Employee getEmp(@Param("id")Integer id,String lastName);

         取值:id==>#{id/param1}   lastName==>#{param2}

 

public Employee getEmp(Integer id,@Param("e")Employee emp);

         取值:id==>#{param1}    lastName===>#{param2.lastName/e.lastName}

 

public Employee getEmpById(List ids);

由于只有1个值,所以其不封装map。那应该怎么写呢?

特别注意:如果是Collection(List、Set)类型或者是数组,也会特殊处理。也是把传入的list或者数组封装在map中。

key:Collection(其key就是collection),如果是List,封装的更精确,还可以使用这个key(list)。如果是数组,key(array)

取值:取出第一个id的值:   #{list[0]}

类似于list={1,2,3,4,5} map=list({0,1),{1,2}...

你可能感兴趣的:(阅读源码,mybatis,源码解析)