MyBatis-@param注解详解

一、源码解析
一、useActualParamName配置
useActualParamName: 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的工程必须采用Java 8编译,并且加上-parameters选项。(从3.4.1开始)
mybatis的全局配置useActualParamName决定了mapper中参数的写法,默认为true
1.如果useActualParamName设置为true时
传递参数需要使用

#{arg0}-#{argn}或者#{param1}-#{paramn}

2.如果useActualParamName设置为false时
传递参数需要使用

#{0}-#{n}或者#{param1}-#{paramn}

二、源码解读(3.4.6)
在mapper的代理对象调用方法时,最终会是MapperMethod对象的execute方法。如下:

@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)) {
      return invokeDefaultMethod(proxy, method, args);
    }
  } catch (Throwable t) {
    throw ExceptionUtil.unwrapThrowable(t);
  }
  // 从缓存中获取MapperMethod 对象,如果没有就创建新的并添加
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  // 执行sql 语句
  return mapperMethod.execute(sqlSession, args);
}

MapperMethod的一个内部类MethodSignature封装了Mapper接口中定义的方法的相关信息。而MethodSignature的一个属性ParamNameResolver对象处理接口中定义的方法的参数列表。

ParamNameResolver 的属性

// 记录参数在参数列表中的位置索引与参数名称之间的对应关系
private final SortedMap<Integer, String> names;

// 记录对应的方法参数是否使用了@Param注解
private boolean hasParamAnnotation;

ParamNameResolver的构造函数

/**
 * 通过反射读取方法中的信息,并初始化上面两个字段
 * @param config
 * @param method
 */
public ParamNameResolver(Configuration config, Method method) {
  // 获取参数列表中每个参数的类型
  final Class<?>[] paramTypes = method.getParameterTypes();
  // 获取参数列表上的注解  @Param
  final Annotation[][] paramAnnotations = method.getParameterAnnotations();
  // 该集合用于记录参数索引与参数名称的对应关系
  final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
  int paramCount = paramAnnotations.length;
  // 遍历所有参数
  for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
    if (isSpecialParameter(paramTypes[paramIndex])) {
      // 如果参数是RowBounds类型或者ResultHandler类型,则跳过该参数
      continue;
    }
    String name = null;
    // 遍历该参数上的注解集合
    for (Annotation annotation : paramAnnotations[paramIndex]) {
      if (annotation instanceof Param) {
        // 获取@Param注解指定的参数名称
        hasParamAnnotation = true;
        name = ((Param) annotation).value();
        break;
      }
    }
    // 没有@Param注解的话 执行下面逻辑
    if (name == null) {
      // useActualParamName==true时  即name = arg0 ...
      if (config.isUseActualParamName()) {
        name = getActualParamName(method, paramIndex);
      }
      if (name == null) {//useActualParamName == false是  即 name="0" ...
        // use the parameter index as the name ("0", "1", ...)
        // 使用参数的索引作为其名称
        name = String.valueOf(map.size());
      }
    }
    map.put(paramIndex, name);
  }
  names = Collections.unmodifiableSortedMap(map);
}

names集合主要是在ParamNameResolver.getNamedParams方法中使用

/**
 *
 * @param args 用户传入的参数值列表
 * @return
 */
public Object getNamedParams(Object[] args) {
  final int paramCount = names.size();
  if (args == null || paramCount == 0) {
    return null;
  } else if (!hasParamAnnotation && paramCount == 1) {// 未使用@Param注解且参数列表只有一个
    return args[names.firstKey()];//即args[0] 参数的值
  } else {
    // 下面是为参数创建param+索引的格式作为默认参数名称 如:param1  下标从1开始
    final Map<String, Object> param = new ParamMap<Object>();
    int i = 0;
    for (Map.Entry<Integer, String> entry : names.entrySet()) {
      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
      if (!names.containsValue(genericParamName)) {
        param.put(genericParamName, args[entry.getKey()]);
      }
      i++;
    }
    return param;
  }
}

二.使用@param参数注解
一、单一属性
service层示例

todoItemsMapper.selectItembyparam1(1,"medium")

dao层示例

List<CuxTodoItems> selectItembyparam1(@Param("id") int userId,@Param("pty") String Priority);

xml映射对应示例

  <select id="selectItembyparam1" resultMap="BaseResultMap">
      SELECT *
      FROM  cux_todo_items
      WHERE user_id = #{id}
      AND   priority = #{pty}
  </select>

数据库数据

查询结果

[{
    "todoItemId": 2,
    "userId": 1,
    "todoItemTitle": "bbbb",
    "todoItemContent": "待办事项2",
    "priority": "medium",
    "creationDate": 1544405869000,
    "lastUpdateDate": 1544498631000,
    "comments": "bb"
}, {
    "todoItemId": 4,
    "userId": 1,
    "todoItemTitle": "dddd",
    "todoItemContent": "待办事项4",
    "priority": "medium",
    "creationDate": 1544422911000,
    "lastUpdateDate": 1544422980000,
    "comments": "ddd"
}]

