一步一步搞定——动态SQL

前言

大家都知道在mybatis中编写接口的时候传入参数的方式大概是四种方式
不知道的小伙伴可以点下面链接查看
多个参数传递

  • 建议大家使用传对象的方式,具体为什么?
    就是对象里面包含了用户传入参数的所有属性
 List<Books> queryById(Books books);
  • 如果使用传统方式和@Param方式虽然你可以写多个参数但是你并不知道用户传入的几个参数
  • 如果使用map方式,也不友好因为你的接口xml的#{}要与map.put的key相对应,不方便
  • 通过上面对传入参数的了解接口一方已经不存在什么问题了,那么接口对应的xml如何编写
    相信没有了解动态SQL之前,同学们应该是编写多个SQL,根据书名查写一条,根据书id查 写一条,这样如果是单条字段少还可以做到
  • 如果是一张表是十几二十个字段呢,这些字段组合起来的情况又会是多少种查询的SQL语句哇!好像很多勒,不知道同学们的概率怎么样?
什么是动态SQL?
  • 简单来说:动态sql就是程序在执行的过程中动态生成sql
  • 详细来说不管你有表中有多少字段,你传入的是什么我就执行具体的sql
  • 比如说传入的是name我就执行select * from table where name=?你传入的是id我就执行select * from table where id=?
    听起来好像不错,那就看下面的例子嘛

准备数据

create table `books` (
	`bookid` int (11),
	`bookname` varchar (30),
	`bookcount` int (20),
	`booktype` varchar (30),
	`bookprice` float 
); 
insert into `books` (`bookid`, `bookname`, `bookcount`, `booktype`, `bookprice`) values('1','红楼梦','22','古典类型','45');
insert into `books` (`bookid`, `bookname`, `bookcount`, `booktype`, `bookprice`) values('2','西游记','45','神魔类型','24');
insert into `books` (`bookid`, `bookname`, `bookcount`, `booktype`, `bookprice`) values('3','三国演义','60','历史类型','30');
insert into `books` (`bookid`, `bookname`, `bookcount`, `booktype`, `bookprice`) values('4','水浒传','10','历史类型','34');
insert into `books` (`bookid`, `bookname`, `bookcount`, `booktype`, `bookprice`) values('5','鬼吹灯','36','冒险类型','26');
动态SQL演化过程
sql标签
  • 需求:通过查询所有书的信息引出sql标签

接口:

    //查询所有书籍信息
    List<Books> queryAll();

接口xml

    <select id="queryAll" resultType="Books">
        select
         bookid, bookname, bookcount,booktype,bookprice
        from
         books
    </select>

测试:

Preparing: select bookid, bookname, bookcount,booktype,bookprice from books 
==> Parameters: 
<==    Columns: bookid, bookname, bookcount, booktype, bookprice
<==        Row: 1, 红楼梦, 22, 古典类型, 45
<==        Row: 2, 西游记, 45, 神魔类型, 24
<==        Row: 3, 三国演义, 60, 历史类型, 30
<==        Row: 4, 水浒传, 10, 历史类型, 34
<==        Row: 5, 鬼吹灯, 36, 冒险类型, 26
<==      Total: 5
  • 通过id查询书的信息

接口

 Books queryById(Books books);

接口xml

   <select id="queryById" resultType="Books">
        select
         bookid, bookname, bookcount,booktype,bookprice
        from
         books
        where
         bookid=#{bookid}
    </select>

测试结果:

==>  Preparing: select bookid, bookname, bookcount,booktype,bookprice from books where bookid=? 
==> Parameters: 2(Integer)
<==    Columns: bookid, bookname, bookcount, booktype, bookprice
<==        Row: 2, 西游记, 45, 神魔类型, 24
<==      Total: 1

通过以上两个查询我们发现下面代码是重复使用,mybatis中我们可以使用sql标签把相同的部分抽取出来,然后查询语句引用。

bookid, bookname, bookcount,booktype,bookprice

//抽取出来的相同部分 id与include标签refid保持一致
<sql id="baseColumn">
bookid, bookname, bookcount,booktype,bookprice
</sql>
//include标签就是放在查询语句中的标签  然后引用相同的部分
<include refid="baseColumn"></include>

所以 以上代码可以修改为

<sql id="baseColumn">
        bookid, bookname, bookcount,booktype,bookprice
