Mybatis 传递参数中的_paramter 的理解

原因:
以前在传递参数的时候,出现传递单个参数,有的时候用#{id} 可以成功,有的时候报错,只能改成#{_parameter}

---------------------------------------------------------------------------------------------------------------------------

分析:

参数的使用分为两部分:

  • 第一种就是常见#{username}或者${username}
  • 第二种就是在动态SQL中作为条件,例如<if test="username!=null and username !=''">

动态SQL为什么会处理参数呢?

主要是因为动态SQL中的<if>,<bind>,<foreache>都会用到表达式,表达式中会用到属性名,属性名对应的属性值如何获取呢?获取方式就在这关键的一步。不知道多少人遇到Could not get property xxx from xxxClass: Parameter ‘xxx’ not found. Available parameters are[…],都是不懂这里引起的。

DynamicContext.java中,从构造方法看起:

public DynamicContext(Configuration configuration, Object parameterObject) {
  if (parameterObject != null && !(parameterObject instanceof Map)) {
    MetaObject metaObject = configuration.newMetaObject(parameterObject);
    bindings = new ContextMap(metaObject);
  } else {
    bindings = new ContextMap(null);
  }
  bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
  bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());
}

这里的Object parameterObject就是我们经过前面两步处理后的参数。这个参数经过前面两步处理后,到这里的时候,他只有下面三种情况:

  1. null,如果没有入参或者入参是null,到这里也是null
  2. Map类型,除了null之外,前面两步主要是封装成Map类型。
  3. 数组、集合和Map以外的Object类型,可以是基本类型或者实体类。

看上面构造方法,如果参数是1,2情况时,执行代码bindings = new ContextMap(null);参数是3情况时执行if中的代码。我们看看ContextMap类,这是一个内部静态类,代码如下:

static class ContextMap extends HashMap<String, Object> {
  private MetaObject parameterMetaObject;
  public ContextMap(MetaObject parameterMetaObject) {
    this.parameterMetaObject = parameterMetaObject;
  }
  public Object get(Object key) {
    String strKey = (String) key;
    if (super.containsKey(strKey)) {
      return super.get(strKey);
    }
    if (parameterMetaObject != null) {
      // issue #61 do not modify the context when reading
      return parameterMetaObject.getValue(strKey);
    }
    return null;
  }
}

 

我们先继续看DynamicContext的构造方法,在if/else之后还有两行:

bindings.put(PARAMETER_OBJECT_KEY, parameterObject);
bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId());

 

其中两个Key分别为:

public static final String PARAMETER_OBJECT_KEY = "_parameter";
public static final String DATABASE_ID_KEY = "_databaseId";

 

也就是说1,2两种情况的时候,参数值只存在于"_parameter"的键值中。3情况的时候,参数值存在于"_parameter"的键值中,也存在于bindings本身。

当动态SQL取值的时候会通过OGNL从bindings中获取值。MyBatis在OGNL中注册了ContextMap:

static {
  OgnlRuntime.setPropertyAccessor(ContextMap.class, new ContextAccessor());
}

 

当从ContextMap取值的时候,会执行ContextAccessor中的如下方法:

@Override
public Object getProperty(Map context, Object target, Object name)
    throws OgnlException {
  Map map = (Map) target;

  Object result = map.get(name);
  if (map.containsKey(name) || result != null) {
    return result;
  }

  Object parameterObject = map.get(PARAMETER_OBJECT_KEY);
  if (parameterObject instanceof Map) {
    return ((Map)parameterObject).get(name);
  }

  return null;
}

参数中的target就是ContextMap类型的,所以可以直接强转为Map类型。 
参数中的name就是我们写在动态SQL中的属性名。

 

-------------------------------------------------总结----------------------------------------------------
1  动态SQL 中的参数和${_param}的参数,采用的都是OGNL的方式来调用参数,这时,如果是单个参数的话,只能采用_paramter的方式来传递值
  也就是只能靠

  public static final String PARAMETER_OBJECT_KEY = "_parameter";

  这个转换后的方式来传递参数

2  #{_parame}的方式,如果是传递的单个参数,那么在#{_param}中可以随便写,可以用#{id}(方便理解)或者#{_parameter}

posted @ 2015-07-07 18:46  roscee  阅读(1928)  评论(0编辑  收藏  举报