《从面试题来看源码》,单参数,多参数,如何正确使用@Param

Mybatis Dao接口中,单参数,多参数,如何正确使用@Param?

答:单参数、多参数下,都可以用注解或不用注解。单参数,一般不用注解,用了注解sql语句参数名必须跟注解名称一致。多参数下,建议使用注解,方便后期调式,如果不用注解必须使用 0,1... 索引 或者 param1,param2...

源码分析
如何初始化,请看该篇文章《从面试题来看源码》,Dao接口的工作原理

首先还是来看MapperProxy代理类调用的时候执行的invoke方法 MapperProxy.java

@Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
      //如果目标方法继承自Object,则直接调用目标方法
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {
        //对jdk7以上版本,动态语言的支持
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    //1️⃣从缓存中获取 MapperMethod对象,如果缓存中没有,则创建新的 MapperMethod对象并添加到缓存中
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    //2️⃣调用 MapperMethod.execute ()方法执行 SQL 语 句
    return mapperMethod.execute(sqlSession, args);
  }

首先来看1️⃣,在MapperMethod 中封装了 Mapper接口中对应方法的信息,以及对应 SQL 语句的信息 MapperMethod.java

 public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    //记录了 SQL语句的名称和类型
    this.command = new SqlCommand(config, mapperInterface, method);
    //Mapper 接 口中对应方法的相关信息
    this.method = new MethodSignature(config, mapperInterface, method);
  }

MethodSignature中使用ParamNameResolver处理Mapper接口中定义方法的参数列表 ParamNameResolver.java

 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

    //PS: 循环处理所有参数,第一层for循环参数列表,第二层for循环参数的注解集合
    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
      如果参数是 RowBounds 类型或 ResultHandler 类型,贝1]跳过对该参数的分析
      if (isSpecialParameter(paramTypes[paramIndex])) {
        // skip special parameters
        continue;
      }
      String name = null;
      //遍历该参数的注解集合
      for (Annotation annotation : paramAnnotations[paramIndex]) {
        //有@param注解
        if (annotation instanceof Param) {
          hasParamAnnotation = true;
          //获取@Param注解指定的参数名称
          name = ((Param) annotation).value();
          break;
        }
      }
      //如果没有注解
      if (name == null) {
        // @Param was not specified.
        // useActualParamName==true时  即name = arg0 ...
        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的结构是这样
如果是使用注解,结构是这样

上面方法的参数列表已经处理完了,下面就要处理***参数列表跟传入数值***的对应关系了,该过程在开头2️⃣中进行处理
MapperMethod.java

  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    //根据 SQL 语句的类型调用 SqlSession 对应的方法
    switch (command.getType()) {
      case INSERT: {
      //负责将 args []数纽( 用户传入的实 参列表)转换成 SQL 语句对应的参数列表
      Object param = method.convertArgsToSqlCommandParam(args);
        //...
        break;
      }
      case UPDATE: {
        //...
      }
      case DELETE: {
        //...
      }
      case SELECT:
        //...
      case FLUSH:
        //...
      default:
        throw new BindingException("Unknown execution method for: " + command.getName());
    }
    //...
  }

convertArgsToSqlCommandParam()方法通过ParamNameResolver对象的getNamedParams实现 ParamNameResolver.java


    final int paramCount = names.size();
    if (args == null || paramCount == 0) {
      return null;
      //未使用@Param且只有一个参数
    } else if (!hasParamAnnotation && paramCount == 1) {
      return args[names.firstKey()];
      //处理使用@Param注解指定了参数名称或有多个参数的情况
    } else {

      final Map<String, Object> param = new ParamMap<Object>();
      int i = 0;
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        //将参数名与实参对应关系记录到 param 中
        param.put(entry.getValue(), args[entry.getKey()]);
        // add generic param names (param1, param2, ...)

        // 下面是为参数创建”param+索号”格式的默认参数名称,例如: param1, param2 等,并添加到param集合中
        //PS:所以如果你不用注解的话,SQL中就得用param1,param2...
        final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
        // ensure not to overwrite parameter named with @Param
        //如果@Param注解指定的参数名称就是”param+索引”格式的,则不需要再添加
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }

最后你会发现param中是这个样子
所以说: 多参数下,如果不用注解必须使用 0,1... 索引 或者 param1,param2...

转载于:https://my.oschina.net/itsaysay/blog/3020434

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值