</sql>
<select id="queryAll" resultType="Books">
        select
         <include refid="baseColumn"></include>
        from
         books
</select>
if标签 和 where标签
  • 需求:现在有个通过书名的id和书名或者书的类型查询的功能
  • 现在要求如果输入书的id按照书的id查询,如果输入书的名字就按照书名查询,如果输入书的类型就按照书类型查询,如果没有输入应该是查询全部
  • 通常我们是编写多个sql
	 //根据id查询
	 <select id="queryById" resultType="Books">
        select
        <include refid="baseColumn"></include>
        from
         books
        where
         bookid=#{bookid}
    </select>
    //根据书名查询
 	<select id="queryById" resultType="Books">
        select
        <include refid="baseColumn"></include>
        from
         books
        where
         bookname=#{bookname}
    </select>
    //根据书类型查询
    <select id="queryById" resultType="Books">
        select
        <include refid="baseColumn"></include>
        from
         books
        where
        booktype=#{booktype}
    </select>
  • 通过上面的代码已经认识到sql是不灵活的 并且代码量大
  • 首先通过传统和if标签相结合的方式去实现根据不同的输入参数,查询出不同的结果
    if标签:相当于我们java中if判断语句条件为真,就执行,如果条件都为假,就应该不执行

这里面的where 1=1就避免了如果不传入参数也执行查询所有书籍信息
这种方式存在sql注入的问题
select bookid, bookname, bookcount,booktype,bookprice from books where 1=1

接口

List<Books> queryById2(Books books);

接口xml

 <select id="queryById2" resultType="Books">
        select
        <include refid="baseColumn"></include>
        from
        books
        where 1=1
        <if test="bookid!=null">
            and bookid=#{bookid}
        </if>
        <if test="bookname!=null">
            and bookname=#{bookname}
        </if>
        <if test="bookcount!=null">
            and bookcount=#{bookcount}
        </if>
        <if test="booktype!=null">
            and booktype=#{booktype}
        </if>
        <if test="bookprice!=null">
            and bookprice=#{bookprice}
        </if>
    </select>

测试代码:

 @Test
    public void queryById2() {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        Books books = new Books();
        books.setBookprice(30f);
        List<Books> booksList = mapper.queryById2(books);
        System.out.println(JSON.toJSONString(booksList,true));
    }

测试结果:

==>  Preparing: select bookid, bookname, bookcount,booktype,bookprice from books where 1=1 and bookprice=? 
==> Parameters: 30.0(Float)
<==    Columns: bookid, bookname, bookcount, booktype, bookprice
<==        Row: 3, 三国演义, 60, 历史类型, 30
<==      Total: 1

重点:select bookid, bookname, bookcount,booktype,bookprice from books where 1=1 and bookprice=?
我们可以通过比较发现执行的sql是根据你传入的参数而定的这就是if标签的作用

  • 其次通过if+where标签的方式根据不同的输入参数,查询出不同的结果
  • where标签在下面的作用是:自动根据判断的条件选择是否生成 where 条件
  • 如果条件都为假不生成where添加语句
  • where标签还可以自动去除第一个and或者or
 <select id="queryById3" resultType="Books">
        select
        <include refid="baseColumn"></include>
        from
        books
        <where>
            <if test="bookid!=null">
                and bookid=#{bookid}
            </if>
            <if test="bookname!=null">
                and bookname=#{bookname}
            </if>
            <if test="bookcount!=null">
                and bookcount=#{bookcount}
            </if>
            <if test="booktype!=null">
                and booktype=#{booktype}
            </if>
            <if test="bookprice!=null">
                and bookprice=#{bookprice}
            </if>
        </where>
    </select>

测试结果

==>  Preparing: select bookid, bookname, bookcount,booktype,bookprice from books WHERE bookname=? 
==> Parameters: 水浒传(String)
<==    Columns: bookid, bookname, bookcount, booktype, bookprice
<==        Row: 4, 水浒传, 10, 历史类型, 34
<==      Total: 1

同样的sql还是根据传入参数属性去执行的

set标签
  • 需求:更新某个id中的书名或者其他列名的信息
//更新数据
    void update(Books books);
 //不使用if标签判断
    <update id="update">
        UPDATE
         `books`
        SET
          bookname=#{bookname},bookcount=#{bookcount},booktype=#{booktype},bookprice=#{bookprice}
        WHERE
          bookid=#{bookid}
    </update>