注意:采用#{}的方式把@Param注解括号内的参数进行引用(括号内参数对应的是形参)xml映射的查询字段应该为@Param注解括号内的参数例如上面查询参数应该为id而不是userId,如果xml映射的查询字段不与@Param注解的形参一样则会出现如下异常


二、JavaBean对象
一、单个javabean对象
service 层示例

   CuxTodoItems items = new CuxTodoItems();
   items.setUserId(1);
   items.setPriority("low");
   List<CuxTodoItems> itemsList = todoItemsMapper.selectItembyparam2(items);
   return itemsList;

dao层示例

    List<CuxTodoItems> selectItembyparam2(@Param("item") CuxTodoItems cuxTodoItems);

xml映射对应示例

<select id="selectItembyparam2" resultMap="BaseResultMap">
     SELECT *
     FROM  cux_todo_items
     WHERE user_id = #{item.userId}
     AND   priority = #{item.priority}
</select>

数据库数据

查询结果:

    [{
        "todoItemId": 5,
        "userId": 1,
        "todoItemTitle": "eee",
        "todoItemContent": "待办事项5",
        "priority": "low",
        "creationDate": 1544423076000,
        "lastUpdateDate": 1544494032000,
        "comments": "ee"
    }, {
        "todoItemId": 6,
        "userId": 1,
        "todoItemTitle": "fffffffff",
        "todoItemContent": "ffffffffff",
        "priority": "low",
        "creationDate": 1544495766000,
        "lastUpdateDate": 1544495772000,
        "comments": "ffffffffffffffffffff"
    }]

二、多个javabean对象
service层

    CuxTodoItems items = new CuxTodoItems();
    items.setPriority("high");
    CuxUsers users = new CuxUsers();
    users.setUserId(2);
    List<CuxTodoItems> itemsList = todoItemsMapper.selectItembyparam3(items,users);

dao层

    List<CuxTodoItems> selectItembyparam3(@Param("it") CuxTodoItems cuxTodoItems, @Param("us")CuxUsers user);
1
xml对应示例

<select id="selectItembyparam3" resultMap="BaseResultMap">
   SELECT *
   FROM  cux_todo_items
   WHERE user_id = #{us.userId}
   AND   priority = #{it.priority}
 </select>

数据库数据

查询结果

    [{
        "todoItemId": 7,
        "userId": 2,
        "todoItemTitle": "ggg",
        "todoItemContent": "代办事项g",
        "priority": "high",
        "creationDate": 1551448314000,
        "lastUpdateDate": 1552485099000,
        "comments": "备注g"
    }]

注意:当使用@param注解JavaBean对象时,@Param注解括号内的参数为形参,xml内的查询字段应该是 形参.bean属性,若形参.bean属性的形参不与@Param注解括号内的参数形参一样或者直接使用bean属性为查询字段则会出现如下异常:


三.不使用@param参数注解
一、单一属性
一、一个单一属性
service

todoItemsMapper.selectItembyparam4(2);

dao 层

List<CuxTodoItems> selectItembyparam4( int userId);

xml对应示例

<select id="selectItembyparam4" resultMap="BaseResultMap">
        SELECT *
        FROM  cux_todo_items
        WHERE user_id = #{aa}
</select>

数据库数据

查询结果:

[{
    "todoItemId": 7,
    "userId": 2,
    "todoItemTitle": "ggg",
    "todoItemContent": "代办事项g",
    "priority": "high",
    "creationDate": 1551448314000,
    "lastUpdateDate": 1552485099000,
    "comments": "备注g"
}]

注意:当查询参数为一个单一属性且没有使用注解时xml中查询字段#{}内可以为任意值

二、多个单一属性
service层

todoItemsMapper.selectItembyparam5(1,"high");

dao层

List<CuxTodoItems> selectItembyparam5( int userId,String Priority);

xml对应示例

<select id="selectItembyparam5" resultMap="BaseResultMap">
        SELECT *
        FROM  cux_todo_items
        WHERE user_id = #{param1}
        AND   priority = #{param2}
 </select>

数据库数据

查询结果:

[{
    "todoItemId": 3,
    "userId": 1,
    "todoItemTitle": "ccc",
    "todoItemContent": "待办事项3",
    "priority": "high",
    "creationDate": 1544405882000,
    "lastUpdateDate": 1544423018000,
    "comments": "cc"
}]

注意:
1.如果useActualParamName设置为true时
传递参数需要使用#{arg0}-#{argn}或者#{param1}-#{paramn}
若使用#{0}-#{n},则会出现如下异常


2.如果useActualParamName设置为false时
传递参数需要使用 #{0}-#{n}或者#{param1}-#{paramn}
若使用#{arg0}-#{argn}则会出现如下异常


