@Param注解使用遇到的坑

一、情景复现

业务需要CURD的方法,而C的方法我是这么写的:

Mapper接口

Integer createOneAnnouncement(@Param("createOneAnnouncementQuery") CreateOneAnnouncementQuery createOneAnnouncementQuery);
SQL语句

<insert id="createOneAnnouncement" parameterType="CreateOneAnnouncementQuery">
insert into ann_basic ( ann_basic_no, ann_type,
  title, content, channel,
  if_comment, priority, publish_user_id,
  publish_time, end_time, is_visible,
  visible_time, if_delete, delete_time,
  user_type, update_time, create_time
  )
values ( #{annBasicNo,jdbcType=VARCHAR}, #{annType,jdbcType=TINYINT},
  #{title,jdbcType=VARCHAR}, #{content,jdbcType=VARCHAR}, #{channel,jdbcType=TINYINT},
  #{ifComment,jdbcType=TINYINT}, #{priority,jdbcType=BIGINT}, #{publishUserId,jdbcType=BIGINT},
  #{publishTime,jdbcType=TIMESTAMP}, #{endTime,jdbcType=TIMESTAMP}, #{isVisible,jdbcType=TINYINT},
  #{visibleTime,jdbcType=TIMESTAMP}, #{ifDelete,jdbcType=TINYINT}, #{deleteTime,jdbcType=TIMESTAMP},
  #{userType,jdbcType=TINYINT}, #{updateTime,jdbcType=TIMESTAMP}, #{createTime,jdbcType=TIMESTAMP}
  )
</insert>

乍一看没有任何问题,但是报错:

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.binding.BindingException: Parameter 'annBasicNo' not found. Available parameters are [createOneAnnouncementQuery, param1]

为何?

二、源码解释表象

下面是Mybatis的获取参数的源码:

public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
 if (args == null || paramCount == 0) {
        //接口方法没有参数,返回null
 return null;
 } else if (!hasParamAnnotation && paramCount == 1) {
        //接口方法上只有1个参数则返回唯一的那个对象
 return args[names.firstKey()];
 } else {
        //接口方法上不止一个参数,就会把所有的参数封装到Map中然后返回
 final Map<String, Object> param = new ParamMap<Object>();
 int i = 0;
 for (Map.Entry<Integer, String> entry : names.entrySet()) {
            //把@Param注解中的value作为key,对应变量的值作为value
 param.put(entry.getValue(), args[entry.getKey()]);
 //自动生成key (param1, param2, ...)
 final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
 //为了不覆盖@Param注解设置的key
 if (!names.containsValue(genericParamName)) {
                param.put(genericParamName, args[entry.getKey()]);
 }
            i++;
 }
        return param;
 }
}

一句话总结:参数列表以Map键值对的形式传递给下游,而使用了@Param 注解的mybatis将注解的value值,即@Param(“createOneAnnouncementQuery”)中的createOneAnnouncementQuery取了出来作为key

但是这是没有值即没有value的,所以我会得到空的值,报错才会显示Parameter ‘annBasicNo’ not found.

三、@Param 注解正确的使用姿势

第一种:方法有多个参数,需要@Param(最常见)
例如下面这样:
接口方法:

Integer insertUser(@Param("id") Integer id, @Param("username") String username, @Param("password") String password, @Param("age") Integer age);
xml文件:

第二种:方法参数要取别名,需要@Param
当需要给一个参数取一个别名时,我们也需要@Param注解,例如:
接口方法:
User getUserByUsername(@Param("name") String username);
xml文件:

<insert id="insertUser">
 INSERT INTO T_USER (ID, USERNAME, PASSWORD, AGE)
 VALUES(#{id}, #{username}, #{password}, #{age})
 </insert>

第二种:方法参数要取别名,需要@Param
当需要给一个参数取一个别名时,我们也需要@Param注解,例如:
接口方法:
User getUserByUsername(@Param("name") String username);
xml文件:

SELECT * FROM T_USER WHERE USERNAME = #{name}

第三种:xml中的SQL使用了@Param,需要@Param 虽然存在SQL注入安全问题,但是有时候确实要使用,比如说传入列名或者表名时,就需要加上@Param注解,例如:
接口方法:
List<User> getUsersOrderByParam(@Param("order_by") String order_by);

xml文件:

SELECT * FROM T_USER ORDER BY ${order_by} DESC

第四种:动态SQL中使用参数作为变量,则需要@Param注解,即使只有一个参数。
这一种是比较特殊的,但是也是容易出错的,如果在动态SQL中使用参数作为判断条件,那么久要加上@Param注解,例如:
接口方法:
User getUserById(@Param("id") Integer id);
xml文件:

SELECT * FROM T_USER ID = #{id}

这种情况,即使只有一个参数,也需要添加@Param注解,否则会报错,这种情况也最容易被忽略。

四、总结

带着底层的逻辑看待一个表象的问题,会使得问题更容易解决。任何时候Why都比What重要

对于程序猿而言,一个程序员能力的提升不在于他做过多少项目,写过多少代码,而在于出现一个问题,它能给出多少的解决办法,提出一个需求能有多少举措

而实现这个目标,其一在于不断地总结,其二就是阅读源码、多读书。

展开阅读全文
©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值