一、简介
使用Mybatis最大的特性就是sql需要自己写,而写sql难免需要传递多个参数。相信大家在使用Mybatis的时候都遇到过 "Parameter 'array' not found. Available parameters are [xsids, param1]" 类似这样的错误,本文将总结一下Mybatis复杂参数传参取参的方式。
二、不使用@Param注解
【a】传递List<xx>类型参数:当不使用@Param注解时,在xml中通过foreach循环,此时必须指定collection=”list”或者collection=”collection”才能取出参数;
(1). Mapper.java:
/**
* mybatis传复杂参数(不使用@Param("xxx")指定参数名称)
*/
List<Map<String, Object>> getStudentsByListNoParamName(List<Integer> pkids);
(2). Mapper.xml:
<!--
List<Map<String, Object>> getStudentsByListNoParamName(List<Integer> pkids);
注意: 因为没有通过@Param指定参数名称,mybatis规定如果是Collection并且没有指定参数名称,那么collection="list"必须为list或collection
否则报错:Parameter 'xxx' not found. Available parameters are [collection, list]
-->
<select id="getStudentsByListNoParamName" resultType="map" parameterType="list">
select * from tbl_student t
where 1 = 1
and t.s_id in
<foreach collection="collection" open="(" close=")" separator="," index="index" item="pkid">
#{pkid}
</foreach>
</select>
或者:
<!--
List<Map<String, Object>> getStudentsByListNoParamName(List<Integer> pkids);
注意: 因为没有通过@Param指定参数名称,mybatis规定如果是Collection并且没有指定参数名称,那么collection="list"必须为list或collection
否则报错:Parameter 'xxx' not found. Available parameters are [collection, list]
-->
<select id="getStudentsByListNoParamName" resultType="map" parameterType="list">
select * from tbl_student t
where 1 = 1
and t.s_id in
<foreach collection="list" open="(" close=")" separator="," index="index" item="pkid">
#{pkid}
</foreach>
</select>
(3). 单元测试代码:
@Test
public void testNoParams() {
List<Integer> pkids = new ArrayList<>();
pkids.add(1);
pkids.add(2);
List<Map<String, Object>> students1 = studentMapper.getStudentsByListNoParamName(pkids);
for (Map<String, Object> map : students1) {
System.out.println(map);
}
}
(4).运行结果:
如果xml写错为下面,则会报错:
<select id="getStudentsByListNoParamName" resultType="map" parameterType="list">
select * from tbl_student t
where 1 = 1
and t.s_id in
<foreach collection="xsids" open="(" close=")" separator="," index="index" item="pkid">
#{pkid}
</foreach>
</select>
【b】传递Array数组类型参数:当不使用@Param注解时,在xml中通过foreach循环,此时必须指定collection=”array”才能取出参数;
(1). Mapper.java:
List<Map<String, Object>> getStudentsByArrayNoParamName(Integer[] pkids);
(2). Mapper.xml:
<!--
List<Map<String, Object>> getStudentsByArrayNoParamName(Integer[] pkids);
注意: 因为没有通过@Param指定参数名称,mybatis规定如果是Array数组类型并且没有指定参数名称,那么collection="array"必须为array
否则报错:假如我们指定collection="array1",那么报错如下: Parameter 'array1' not found. Available parameters are [array]
-->
<select id="getStudentsByArrayNoParamName" resultType="map">
select * from tbl_student t
where 1 = 1
and t.s_id in
<foreach collection="array" open="(" close=")" separator="," index="index" item="pkid">
#{pkid}
</foreach>
</select>
(3).单元测试代码:
@Test
public void testNoParams() {
List<Map<String, Object>> students2 = studentMapper.getStudentsByArrayNoParamName(new Integer[]{1, 2, 3});
for (Map<String, Object> map : students2) {
System.out.println(map);
}
}
(4). 运行结果:
如果xml文件错写成,则会报错。
<select id="getStudentsByArrayNoParamName" resultType="map">
select * from tbl_student t
where 1 = 1
and t.s_id in
<foreach collection="array12" open="(" close=")" separator="," index="index" item="pkid">
#{pkid}
</foreach>
</select>
【c】传递Map类型参数:当不使用@Param注解时,在xml文件可以通过#{map的key}取出参数值;
(1). Mapper.java:
List<Map<String, Object>> getStudentsByMapNoParamName(Map<String, Object> param);
(2). Mapper.xml:
<!--
List<Map<String, Object>> getStudentsByMapNoParamName(Map<String, Object> param);
注意: 因为没有通过@Param指定参数名称,mybatis规定如果是map类型并且没有指定参数名称,那么在xml中取值的时候:
对于普通属性:对应map.put('aaa',bbb) map中的key键
对于集合类型属性:也是对应map.put('aaa',bbb) map中的key键
对于这个示例,如下:
paramMap.put("sname", "zhangsan2"); 取值方式 -> #{sname}
paramMap.put("pkids", pkids2); 取值方式 -> collection="pkids"
-->
<select id="getStudentsByMapNoParamName" parameterType="map" resultType="map">
select * from tbl_student t
where 1 = 1
<if test="sname!=null and sname!=''">
and t.s_name = #{sname}
</if>
<if test="pkids != null and pkids.size() > 0">
and t.s_id in
<foreach collection="pkids" open="(" close=")" separator="," index="index" item="pkid">
#{pkid}
</foreach>
</if>
</select>
(3).单元测试代码:
@Test
public void testNoParams() {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("sname", "zhangsan2");
List<Integer> pkids2 = new ArrayList<>();
pkids2.add(1);
pkids2.add(2);
paramMap.put("pkids", pkids2);
List<Map<String, Object>> students3 = studentMapper.getStudentsByMapNoParamName(paramMap);
for (Map<String, Object> map : students3) {
System.out.println(map);
}
}
(4). 运行结果:
三、使用@Param注解
【a】传递List<xx>类型参数:假如指定了参数名称为xsids,那么取值方式:collection="xsids", 此时默认的collection、list会失效;
(1).Mapper.java:
/**
* mybatis传复杂参数(使用@Param("xxx")指定参数名称)
*/
List<Map<String, Object>> getStudentsByListWithParamName(@Param("xsids") List<Integer> pkids);
(2).Mapper.xml:
<!--
List<Map<String, Object>> getStudentsByListWithParamName(@Param("xsids") List<Integer> pkids);
因为我们指定了参数名称为xsids,所以取值方式:collection="xsids", 此时默认的collection、list会失效
如果我们使用collection="list", 会报错:Parameter 'list' not found. Available parameters are [xsids, param1]
-->
<select id="getStudentsByListWithParamName" resultType="map" parameterType="list">
select * from tbl_student t
where 1 = 1
and t.s_id in
<foreach collection="xsids" open="(" close=")" separator="," index="index" item="xsid">
#{xsid}
</foreach>
</select>
(3).单元测试代码:
@Test
public void testWithParams() {
List<Integer> pkids = new ArrayList<>();
pkids.add(1);
pkids.add(2);
List<Map<String, Object>> students1 = studentMapper.getStudentsByListWithParamName(pkids);
for (Map<String, Object> map : students1) {
System.out.println(map);
}
}
(4).运行结果:
如果xml中collection不是“xsid”,那么会报错
<select id="getStudentsByListWithParamName" resultType="map" parameterType="list">
select * from tbl_student t
where 1 = 1
and t.s_id in
<foreach collection="list" open="(" close=")" separator="," index="index" item="xsid">
#{xsid}
</foreach>
</select>
【b】传递Array类型参数:假如我们指定了参数名称为xsids,那么取值方式:collection="xsids",此时默认的array会失效;
(1).Mapper.java:
List<Map<String, Object>> getStudentsByArrayWithParamName(@Param("xsids") Integer[] pkids);
(2).Mapper.xml:
<!--
List<Map<String, Object>> getStudentsByArrayWithParamName(@Param("xsids") Integer[] pkids);
因为我们指定了参数名称为xsids,所以取值方式:collection="xsids",此时默认的array会失效.
如果我们使用collection="array", 会报错:Parameter 'array' not found. Available parameters are [xsids, param1]
-->
<select id="getStudentsByArrayWithParamName" resultType="map">
select * from tbl_student t
where 1 = 1
and t.s_id in
<foreach collection="xsids" open="(" close=")" separator="," index="index" item="pkid">
#{pkid}
</foreach>
</select>
(3).单元测试代码:
@Test
public void testWithParams() {
List<Map<String, Object>> students2 = studentMapper.getStudentsByArrayWithParamName(new Integer[]{1, 2, 3});
for (Map<String, Object> map : students2) {
System.out.println(map);
}
}
(4).运行结果:
如果xml中的collection不是@Param指定的名字的话,同样会报错:
<select id="getStudentsByArrayWithParamName" resultType="map">
select * from tbl_student t
where 1 = 1
and t.s_id in
<foreach collection="xsids123" open="(" close=")" separator="," index="index" item="pkid">
#{pkid}
</foreach>
</select>
【c】传递Map类型参数:假如我们使用@Param("paramMap")指定了参数名称为paramMap,那么我们在xml中取值的时候,就需要通过paramMap.xxx,xxx就是对应map中的键;
(1).Mapper.java:
List<Map<String, Object>> getStudentsByMapWithParamName(@Param("paramMap") Map<String, Object> param);
(2).Mapper.xml:
<!--
List<Map<String, Object>> getStudentsByMapWithParamName(@Param("paramMap") Map<String, Object> param);
由于我们使用@Param("paramMap")指定了参数名称为paramMap,所以我们在xml中取值的时候,就需要通过paramMap.xxx,
xxx就是对应map中的键,如下
paramMap.put("sname", "zhangsan2"); 取值方式 -> #{paramMap.sname}
paramMap.put("pkids", pkids2); 取值方式 -> collection="paramMap.pkids"
-->
<select id="getStudentsByMapWithParamName" parameterType="java.util.Map" resultType="map">
select * from tbl_student t
where 1 = 1
<if test="paramMap.sname!=null and paramMap.sname!=''">
and t.s_name = #{paramMap.sname}
</if>
<if test="paramMap.pkids != null and paramMap.pkids.size() > 0">
and t.s_id in
<foreach collection="paramMap.pkids" open="(" close=")" separator="," index="index" item="pkid">
#{pkid}
</foreach>
</if>
</select>
(3).单元测试代码:
@Test
public void testWithParams() {
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("sname", "zhangsan2");
List<Integer> pkids2 = new ArrayList<>();
pkids2.add(1);
pkids2.add(2);
paramMap.put("pkids", pkids2);
List<Map<String, Object>> students3 = studentMapper.getStudentsByMapWithParamName(paramMap);
for (Map<String, Object> map : students3) {
System.out.println(map);
}
}
(4).运行结果:
注意,取出参数的时候需要与Map中的key值对应上,否则取不出参数值。
四、mybatis参数封装成map源码解析
-
下面通过查看mybatis源码了解一下里面是怎么封装参数的,同时可以加深我们对传递参数和取出参数的理解。Mybatis中参数解析过程主要是在这个类 -> ParamNameResolver.java: 下面对源码做了一些注释说明,大家也可以debug一步一步查看其中怎么封装参数的。
// 假设args=[2,学生2],names={0=sid,1=sname}
public Object getNamedParams(Object[] args) {
int paramCount = this.names.size();
//参数不为空时,封装成map
if (args != null && paramCount != 0) {
//如果没有@Param注解并且参数个数只有一个,直接返回args[0]
if (!this.hasParamAnnotation && paramCount == 1) {
return args[(Integer)this.names.firstKey()];
} else {
Map<String, Object> param = new ParamMap();
int i = 0;
//遍历names: {0=sid,1=sname}
for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) {
Entry<Integer, String> entry = (Entry)var5.next();
//names集合的value作为key;
//names集合的key作为取值的参考args[0]
//params: sid=args[0]=2 sname=args[1]=学生2
param.put(entry.getValue(), args[(Integer)entry.getKey()]);
String genericParamName = "param" + String.valueOf(i + 1);
//注意从1开始, param1...paramN
//params: param1=2 param2=学生2
if (!this.names.containsValue(genericParamName)) {
param.put(genericParamName, args[(Integer)entry.getKey()]);
}
}
return param;
}
} else {
//参数为空的时候直接return null
return null;
}
}
五、总结
- 方法不加@param注解时,并且只有一个参数的时候:
【a】String类型等,xml中可以通过#{任意值}都可以取出参数值;
【b】Entity对象类型:xml中可以通过#{实体对象中的属性名称}取出参数值;
【c】Map 类型:xml文件可以通过#{map中的键key}可以取出参数值;
【d】List 类型:xml中必须要写collection=”list”或者 collection=”collection”才能够取出参数值;
【e】Array数组类型:xml中必须要写collection=”array”才能够取出参数值;
- 方法加@param注解时,并且只有一个参数的时候:
【a】String类型等:如@param(value="name") ,xml中可以使用#{name}或者#{param1}取出参数值;
【b】对象Entity类型: 如@param(value="student"),xml可以通过#{student.对象属性}取值,或者#{param1.对象属性}取出参数值;
【c】Map 类型:如@param(value="paramsMap"),xml可以通过#{paramsMap.map的key}取值,或者#{param1.map的key}取出参数值;
【d】List集合类型:如@param(value="ids"),xml中可以通过 collection=“ids”或者collection=”param1”取出参数值;
【e】Array类型:如@param(value="ids"),此时要写collection=“ids”或者collection=”param1”取出参数值;
- 当有多个参数的时候,如果没有指定@Param,那么可以使用#{param1},#{param2}…#{paramN}取出 或者 使用#{0},#{1}…#{N}取出,否则会报参数找不到异常;如果指定了@Param(value="xxx"),那么取参数的时候 可以使用@Param中指定的名称#{xxx}取出 或者 使用#{param1},#{param2}…#{paramN}取出。