测试代码:

 @Test
    public void update() throws IllegalAccessException {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        Books books = new Books();
        books.setBookname("口才与训练");
        books.setBookid(1);
        mapper.update(books);
        sqlSession.commit();
    }
==>  Preparing: UPDATE `books` SET bookname=?,bookcount=?,booktype=?,bookprice=? WHERE bookid=? 
==> Parameters: 口才与训练(String), null, null, null, 1(Integer)
<==    Updates: 1

这里我们发现如果只修改书名数据库中的其他列的值被通通赋值为null
在这里插入图片描述
上面的结果显然不是我们想要看到的,我们想要的结果是:修改的书名生效其他列的值应该不变
为了防止这种把null值传入数据库其实在测试的时候判断一下即可

@Test
    public void update() throws IllegalAccessException {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        Books books = new Books();
        books.setBookname("努力一点");
        books.setBookid(1);
        if (books.getBookcount()!= null && books.getBookprice() != null && books.getBooktype() != null) {
            mapper.update(books);
            sqlSession.commit();
        } else {
            throw new IllegalAccessException("数量、价格、类型为null");
        }
    }

测试结果:应该是抛出异常 不提交给数据库

java.lang.IllegalAccessException: 数量、价格、类型为null
  • 使用if标签

接口

    void update2(Books books);

接口xml

  <update id="update2">
        UPDATE
         `books`
        SET
        <if test="bookname!=null">
            bookname=#{bookname},
        </if>
        <if test="bookcount!=null">
            bookcount=#{bookcount},
        </if>
        <if test="booktype!=null">
            booktype=#{booktype},
        </if>
        <if test="bookprice!=null">
            bookprice=#{bookprice},
        </if>
        WHERE
         bookid=#{bookid}
    </update>

测试代码

 @Test
    public void update2(){
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        Books books = new Books();
        books.setBookname("三国演义");
        books.setBookid(1);
        mapper.update2(books);
        sqlSession.commit();

    }

测试结果:

//根据书名查询 很显然sql语法不对 多了个逗号
UPDATE `books`  SET  bookname=?,    WHERE  bookid=?

问题:修改书名生效,其他列的值应该不变
使用if标签始终都有逗号存在

  • 使用set标签解决上面的两个问题

接口

 void update3(Books books);

接口xml

<update id="update3">
        UPDATE
        `books`
        <set>
            <if test="bookname!=null">
                 bookname=#{bookname},
            </if>
            <if test="bookcount!=null">
                 bookcount=#{bookcount},
            </if>
            <if test="booktype!=null">
                 booktype=#{booktype},
            </if>
            <if test="bookprice!=null">
                 bookprice=#{bookprice},
            </if>
        </set>
        WHERE
        bookid=#{bookid}
    </update>

测试代码

@Test
    public void update3(){
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        Books books = new Books();
        //这里有个问题就是如果不传入其他参数 结果应该是更新的数据为null 但是实际上SQL报错
        books.setBookname("大学英语四级考试题型");
        books.setBookid(2);
        mapper.update3(books);
        sqlSession.commit();
    }

测试结果

==>  Preparing: UPDATE `books` SET bookname=? WHERE bookid=? 
==>  Parameters: 大学英语四级考试题型(String), 2(Integer)
<==    Updates: 1

总结:
set标签可以动态生成set条件语句
set标签可以帮我们去除逗号

trim标签

上述中我们使用了where标签和set标签
但是强大的是trim标签,它可以替换if和where标签

trim标签的参数值:
prefix 添加前缀
prefixOverrides 去除前缀
suffixOverrides 添加后缀
suffix 去除后缀

代替where标签
接口

//查询所有书的信息
List<Books> selectByBooks(Books books);

接口xml

<select id="selectByBooks" resultType="Books">
        select
        <include refid="baseColumn"></include>
        from
        books
        <trim prefix="where" prefixOverrides="and|or" >
            <if test="bookid!=null">
                or bookid=#{bookid}
            </if>
            <if test="bookname!=null">
                or bookname=#{bookname}
            </if>
            <if test="bookcount!=null">
                and bookcount=#{bookcount}
            </if>
            <if test="booktype!=null">
                and booktype=#{booktype}
            </if>
            <if test="bookprice!=null">
                and bookprice=#{bookprice}
            </if>
        </trim>
    </select>

