一.foreach (在实现 mybatis in 语句查询时特别有用)
1.单参数List的类型
(1).创建表(t_blog)和添加数据
CREATE TABLE `t_blog` (
`id` INT (11),
`title` VARCHAR (765),
`content` VARCHAR (765),
`owner` VARCHAR (150)
);
INSERT INTO `t_blog` (`id`, `title`, `content`, `owner`) VALUES('1','java基础','详细描述','唐国强11');
INSERT INTO `t_blog` (`id`, `title`, `content`, `owner`) VALUES('2','中文','中国','ofo11');
INSERT INTO `t_blog` (`id`, `title`, `content`, `owner`) VALUES('3','html','html标签','样式');
INSERT INTO `t_blog` (`id`, `title`, `content`, `owner`) VALUES('6','html','www','eee');
INSERT INTO `t_blog` (`id`, `title`, `content`, `owner`) VALUES('9','中文','111','1111');
(2).实体类Blog
public class Blog {
private int id;
private String title;
private String content;
private String owner;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
@Override
public String toString() {
return "Blog{" +
"id=" + id +
", title='" + title + '\'' +
", content='" + content + '\'' +
", owner='" + owner + '\'' +
'}';
}
}
(3).工具类MyBatisUtil
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.InputStream;
public class MyBatisUtil {
public static SqlSessionFactory getSqlSessionFactory(){
/*
* 获取SqlSessionFactory
* */
String resource = "conf.xml";
InputStream is = MyBatisUtil.class.getClassLoader().getResourceAsStream(resource);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
return factory;
}
public static SqlSession getSqlSession(){
return getSqlSessionFactory().openSession();
}
/**
* 获取SqlSession
* @param isAutoCommit
* true 表示创建的SqlSession对象在执行完SQL之后会自动提交事务
* false 表示创建的SqlSession对象在执行完SQL之后不会自动提交事务,这时就需要我们手动调用sqlSession.commit()提交事务
* @return SqlSession
*/
public static SqlSession getSqlSession(boolean isAutoCommit){
return getSqlSessionFactory().openSession(isAutoCommit);
}
}
(5).定义接口BlogMapperI.java
import me.gacl.domain.Blog;
import java.util.List;
public interface BlogMapperI {
//方法名与select语句中的id一样getBlogForeachById1
public List<Blog> getBlogForeachById1(List<Integer> ids);
}
(6).定义映射文件BlogMapper.xml文件
collection的值为list,
<?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 namespace="me.gacl.Impl.BlogMapperI">**
<select id="getBlogForeachById1" resultType="Blog">
select * from t_blog where id IN
<foreach collection="list" index="index" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
</mapper>
(7).在核心配置文件conf.xml注册映射文件
<mappers>
<mapper resource="me/gacl/mapping/BlogMapper.xml"/>
</mappers>
(8).编写测试类TestBlog
import me.gacl.domain.Blog;
import me.gacl.Impl.BlogMapperI;
import me.gacl.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
public class TestBlog {
@Test
public void dynamicForeachTest1(){
SqlSession sqlSession=MyBatisUtil.getSqlSession(true);
BlogMapperI blogMapperI=sqlSession.getMapper(BlogMapperI.class);
//使用LIst集合添加3条数据
List<Integer> ids=new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(3);
List<Blog> blogs = blogMapperI.getBlogForeachById1(ids);
for (Blog blog : blogs){
System.out.println(blog);
sqlSession.close();
}
}
}
执行结果:
Blog{id=1, title=‘java基础’, content=‘详细描述’, owner=‘唐国强11’}
Blog{id=2, title=‘中文’, content=‘中国’, owner=‘ofo11’}
Blog{id=3, title=‘html’, content=‘html标签’, owner=‘样式’}
2.数组类型的参数
在上述操作1的基础上编写代码
(1).定义接口BlogMapperI.java
//方法名与select语句中的id一样 getBlogForeachById2
public List<Blog> getBlogForeachById2(int[] ids);
(2).定义映射文件BlogMapper.xml文件
collection的值为array,
<select id="getBlogForeachById2" resultType="Blog">
select * from t_blog where id IN
<foreach collection="array" index="index" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
(3).编写测试类
@Test
public void dynamicForeachTest2(){
SqlSession sqlSession=MyBatisUtil.getSqlSession(true);
BlogMapperI blogMapperI=sqlSession.getMapper(BlogMapperI.class);
int[] ids=new int[3]; //使用数组
ids[0]=1;
ids[1]=2;
ids[2]=3;
List<Blog> blogList=blogMapperI.getBlogForeachById2(ids);
for (Blog blog:blogList){
System.out.println(blog);
sqlSession.close();
}
}
执行结果:
Blog{id=1, title=‘java基础’, content=‘详细描述’, owner=‘唐国强11’}
Blog{id=2, title=‘中文’, content=‘中国’, owner=‘ofo11’}
Blog{id=3, title=‘html’, content=‘html标签’, owner=‘样式’}
3.Map类型的参数
在上述操作1的基础上编写代码
(1).定义接口BlogMapperI.java
//方法名与select语句中的id一样 getBlogForeachById3
public List<Blog> getBlogForeachById3(Map<String,Object> params);
(2).定义sql映射文件BlogMapper.xml
collection的值为map,
<select id="getBlogForeachById3" resultType="Blog">
SELECT * from t_blog where title LIKE "%"#{title}"%" and id IN
<foreach collection="map" index="index" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
(3).编写测试类
@Test
public void dynamicForeachTest3(){
SqlSession sqlSession=MyBatisUtil.getSqlSession(true);
BlogMapperI blogMapperI=sqlSession.getMapper(BlogMapperI.class);
//使用map键值对
Map<String,Object> maps=new HashMap<>();
maps.put("title","html");
//使用LIst集合添加3条数据
List<Integer> ids=new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(3);
maps.put("map",ids);
List<Blog> blogList=blogMapperI.getBlogForeachById3(maps);
for (Blog blog:blogList){
System.out.println(blog);
sqlSession.close();
}
}
执行结果;
Blog{id=3, title=‘html’, content=‘html标签’, owner=‘样式’}
4.使用foreach来添加数据
(1).定义接口BlogMapperI.java
**//使用@param注解,显式指定集合参数类的别名(列表和数组有默认的别名list和array):**
//方法名与insert语句中的id一样addBlogForeach
public int addBlogForeach(@Param(value = "params")Map<String,Object> params);
(2).定义sql映射文件BlogMapper.xml
params.keys为接口方法的参数名
//这里添加数据用insert
<insert id="addBlogForeach" parameterType="java.util.Map">
insert ignore into t_blog
<foreach collection="params.keys" item="key" open="(" close=")" separator="," >
${key}
</foreach>
values
<foreach collection="params.keys" item="key" open="(" close=")" separator=",">
#{params[${key}]}
</foreach>
</insert>
执行结果为:1
注意:
1.方法名与select(insert)语句中的id一样
2.定义sql映射文件的命名空间为接口的包名,接口名
如:
3.foreach属性collection,item必选
二 .foreach属性
1.作用:foreach的主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合
2.foreach元素的属性主要有
(1).collection : 该属性是必须指定的
但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:
a.如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
b.如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
c.如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,
如果传入参数类型为map,这个入参有注解@Param(“params”),则map的所有的key集合可以写成params.keys,所有值集合可以写成params.values。这样foreach就可以对key集合或值集合进行迭代了,设置keyName后,list,array将会失效
(2)item : 表示集合中每一个元素进行迭代时的别名(该参数为必选)
主要有2种情况,如下:
a.若collection属性为list或array,则item代表list或array里面的一个素。
b. 若collection属性对应一个map,则item代表的是map中的value集合中的单个value
(3)index : 指定一个名字,用于表示在迭代过程中,每次迭代到的位置(该参数可选)
主要有2种情况,如下:
a.在list和数组中,index是元素的序号,
b.在map中,index是元素的key,
(4)open : 表示该语句以什么开始,(该参数可选)
foreach代码的开始符号,一般是(和close=")"合用。常用在in(),values()时。
(5)close : 表示该语句以什么结束,(该参数可选)
foreach代码的关闭符号,一般是)和open="("合用。常用在in(),values()时。
(6)separator : 元素之间的分隔符(该参数可选)
例如在in()的时候,separator=","会自动在元素中间用“,“隔开,避免手动输入逗号导致sql错误,如in(1,2,)这样。
注意:
1.List对象默认用list代替作为键,
数组对象有array代替作为键,
Map对象没有默认的键
2. foreach的主要用在构建in条件中
异常:mybatis Column count doesn’t match value count at row 1
针对这个异常,有许多情况,报错主要说mybatis动态sql前后参数不匹配
举例:
报错的
<insert id="insert">
INSERT INTO
user
(class, student)
VALUES
<foreach collection="students" item="student" open="(" close=")" separator=",">
#{class,jdbcType=VARCHAR}, #{student,jdbcType=VARCHAR}
</foreach>
</insert>
这里open="(" close=")" 是给sql语句加上( )的意思
解析后语句为 insert into user (class,student) values (class,student class,student class,student…)
所以造成了动态sql前后匹配不一致
修改为:
<insert id="insert">
INSERT INTO
user
(class, student)
VALUES
<foreach collection="students" item="student" separator=",">
(#{class,jdbcType=VARCHAR}, #{student,jdbcType=VARCHAR})
</foreach>
</insert>
把open="(" close=")"去掉 在循环内直接加上( )
这时候语句就变成了insert into user (class,student) values (class,student)(class,student)(class,student)(…)
这次就符合正常sql语句了