MyBatis————动态sql——foreach

本文介绍了MyBatis中动态SQL的foreach标签的使用,包括List、数组和Map类型的参数,以及foreach属性的详细解释,如collection、item、index、open、close和separator,并给出了常见错误及解决方案。
摘要由CSDN通过智能技术生成

一.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语句了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值