测试代码

  @Test
    public void selectByBooks() {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        //要么查询出来书名是红楼梦的信息 要么查询出来id 1 的信息,
        Books books = new Books();
        books.setBookname("红楼梦");
        books.setBookid(1);
        List<Books> booksList = mapper.selectByBooks(books);
        System.out.println(JSON.toJSONString(booksList,true));
    }

测试结果

Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@15bfd87]
==>  Preparing: select bookid, bookname, bookcount,booktype,bookprice from books where bookid=? or bookname=? 
==> Parameters: 1(Integer), 红楼梦(String)
<==    Columns: bookid, bookname, bookcount, booktype, bookprice
<==        Row: 1, 口才与训练, null, null, null
<==      Total: 1

代替set标签
接口

//更新数据
 void updateByTrim(Books books);

接口xml

<update id="updateByTrim">
        UPDATE
        `books`
        <trim prefix="set" suffixOverrides=",">

            <if test="bookname!=null">
                bookname=#{bookname},
            </if>
            <if test="bookcount!=null">
                bookcount=#{bookcount},
            </if>
            <if test="booktype!=null">
                booktype=#{booktype},
            </if>
            <if test="bookprice!=null">
                bookprice=#{bookprice},
            </if>
        </trim>
        WHERE
        bookid=#{bookid}
    </update>

测试代码

    @Test
    public void updateByTrim() {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        Books books = new Books();
        books.setBookname("快乐");
        books.setBookid(2);
        mapper.updateByTrim(books);
        sqlSession.commit();
    }

测试结果

==>  Preparing: UPDATE `books` set bookname=? WHERE bookid=? 
==> Parameters: 快乐(String), 2(Integer)
<==    Updates: 1

trim标签插入数据的运用
接口

//插入数据
  void insert2(Books books);

接口xml

  <insert id="insert2">
       INSERT
       INTO
        `books`
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="bookname!=null">
               bookname,
            </if>
            <if test="bookcount!=null">
                bookcount,
            </if>
            <if test="booktype!=null">
                booktype,
            </if>
            <if test="bookprice!=null">
                bookprice,
            </if>
        </trim>
       VALUES
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="bookname!=null">
                #{bookname},
            </if>
            <if test="bookcount!=null">
               #{bookcount},
            </if>
            <if test="booktype!=null">
               #{booktype},
            </if>
            <if test="bookprice!=null">
               #{bookprice},
            </if>
        </trim>
    </insert>

测试代码

 @Test
    public void insert2() {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        Books books = new Books();
        books.setBookname("别怕C语言");
        books.setBookcount(20);
        books.setBookprice(36f);
        books.setBooktype("教材");
        mapper.insert2(books);
        sqlSession.commit();
    }

测试结果

==>  Preparing: INSERT INTO `books` ( bookname, bookcount, booktype, bookprice ) VALUES ( ?, ?, ?, ? ) 
==> Parameters: 别怕C语言(String), 20(Integer), 教材(String), 36.0(Float)
<==    Updates: 1

注意:insert和update情况不一样
insert理所当然插入什么就有什么其他值默认为null(前提是数据库没有not null约束)
update更新数据 你更新什么就改变什么

choose标签
   //choose标签相当于switch case 语句 如果传入的是某一个参数就执行
   //如果都没有传入就执行默认的
    <choose>
    <when test=""></when>
    <when test=""></when>
     ......
    <otherwise></otherwise>
    </choose>

接口

    //动态SQL 使用choose
    List<Books> byChoose(Books books);

接口xml

<select id="byChoose" resultType="Books">
        select
        <include refid="baseColumn"></include>
        from
        books
        <trim prefix="where" prefixOverrides="and">
            <choose>
                <when test="bookid!=null">
                    and bookid=#{bookid}
                </when>
                <when test="bookname!=null">
                    and bookname=#{bookname}
                </when>
                <when test="booktype!=null">
                    and booktype=#{booktype}
                </when>
                <when test="bookid!=null">
                    and bookid=#{bookid}
                </when>
                <when test="bookprice!=null">
                    and bookprice=#{bookprice}
                </when>
                <otherwise>
                    and bookcount=#{bookcount}
                </otherwise>
            </choose>
        </trim>
    </select>

