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