MyBatis 从入门到放弃系列四【 动态SQL】

很长一段时间没更新博客了,真的是差点儿就放弃了(都怪自己太懒散了)!!!

继续我们的 MyBatis 之旅,上一篇讲到MyBatis的SQL映射,今天学习一下动态SQL,看看如何用MyBatis写出灵活的SQL。

参考文档MyBatis官方文档

if

大家在开发中是否碰到过这种情况:查询一个表时,页面中有多个查询条件,但是用户并不是必须选择查询条件的,每次选择查询条件的也不一定相同。比如,有一个User表的查询,页面中的查询条件为 用户名(username)、真是姓名(realname)、昵称(nickname),这个时候应该怎么去写SQL呢?总不能在 Java 代码中一行行地写 if (username != null)if (realname != null) 。。。这些判断吧,这样看着是不是有点儿蠢???

这时候 if 就有了用武之地,在我们的mapper.xml文件中可以这么写:

<?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="org.gaipianer.mybatis.demo.UserMapper">
    <select id="findUser" resultType="User">
        SELECT * FROM user 
        WHERE 1 = 1
        <if test="username != null">
            AND username like #{username}
        </if>
        <if test="realname != null">
            AND realname like #{realname}
        </if>
        <if test="nickname != null">
            AND nickname like #{nickname}
        </if>
    </select>
</mapper>

这时不管用户选择什么样的查询条件,都被我们轻松地搞定了,MyBatis在创建SQL语句时就会帮我们执行 <if> 标签里的判断逻辑,如果username不为空,AND username like #{username}就会假如到我们的SQL语句中,如果realname为空,realname的查询条件就不会出现在SQL语句中。

大家可能会对上面的 SQL中 WHERE 1 = 1 这行比较疑惑,为什么要这么写呢?那是因为在我们举例的场景中,用户是可以不选择查询条件的,而我们的 <if> 条件语句中,我们并不确定用户到底选择了哪些查询条件,如果不在需要判断条件的SQL语句中加上 AND ,那么我们的SQL拼接出来就会是这样的:

SELECT * FROM user WHERE username like 'zhang' realname like ‘张三’

这明显是错误的,为了保证我们的拼接条件在有AND 前缀时也能保证正确性,所以在WHERE后面加了一个永远都成立的条件 WHERE 1 = 1,Mybatis提供了一个 <where>标签,可以帮助我们更优雅地解决这个问题,我们在文章的下面会讲到。

choose、when、otherwise

将上面的业务场景改为如果用户传入了 username、realname等条件,就按照传入的条件进行查询,如果用户什么都没有传,我们就添加另外一个查询条件,查询没有被逻辑删除的用户,即:delete = 0。

显然,这个时候 <if> 标签已经不能满足我们需求了,这时候轮到我们的 choose、when、otherwise闪亮登场了:

<select id="findUser">
    SELECT * FROM user WHERE 1 = 1
  <choose>
    <when test="username != null">
      AND username like #{username}
    </when>
    <when test="realname != null and nickname != null">
      AND realname like #{realname} AND nickname like #{nickname}
    </when>
    <otherwise>
      AND delete = 0
    </otherwise>
  </choose>
</select>

从执行逻辑来看,上面的代码是不是很像我们的 if () else () 语句。因为MyBatis中并没有提供 if else的写反,所以个人感觉choose、when、otherwise更像是 if else 的替代实现方式。

where、trim、set

在 if 的例子中,为了能够让SQL正确地拼接,我们有一个 WHERE 1=1 <if>...</if>的写法,这种方式虽然能够解决问题,但是根本没有凸显出我们 MyBatis 优雅的气质。而 where 标签能够帮助我们很好地解决这个问题:

<?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="org.gaipianer.mybatis.demo.UserMapper">
    <select id="findUser" resultType="User">
        SELECT * FROM user 
        <where>
            <if test="username != null">
                username like #{username}
            </if>
            <if test="realname != null">
                AND realname like #{realname}
            </if>
            <if test="nickname != null">
                AND nickname like #{nickname}
            </if>
        </where>
    </select>
</mapper>

加上 <where> 标签后,只有在有条件成立时,才会将 WHERE 拼接到我们的 SQL 语句中,假如上述例子中仅有 <if test="realname != null">AND realname like #{realname}</if> 这一个条件成立,MyBatis 也会帮助我们去掉 AND 前缀,保证我们查询语句的正确性。<where> 标签能够去除的前缀有 AND 和 OR 两个关键字。

where 标签实现的功能,也可以使用 trim 标签实现,<where>...</where>等价于以下的代码:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

注意:上述代码 prefixOverrides 属性中的空格是必填的!

MyBatis 会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。

同样的,MyBatis也为我们提供了 set 标签,更好地实现动态 update 操作,代码如下:

<?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="org.gaipianer.mybatis.demo.UserMapper">
    <update id="updateUser">
        UPDATE user 
        <set>
            <if test="realname != null">
               realname = #{realname},
            </if>
            <if test="nickname != null">
               nickname = #{nickname}
            </if>
        </set>
        WHERE username = #{username}
    </update>
</mapper>

和 where 标签一样,set 标签也会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。

set 标签实现的功能,同样也可以使用 trim 标签实现,<set>...</set>等价于以下的代码:

<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

在 where 标签的例子中,我们使用的是 prefixOverrides 覆盖多余的前缀,在 set 的例子中,使用的是 suffixOverrides 覆盖多月的后缀。

foreach

有的时候我们会遇到这样一种业务场景,查询 username 在 zhangsan、luolaoshi、lisi、wangmou 中的用户信息。自然而然地,我们想到了 SQL 中的 IN 关键字,foreach 标签能够让我们更加方便的拼接带有 IN 操作的 SQL 语句,代码如下:

<select id="selectUsernameIn" parameterType="java.util.List" resultType="User">
        SELECT * FROM user
        WHERE username IN
        <foreach collection="list" item="item" index="index" open="(" separator="," close=")">
            #{item}
        </foreach>
</select>

foreach 标签中的 open 属性表示动态 SQL 拼接的起始字符, separator 属性表示在循环中每个元素之间的分隔符,在本例中我们的 IN 使用 , 分隔,close 属性表示拼接结束的字符。一顿操作之后,MyBatis会帮我们拼接出如下 SQL:

SELECT * FROM user
WHERE username IN (zhangsan,luolaoshi,lisi,wangmou)

foreach 不仅能够遍历 List 对象,他还能够遍历任何可迭代对象,比如 数组、Set等,foreach 甚至能够遍历 Map 类型的对象!!!当遍历对象为数组、List、Set 对象时,item 属性为集合中的元素,index 为元素在集合中的索引位置,当遍历对象为 Map 对象时,item 为 Map 中的 value,index 为 key 值。

script

以上的例子,我们都是在 Mapper.xml 中完成的。在之前的文章中介绍过,MyBatis 同样支持在Mapper.java 文件中以注解的方式拼接 SQL 语句,但是注解方式不能直接在 SQL 中使用 if set foreach where 等动态 SQL 标签,而是需要我们在 SQL 中使用 script 标签之后才可以:

@Select("<script>" +
            "SELECT * FROM user " +
            "<where>" +
            "<if test='username != null'>username = #{username},</if>" +
            "<if test='nickname != null'>nickname = #{nickname},</if>" +
            "<if test='realname != null'>realname = #{realname}</if>" +
            "</where>" +
            "</script>")
List<User> listUser(User user);

今天的【从入门的放弃系列】就先到这里,以我最近的状态,下次更新说不定又是什么时候了。。。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值