测试代码

@Test
    public void byChoose() {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        Books books = new Books();
        books.setBookname("大学英语");
        List<Books> booksList = mapper.byChoose(books);
        System.out.println(JSON.toJSONString(booksList,true));
    }

测试结果

Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@15bfd87]
==>  Preparing: select bookid, bookname, bookcount,booktype,bookprice from books where bookname=? 
==> Parameters: 大学英语(String)
<==    Columns: bookid, bookname, bookcount, booktype, bookprice
<==        Row: 2, 大学英语, 45, 神魔类型, 24
<==      Total: 1

以上就是动态sql中的标签,如果那块有问题欢迎及时交流。

foreach标签
  • 需求:查询书的id分别为1,9,10的信息
//通过以下两种方式可以实现
SELECT * FROM books WHERE bookid=1 OR bookid=9 OR bookid=10;
SELECT * FROM books WHERE  bookid IN(1,9,10);

那么在mybatis中如何实现SELECT * FROM books WHERE bookid IN(1,9,10)
这就需要用到foreach标签了,foreach标签见名知意就是循环的意思,

 foreach的属性取值有三种:
        1.array数组
        2.list集合
        3.map集合
  • 首先了解array数组
    接口
//根据id查询多本书的信息
 List<Books> selectByForeach(Integer[] bookid);

接口xml

<!--  
		 通过trim标签与foreach标签方式实现
		 item 的属性值是循环的每一项值 必须和#{}保持一致 名字任意但是为了见名知意我们这里写bookid
		 使用trim标签时候我们要去除后缀的最后一个逗号
 -->
 <select id="selectByForeach" resultType="Books">
       SELECT
        *
       FROM
        books
       WHERE
        bookid IN(
       <trim suffixOverrides=",">
           <foreach collection="array" item="bookid">
               #{bookid},
           </foreach>
       </trim>
        )
   </select>

测试代码

  @Test
    public void selectForeach() {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        Integer[] bookid={2,3,10};
        List<Books> books = mapper.selectByForeach(bookid);
        System.out.println(JSON.toJSONString(books,true));
    }

测试结果

==>  Preparing: SELECT * FROM books WHERE bookid IN( ?, ?, ? ) 
==> Parameters: 2(Integer), 3(Integer), 10(Integer)
<==    Columns: bookid, bookname, bookcount, booktype, bookprice
<==        Row: 2, 高数, 45, 神魔类型, 24
<==        Row: 3, 三国演义, 60, 历史类型, 30
<==        Row: 10, 别怕C语言, 20, 教材, 36
<==      Total: 3

通过trim标签和foreach标签实现了根据bookid查询多个书的信息
注意:使用trim标签我们需要注意必须使用后缀去除最后一个逗号
对于foreach标签它里面的属性可以完全规避逗号的问题

下面只使用foreach标签
接口

	//根据id查询多本书的信息
    List<Books> selectByForeach2(Integer[] bookid);

接口xml

<!--
		separator:分隔符可以实现两个值之间自动生成逗号 避免最后一位多逗号的问题
-->
<select id="selectByForeach2" resultType="Books">
        SELECT
        *
        FROM
        books
        WHERE
        bookid IN(
            <foreach collection="array" item="bookid" separator=",">
                #{bookid}
            </foreach>
        )
    </select>

测试代码

    @Test
    public void selectForeach2() {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        Integer[] bookid = {4, 5, 9};
        List<Books> books = mapper.selectByForeach2(bookid);
        System.out.println(JSON.toJSONString(books, true));
    }

测试结果

==>  Preparing: SELECT * FROM books WHERE bookid IN( ? , ? , ? ) 
==> Parameters: 4(Integer), 5(Integer), 9(Integer)
<==    Columns: bookid, bookname, bookcount, booktype, bookprice
<==        Row: 4, 水浒传, 10, 历史类型, 34
<==        Row: 5, 鬼吹灯, 36, 冒险类型, 26
<==        Row: 9, 别怕C语言, 20, 教材, 36
<==      Total: 3

通过foreach标签的属性值还可以实现in后面的小括号
接口

	//根据id查询多本书的信息
    List<Books> selectByForeach3(Integer[] bookid);

接口xml

