背景:
一 、MyBatis多参数传递 四种情况需要加@Param
List<Device> getDeviceListTest(@Param("deviceId")String deviceId,@Param("deviceName")String deviceName);
3.XML 中的 SQL 使用了 $ ,那么参数中也需要 @Param 注解,$ 会有注入漏洞的问题,但是有的时候你必须要 $ 符号,例如要传入列名或者表名的时候,这个时候必须要
//mapper 接口
List<Device> getDeviceListTest(@Param("columnName") String columnName);
<!--mybatis的xml-->
select * from t_device order by ${columnName} desc
4.动态 SQL ,如果在动态 SQL 中使用了参数作为变量,那么也需要 @Param 注解,即使你只有一个参数。
//mapper 接口
List<Device> getDeviceListTest(@Param("deviceId") String deviceId);
<!--mybatis的xml-->
<select id="getDeviceListTest" parameterType="String" resultType="Device">
select * from t_device
<where>
<if test="deviceId != null and deviceId != ''">
device_id=#{deviceId}
</if>
</where>
</select>
二 、为啥平时写代码有些时候不加可以正常运行 ,谁在搞鬼?
测试 多参数时,不写 @Param
List<Device> getDeviceListTest(String deviceId,String deviceName);
得到结果如下图所示:
历史原因:
在Java8之前,可以说你无法做到(你是不可能读取这个 id) 的,因为Java在编译的时候会将 String deviceId
编译为 String arg0
,然而Java8中新增了这样的一个特性,你可以在编译的时候设定保留参数名称.详见源码分析
错误总结:
注: 使用jdk1.7得到的是: [1, 0, param1, param2]
使用1.8得到的则是: [arg1, arg0, param1, param2]
例如 xml 可以这样写,但这种方法不建议使用,sql层表达不直观,且一旦顺序调整容易出错。
为了证明上述解释是对的:以下sql语句可以完美运行
select * from t_device where device_id = #{arg0} and device_name != #{arg1}
idea有时可以不加@Param,那么它 对我的代码做了什么?
但是 你如使用的是idea ,即时不写@Param 也能成功,原因是
IDEA编译时采取了强制保持方法参数变量名,但需要满足如下
1. 必须是jdk8或以上
2. 编译器参数-parameters
三、错误源码分析
debug断点进入service实现类
deviceMapper.getDeviceListTest(device.getDeviceId(),device.getDeviceName());
进入后,见下图,怎么样熟悉的感觉吧 JDK动态代理,我们写的mapper 接口,MyBatis 通过动态代理,自动添加了实现类,主要看invoke() 方法,参数变成了数组args
下面对 paramAnnotations 的遍历,如果没有设置 @Param,那么 name 也不会有值,那么将会通过 getActualParamName 来获取参数值。获取实体或者map 里的参数名,或者直接得到参数名arg0 补充:getActualParamName 使用了 JDK 1.8 新增特性,反射获取参数名