为大家简单介绍一下在mybatis中的参数问题:
单个参数:mybatis不会做特殊处理
#{参数名}:取出参数值
多个参数:mybatis会做特殊处理
多个参数会被封装成一个map
key:param1...paramN或者参数的索引也可以
value:传入的参数的值
#{}就是从map中获取指定的key的值
异常:
org.apache.ibatis.binding.BindingException:
Parameter 'id' not found.
Available parameters are [0, 1, param1, param2]
操作:
方法:public Dept getByIdAndDname(Integer deptno,String dname);
取值:#{deptno},#{dname}
命名参数:明确指定封装参数是map的key:@Param("deptno")Integer deptno
public Dept getByIdAndDname(@Param("deptno")Integer deptno,@Param("dname")String dname);
多个参数会被封装成一个map
key:使用@Param注释指定的值
value: 参数值
#{指定的key}取出对应的参数值
POJO:
如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入pojo;
#{属性名}:取出传入的pojo的属性值
Map:
A.如果多个参数不是我们业务逻辑的数据模型,没有对应的pojo,不经常使用,为了方便,我们也可以传入map;
#{key}:取出map中对应的值
列子:
1.接口类:
public Dept getByMap(Map<String, Object> map);
2.实现接口类的xml:
<!-- public Dept getByMap(Map<String, Object> map); -->
<select id="getByMap" resultType="com.jadeon.mybatis.bean.Dept"
databaseId="oracle">
select * from dept where deptno = #{deptno} and dname=#{dname}
</select>
3.测试类:
Map<String, Object> map = new HashMap<>();
map.put("deptno", 10);
map.put("dname", "ACCOUNTING");
Dept dept = deptMapper.getByMap(map);
TO:
如果多个参数不是我们业务逻辑的数据模型,要经常使用,推荐来编写一个TO(Transfer Object)数据传输对象;
Page{
int index;
int size;
}
=======================思考=================================
public Dept getDept(@Param("deptno")Integer deptno,String dname);
取值:deptno==>>#{deptno/param1} dname==>>#{param2}
public Dept getDept(Integer deptno,@Param("d")Dept dept);
取值:deptno==>>#{param1} dname==>>#{param2.dname/d.dname}
#####特别注意:如果是Collection(List,Set)类型或者是数组,
也会做特殊处理, 也是把传入的list或者数组封装在map中。
key:Collection(collection),
如果是List还可以使用这个key(list)
数组(array)
public Dept getDeptById(List<Integer> ids);
取值:取出第一个值:#{list[0]}
=======================结合源码,mybatis怎么处理参数=================================
总结:参数多时会封装map,为了不混乱,我们可以使用注解@Param来指定封装时使用的key
使用#{key}就可以取出map中的值
(@Param("deptno")Integer deptno,@Param("dname")String dname);
ParamNameResolver类解析参数封装map的:
//1、names:{0=deptno,1=dname}:构造器的时候就已经确定
确定流程:
1.获取每个标了param注解的参数的@Param的值:deptno,dname;赋值给name;
2.每次解析一个参数给map中保存信息:(key:参数索引,value:name的值)
name的值:标注了param注解,注解的值
没有标注:
1.全局配置:useActualParamName(jdk1.8);name=参数名
2.name=map.size();相当于当前元素的索引
{0=deptno,1=dname,2=2,...}
args[1,"Tom","hellp"]
public Object getNamedParams(Object[] args) {
int paramCount = this.names.size();
//1、参数为null直接返回
if ((args == null) || (paramCount == 0))
return null;
//2、如果只有一个元素,并且没有Param注解;args[0]
if ((!(this.hasParamAnnotation)) && (paramCount == 1)) {
return args[((Integer) this.names.firstKey()).intValue()];
}
//3、多个元素或者Param有标注
Map param = new MapperMethod.ParamMap();
int i = 0;
//4.遍历names集合:{0=deptno,1=dname,2=2}
for (Map.Entry entry : this.names.entrySet()) {
//names集合的value作为key;names几个又作为取值的参考args[1,"Tom"]
//eg:{deptno=args[0]:10,dname:args[1]: ACCOUNTING,2=args[2}
param.put(entry.getValue(), args[((Integer) entry.getKey()).intValue()]);
//add generic param names(param1,param2,...)param
//额外的讲每一个参数也保存到map中,使用新的key:param1,param2,...paramN
//效果:有Param注解可以#{指定的key},或者(有没有注解都)#{param1}
String genericParamName = "param" + String.valueOf(i + 1);
if (!(this.names.containsValue(genericParamName))) {
param.put(genericParamName, args[((Integer) entry.getKey()).intValue()]);
}
++i;
}
return param;
}
=======================mybatis参数值得获取=================================
#{}:可以获取map中的值或者pojo对象属性的值
${}:可以获取map中的值或者pojo对象属性的值
sql映射文件:select * from dept where deptno = ${deptno} and dname=#{dname}
Preparing: select * from dept where deptno=10 and dname=?
区别:
#{}:是以预编译的形式,将参数设置到SQL语句中,PrepareStatement,放在SQL注入;
${}:取出的值直接封装到SQL语句中,会有安全问题;
大多数情况,我们取出参数的值都使用#{};
原生JDBC不支持占位符的地方我们就可以使用${}进行取值
比如分表:
按照年份分表拆分
SELECT * FROM ${year}_salary WHERE xxx;
排序:
select * from dept where order by ${deptno} ${desc/asc}
#{}:更丰富的用法
规定参数的一些规则:
javaType、jdbcType、mode(存储过程)、numbericScale、
resultMap、typeHandle、jdbcTypeName
jdbcType通常需要在某种特定的条件下被设置:
在我们数据null的时候,有些数据库可能不识别mybatis对null 的默认处理,比如Oracle报错;
JdbcType OTHER:无效的类型,因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型,Oracle不能正确处理;
列子:
错误:
insert into dept(dname,loc) values (#{dname},#{loc});
正确:
insert into dept(dname,loc) values (#{dname},#{loc,JdbcType=NULL});
由于全局配置文件中:jdbcTypeForNull=OTHER,Oracle不支持
解决办法:
1.#{loc,JdbcType=NULL}
2.jdbcTypeForNull=NULL
<setting name="jdbcTypeForNull" value="NULL"/>