<!--
    open:  以什么开头
    close: 以什么结尾
-->
 <select id="selectByForeach3" resultType="Books">
        SELECT
        *
        FROM
        books
        WHERE
        bookid IN
        <foreach collection="array" item="bookid" separator="," open="(" close=")">
            #{bookid}
        </foreach>
    </select>

测试代码

 @Test
    public void selectForeach3() {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        Integer[] bookid = {5, 9};
        List<Books> books = mapper.selectByForeach3(bookid);
        System.out.println(JSON.toJSONString(books, true));
    }

测试结果

==>  Preparing: SELECT * FROM books WHERE bookid IN ( ? , ? ) 
==> Parameters: 5(Integer), 9(Integer)
<==    Columns: bookid, bookname, bookcount, booktype, bookprice
<==        Row: 5, 鬼吹灯, 36, 冒险类型, 26
<==        Row: 9, 别怕C语言, 20, 教材, 36
<==      Total: 2
  • 其次了解list集合
    对于collection的属性值是list集合
    泛型有几种情况:
    list集合的泛型是Integer、String
    list集合的泛型是对象
    接口
//根据id查询多本书的信息
 List<Books> selectByForeach4(List<Integer> list);

接口xml

<!--
	collection的属性值是list item的意思还是循环中的每一项值
-->
<select id="selectByForeach4" resultType="Books">
        SELECT
        *
        FROM
        books
        WHERE
        bookid IN
        <foreach collection="list" item="bookid" separator="," open="(" close=")">
            #{bookid}
        </foreach>
    </select>

测试代码

  @Test
    public void selectForeach4() {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        List<Integer> list = new ArrayList<>();
        list.add(2);
        list.add(10);
        List<Books> books = mapper.selectByForeach4(list);
        System.out.println(JSON.toJSONString(books, true));
    }

测试结果

==>  Preparing: SELECT * FROM books WHERE bookid IN ( ? , ? ) 
==> Parameters: 2(Integer), 10(Integer)
<==    Columns: bookid, bookname, bookcount, booktype, bookprice
<==        Row: 2, 高数, 45, 神魔类型, 24
<==        Row: 10, 别怕C语言, 20, 教材, 36
<==      Total: 2

注意:对于collection参数值是list集合 加不加泛型 integer或者string都没有问题

接口

 List<Books> selectByForeach5(List<Books> list);

接口xml

  <!--
        如果参数类型是集合且泛型是对象 #{books.bookid}里面的值必须是对象.属性值
        其中item是集合中的对象
    -->
    <select id="selectByForeach5" resultType="Books">
        SELECT
        *
        FROM
        books
        WHERE
        bookid IN
        <foreach collection="list" item="books" separator="," open="(" close=")">
            #{books.bookid}
        </foreach>
    </select>

测试代码

 @Test
    public void selectForeach5() {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        List<Books> list = new ArrayList<>();
        //这里添加多个对象的时候使用代码块相当于每次创建了不同的对象
        {
            Books b = new Books();
            b.setBookid(3);
            list.add(b);
        }
        {
            Books b = new Books();
            b.setBookid(10);
            list.add(b);
        }
        List<Books> books = mapper.selectByForeach5(list);
        System.out.println(JSON.toJSONString(books, true));
    }

测试结果

==>  Preparing: SELECT * FROM books WHERE bookid IN ( ? , ? ) 
==> Parameters: 3(Integer), 10(Integer)
<==    Columns: bookid, bookname, bookcount, booktype, bookprice
<==        Row: 3, 三国演义, 60, 历史类型, 30
<==        Row: 10, 别怕C语言, 20, 教材, 36
<==      Total: 2
  • 最后了解map集合

特别注意:map必须配合@Param使用否则会报以下错误
The expression 'map' evaluated to a null value.

接口

    List<Books>  selectByForeach7(@Param("map") Map<String, Object> map);

接口xml

<!--
        collection参数是map
        item对应的是map中的value
        item的值与#{}里面的值保持一致
    -->
    <select id="selectByForeach7" resultType="Books">
        SELECT
        *
        FROM
        books
        WHERE
        bookid IN
        <foreach collection="map" item="bookid" separator="," open="(" close=")">
            #{bookid}
        </foreach>
    </select>

