当传入的参数是一个集合,我们需要遍历取出集合中的每一个参数时,就需要用到foreach标签了,这里介绍它的6个常用属性:
- collection:指传入可迭代(遍历)的参数的变量名称。
- 当传入的参数为list类型,为list
- 当传入的参数为array类型,为array
- 当传入的参数为map类型,为map的键key(较少使用)
- 当传入的参数是java bean类型,那么保持和该java bean对象中支持遍历的属性的名称一致
- item:迭代集合时,元素的别名
- index:在list和数组中,index是元素的序号,在map中,index是元素的key
- open:循环开始拼接的字符串
- close:循环结束拼接的字符串
- separator:循环中拼接的分隔符
其中collection和item参数为必填!
举个例子,多个id查询的sql语句可以写成这样
select * from user where id in (1,2,3,4)
上面的语句表示查询id为1,2,3,4的数据,那么如果在mybatis的映射文件该如何书写呢,我们总不可能直接写死这些id吧?
在<select>
标签的parameterType属性,我们可以指定参数类型为java.util.List,表示输入参数是一个list集合,显然我是想将所有需要查询的id保存到这个list集合中。
然后UserMapper.xml中的<select>
标签体内的sql语句就可以这样书写了:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- mapper接口代理实现规则:
1.映射文件中namespace要等于接口的全路径名称
2. 映射文件中sql语句的id要等于接口的方法名称
3. 映射文件中传入参数类型要等于接口方法的传入参数类型
4. 映射文件中返回结果集类型要等于接口方法的返回值类型,如果接口方法的返回值类型是集合,那么它表示的就是集合中的泛型参数类型 -->
<mapper namespace="blog.csdn.net.mchenys.mapper.UserMapper">
<select id="findUserByIds" parameterType="java.util.List"
resultType="blog.csdn.net.mchenys.poj.User">
select * from user
<where>
<if test="list != null">
<!-- 循环传入的集合参数
collection:传入参数中的集合的变量名称
item:每次循环从集合中取出的数据保存到item中
open:循环开始拼接的字符串
close:循环结束拼接的字符串
separator:循环中拼接的分隔符
-->
<foreach collection="list" item="id" open="id in(" close=")" separator=",">
#{id}
</foreach>
</if>
</where>
</select>
</mapper>
对于的UserMapper接口方法如下:
public List<User> findUserByIds(List<Integer> list);
由于select标签的parameterType属性传入的参数是List集合,所以foreach 标签的collection属性的变量名就是小写的list。
在UserMapperTest测试类中添加测试方法进行测试:
// 同时查询多个id
@Test
public void testFindUserByIds() throws Exception {
SqlSession session = factory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
List<Integer> ids = new ArrayList<Integer>();
ids.add(33);
ids.add(34);
ids.add(35);
ids.add(36);
List<User> list = mapper.findUserByIds(ids);
System.out.println(list);
}
控制台输出的结果:
假设我把foreach标签的collection属性值改成ids,再次运行就会看到这个错误:
意思就是找不到ids,可用的参数叫list。
封装传入参数
虽然parameterType的值可以直接指定具体的参数类型,但是还是建议将需要用到的参数封装到一个实体bean中,然后再把该实体bean当做输入参数来用。
还是以上面那个例子为例,我将List集合封装到一个java bean类中,例如:
public class QueryVo {
private List<Integer> ids;
public void setIds(List<Integer> ids) {
this.ids = ids;
}
public List<Integer> getIds() {
return ids;
}
}
然后映射文件修改如下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- mapper接口代理实现规则:
1.映射文件中namespace要等于接口的全路径名称
2. 映射文件中sql语句的id要等于接口的方法名称
3. 映射文件中传入参数类型要等于接口方法的传入参数类型
4. 映射文件中返回结果集类型要等于接口方法的返回值类型,如果接口方法的返回值类型是集合,那么它表示的就是集合中的泛型参数类型 -->
<mapper namespace="blog.csdn.net.mchenys.mapper.UserMapper">
<select id="findUserByIds2" parameterType="blog.csdn.net.mchenys.poj.QueryVo"
resultType="blog.csdn.net.mchenys.poj.User">
select * from user
<where>
<if test="ids != null">
<!-- 循环传入的集合参数
collection:传入的集合变量名称
item:每次循环从集合中取出的数据保存到item中指定的变量名
open:循环开始拼接的字符串
close:循环结束拼接的字符串
separator:循环中拼接的分隔符
-->
<foreach collection="ids" item="id" open="id in(" close=")" separator=",">
#{id}
</foreach>
</if>
</where>
</select>
</mapper>
对应的UserMapper接口添加一个findUserByIds2的方法
public List<User> findUserByIds2(QueryVo vo);
注意上面映射文件中foreach 标签的collection属性值变成了ids,因为QueryVo 类中的集合属性就是ids,这2个名字是必须对应的,否则会报错,稍后会演示该错误。
在UserMapperTest测试类中添加测试方法进行测试:
// 同时查询多个id
@Test
public void testFindUserByIds2() throws Exception {
SqlSession session = factory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
QueryVo queryVo = new QueryVo();
List<Integer> ids = new ArrayList<Integer>();
ids.add(10);
ids.add(16);
ids.add(22);
ids.add(24);
queryVo.setIds(ids);
List<User> list = mapper.findUserByIds2(queryVo);
System.out.println(list);
}
控制台输出的结果:
效果和直接传List集合的一样,但是封装后,灵活度大一点,因为我们可以将同一个业务的各种sql语句的传入参数都封装到一个java bean中进行维护。
上面提到过,如果映射文件中foreach标签的collection属性值和java bean中的集合属性名称不一样会报错,例如我将collection的属性值 ids 改成 ids2,再次运行测试代码就会看到这个错误:
意思就是在QueryVo实体类中找不到有叫ids2的属性,注意java bean规范中有getter方法的字段才能叫属性,看上图的异常也能说明这点,mybatis会去java bean中查找符合条件的getter方法,然后截取get后的方法名首字母小写就是对应的属性名,当然getter方法不是必须的,mybatis会根据collection属性值去java bean中优先找符合条件的字段名,如果找不到才会去找对应的getter方法名。
例如collection中的属性值是ids, 然后QueryVo实体类中的字段存在ids且类型是List集合类型,那么mybatis就能与之对应,无需提供getIds方法,用代码来表示就是这样的:
public class QueryVo {
private List<Integer> ids;//字段名是ids,且类型是List集合就符合条件了
public void setIds(List<Integer> ids) {
this.ids = ids;
}
//没有提供getIds方法也ok
}
同理,假设QueryVo实体类中ids字段的类型不是List,但是有个叫getIds的方法,且返回值是List集合类型,那么mybatis也不会报错,它也能识别,什么意思呢,用代码来表示就是这样的:
public class QueryVo {
private List<Integer> list;
private String ids; //字段符合,但是类型不符合
public void setIds(List<Integer> ids) {
this.list = ids;
}
public List<Integer> getIds() { //存在getter方法,且参类型符合也ok
return list;
}
}
运行后,一点问题都没有,顺利通过,不过话又说回来,没有人会这么干的,一般都是按照java bean的规范来,都会提供getter方法的。