二、JavaBean对象
一、单个javabean对象
service层

CuxTodoItems items = new CuxTodoItems();
items.setUserId(2);
items.setPriority("high");
List<CuxTodoItems> itemsList = todoItemsMapper.selectItembyparam6(items);

dao层

List<CuxTodoItems> selectItembyparam6( CuxTodoItems cuxTodoItems);

xml对应查询字段

<select id="selectItembyparam6" resultMap="BaseResultMap">
  SELECT *
  FROM  cux_todo_items
  WHERE user_id = #{userId}
  AND priority = #{priority}
</select>

数据库数据

查询结果

[{
    "todoItemId": 7,
    "userId": 2,
    "todoItemTitle": "ggg",
    "todoItemContent": "代办事项g",
    "priority": "high",
    "creationDate": 1551448314000,
    "lastUpdateDate": 1552485099000,
    "comments": "备注g"
}]

注意:xml查询字段需要与javabean属性一致

二、多个javabean对象
service层

CuxTodoItems items = new CuxTodoItems();
items.setPriority("medium");
CuxUsers users = new CuxUsers();
users.setUserId(1);
List<CuxTodoItems> itemsList = todoItemsMapper.selectItembyparam7(items,users);

dao层

List<CuxTodoItems> selectItembyparam7( CuxTodoItems cuxTodoItems, CuxUsers user);

xml对应查询

<select id="selectItembyparam7" resultMap="BaseResultMap">
        SELECT *
        FROM  cux_todo_items
        WHERE priority = #{param1.priority}
        AND   user_id = #{param2.userId}
 </select>

数据库数据

查询结果:

[{
    "todoItemId": 2,
    "userId": 1,
    "todoItemTitle": "bbbb",
    "todoItemContent": "待办事项2",
    "priority": "medium",
    "creationDate": 1544405869000,
    "lastUpdateDate": 1544498631000,
    "comments": "bb"
}, {
    "todoItemId": 4,
    "userId": 1,
    "todoItemTitle": "dddd",
    "todoItemContent": "待办事项4",
    "priority": "medium",
    "creationDate": 1544422911000,
    "lastUpdateDate": 1544422980000,
    "comments": "ddd"
}]

注意:
1.如果useActualParamName设置为true时
传递参数需要使用#{arg0.bean属性}-#{argn.bean属性}或者#{param1.bean属性}-#{paramn.bean属性}
若使用#{0.bean属性}-#{n.bean属性},则会出现如下异常

2.如果useActualParamName设置为false时
传递参数需要使用 #{0.bean属性}-#{n.bean属性}或者#{param1.bean属性}-#{paramn.javabean属性}
若使用#{arg0.bean属性}-#{argn.bean属性}则会出现如下异常

四、总结:
1.如果接口方法有一个或多个参数,并且使用了@Param注解,若注解的为单一属性则sql语句中的参数用注解的value值,若为bean则sql语句中使用的参数注解的value.bean属性值

2.如果接口方法的参数只有一个,并且没有使用@Parma注解,若为单一属性,则sql语句直接使用任何名称均可,若为bean,则sql 语句中的参数直接为bean的属性值就行。

3.如果接口的方法有多个参数,并且没有使用@Parma注解,单一属性 时sql语句使用param1…paramn,bean时sql语句使用param1.属性值…paramn.属性值是不会错的。

项目地址:
https://github.com/xu158/MyBatisParam.git
参考博客:
https://blog.csdn.net/u012031380/article/details/54924641/
https://my.oschina.net/u/3737136/blog/1811654
————————————————

MyBatis中的@Param注解用于指定参数的名称,以便在SQL语句中引用。在使用该注解时,需要注意以下几点: 1. 单一属性:使用#{}的方式将@Param注解中的参数名称引用到SQL语句中。例如,如果@Param注解的参数名称为id,则在XML映射文件中查询字段应该为#{id}而不是#{userId}。 2. JavaBean对象:对于单个JavaBean对象,可以直接引用该对象的属性名称作为参数。例如,如果参数为一个名为user的JavaBean对象,可以在SQL语句中直接引用user对象的属性,如#{user.id}、#{user.name}等。 3. useActualParamName配置项:MyBatis的全局配置项useActualParamName决定了是否允许使用方法签名中的参数名称作为语句参数名称。默认情况下,该配置项为true,表示可以使用方法签名中的参数名称。如果想要启用该特性,需要确保项目使用Java 8编译,并在编译参数中加上-parameters选项。使用该特性时,传递参数时需要使用参数名称作为键,如#{paramName}。 总结起来,@Param注解MyBatis中用于指定参数名称的注解,可以让开发者更灵活地在SQL语句中引用参数。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [MyBatis-@param注解详解](https://blog.csdn.net/xuonline4196/article/details/87994394)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值