测试代码

   @Test
    public void selectForeach7() {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        Map<String,Object> map = new HashMap<>();
        //map的key任意
        map.put("m1", 2);
        map.put("m2",10);

        List<Books> books = mapper.selectByForeach7(map);
        System.out.println(JSON.toJSONString(books, true));
    }

测试结果

==>  Preparing: SELECT * FROM books WHERE bookid IN ( ? , ? ) 
==> Parameters: 2(Integer), 10(Integer)
<==    Columns: bookid, bookname, bookcount, booktype, bookprice
<==        Row: 2, 高数, 45, 神魔类型, 24
<==        Row: 10, 别怕C语言, 20, 教材, 36
<==      Total: 2

foreach标签里面还有个index的属性通过以下实例测试
接口

    //测试collection参数index
    List<Books> selectByForeach8(@Param("map") Map<String, Object> map);

接口xml

  <select id="selectByForeach8" resultType="Books">
        SELECT
        *
        FROM
        books
        WHERE
        bookid IN
        <foreach collection="map" item="bookid" index="key" separator="," open="(" close=")">
            #{bookid},#{key}
        </foreach>
    </select>

测试代码

   @Test
    public void selectForeach8() {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        Map<String, Object> map = new HashMap<>();
        //map的key任意
        map.put("m1", 3);
        map.put("m2", 4);
        map.put("m3", 5);
        List<Books> books = mapper.selectByForeach8(map);
        System.out.println(JSON.toJSONString(books, true));
    }

测试结果

==>  Preparing: SELECT * FROM books WHERE bookid IN ( ?,? , ?,? , ?,? ) 
==> Parameters: 3(Integer), m1(String), 4(Integer), m2(String), 5(Integer), m3(String)
<==    Columns: bookid, bookname, bookcount, booktype, bookprice
<==        Row: 3, 三国演义, 60, 历史类型, 30
<==        Row: 4, 水浒传, 10, 历史类型, 34
<==        Row: 5, 鬼吹灯, 36, 冒险类型, 26
<==      Total: 3

SELECT * FROM books WHERE bookid IN ( ?,? , ?,? , ?,? )
Parameters: 3(Integer), m1(String), 4(Integer), m2(String), 5(Integer), m3(String)

结论:通过参数和sql语句来看index属性的值和map.put()方式中的key是保持一致的

特殊情况

在前言中我讲到传入参数建议使用对象,下面说一说如果传入的是Sting字符串呢
会有什么问题?

  • 出现的错误演示
    错误:
    There is no getter for property named 'bookname' in 'class java.lang.String'
    接口
 List<Books> solveString(String bookname);

接口xml


    <!-- if标签在对String 引用类型进行判断是回去找String类里面的bookname的get方法
         找不到就报错
         There is no getter for property named 'bookname' in 'class java.lang.String'
     -->
    <select id="solveString" resultType="Books">
        select
        <include refid="baseColumn"></include>
        from
        books
        <where>
            <if test="bookname!=null and bookname!=''">
                and bookname=#{bookname}
            </if>
        </where>
    </select>

测试代码


    @Test
    public void solveString() {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        List<Books> books = mapper.solveString("");
        System.out.println(JSON.toJSONString(books, true));
    }

测试结果

There is no getter for property named 'bookname' in 'class java.lang.String'

对于传入参数是String字符串,接口的xml使用了if标签就会上面的错误。
意思是在String类中找不到有关的bookname属性的getter方法
其实报这个错误是因为不符合OGNL表达式语言,具体是为什么不符合,还不清楚只知道解决方法,有兴趣可以百度一下查询

  • 解决如上问题:

接口

 //使用value值
    List<Books> solveString2(String bookname);

接口xml

<!--
	这里在if标签中对参数值作了非空和不等于null判断
	使用value值去替换 可以看做在使用${}对于传入参数是Integer类型 所表达的意思一样
	
-->
<select id="solveString2" resultType="Books">
        select
        <include refid="baseColumn"></include>
        from
        books
        <where>
            <if test="value!=null and value!=''">
                and bookname=#{bookname}
            </if>
        </where>
    </select>

测试代码

 @Test
    public void solveString2() {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        //使用value值作为if判断条件 对于非空判断不起作用  传入"" 还是会执行条件语句
        List<Books> books = mapper.solveString2("");
        System.out.println(JSON.toJSONString(books, true));
    }

测试结果

==>  Preparing: select bookid, bookname, bookcount,booktype,bookprice from books WHERE bookname=? 
==> Parameters: (String)
<==      Total: 0
[]

在接口xml中if标签作了判断但是测试的时候传入空值执行了where后面的条件语句
这个问题还是不符合OGNL表达式

  • 解决方式二:
    使用_parameter替换
    接口
  //使用_parameter
    List<Books> solveString3(String bookname);

接口xml

 <!--使用_parameter 参数进行if判断 不会出现非空不起作用 -->
    <select id="solveString3" resultType="Books">
        select
        <include refid="baseColumn"></include>
        from
        books
        <where>
            <if test="_parameter!=null and _parameter!=''">
                and bookname=#{bookname}
            </if>
        </where>
    </select>

测试代码

    @Test
    public void solveString3() {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        List<Books> books = mapper.solveString3("");
        System.out.println(JSON.toJSONString(books, true));
    }

测试结果

==>  Preparing: select bookid, bookname, bookcount,booktype,bookprice from books 
==> Parameters: 
<==    Columns: bookid, bookname, bookcount, booktype, bookprice
<==        Row: 1, 努力一点, null, null, null
<==        Row: 2, 高数, 45, 神魔类型, 24
<==        Row: 3, 三国演义, 60, 历史类型, 30
<==        Row: 4, 水浒传, 10, 历史类型, 34
<==        Row: 5, 鬼吹灯, 36, 冒险类型, 26
<==        Row: 9, 别怕C语言, 20, 教材, 36
<==        Row: 10, 别怕C语言, 20, 教材, 36
<==      Total: 7
  • 解决方式三:
  • 使用注解@Param
    接口
  //使用@Param("bookname")
    List<Books> solveString4(@Param("bookname") String bookname);

接口xml

<!--这里test的参数还是写bookname-->
 <select id="solveString4" resultType="Books">
        select
        <include refid="baseColumn"></include>
        from
        books
        <where>
            <if test="bookname!=null and bookname!=''">
                and bookname=#{bookname}
            </if>
        </where>
    </select>

测试代码

  @Test
    public void solveString4() {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        List<Books> books = mapper.solveString4("水浒传");
        System.out.println(JSON.toJSONString(books, true));
    }

测试结果

==>  Preparing: select bookid, bookname, bookcount,booktype,bookprice from books WHERE bookname=? 
==> Parameters: 水浒传(String)
<==    Columns: bookid, bookname, bookcount, booktype, bookprice
<==        Row: 4, 水浒传, 10, 历史类型, 34
<==      Total: 1
  • 解决方式四:
  • 使用map方式

接口

//使用map方式
    List<Books> solveString5(Map map);

接口xml


    <!--使用map方式 map的方法put的key 要对应test属性的值-->
    <select id="solveString5" resultType="Books">
        select
        <include refid="baseColumn"></include>
        from
        books
        <where>
            <if test="bookname!=null and bookname!=''">
                and bookname=#{bookname}
            </if>
        </where>
    </select>

测试代码

 @Test
    public void solveString5() {
        SqlSession sqlSession = MyBatisUtils.getSqlSessionFactory().openSession();
        BooksMapper mapper = sqlSession.getMapper(BooksMapper.class);
        Map map = new HashMap();
        map.put("bookname","");
        List<Books> books = mapper.solveString5(map);
        System.out.println(JSON.toJSONString(books, true));
    }

测试结果

==>  Preparing: select bookid, bookname, bookcount,booktype,bookprice from books 
==> Parameters: 
<==    Columns: bookid, bookname, bookcount, booktype, bookprice
<==        Row: 1, 努力一点, null, null, null
<==        Row: 2, 高数, 45, 神魔类型, 24
<==        Row: 3, 三国演义, 60, 历史类型, 30
<==        Row: 4, 水浒传, 10, 历史类型, 34
<==        Row: 5, 鬼吹灯, 36, 冒险类型, 26
<==        Row: 9, 别怕C语言, 20, 教材, 36
<==        Row: 10, 别怕C语言, 20, 教材, 36
<==      Total: 7

总结:对于传入参数是String字符串使用@Param、map、_parameter都可以解决
不管传入参数是哪种引用类型建议使用对象,不存在以上的问题

至此动态sql的所有标签就分享到这!

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值