MyBatis从入门到精通(第4章):MyBatis动态SQL

 

 (第4章):MyBatis动态SQL


MyBatis 3.5.2版本采用了功能强大的OGNL(Object-Graph Navigation Language)表达式语言,以下是MyBatis的动态SQL在XML中支持的几种标签。

  • if
  • choose(when、otherwise)
  • trim(where、set)
  • foreach
  • bind

 本章除了讲解这几种标签的用法外,还会介绍如何在一个XML中针对不同的数据库编写不同的SQL语句,另外会对使用这5种标签必须用到的OGNL表达式进行简单的介绍。

 4.1 <if>用法

<if >标签相当于java语言中的if条件语句, 通过判断是否符合预置条件来拼接sql语句。其中判断条件可以使用OGNL表达式。

4.1.1 在where条件中使用<if >

 假设有一个新的需求:实现一个用户管理高级查询功能,根据输入的条件去检索用户信息。这个功能还需要支持以下三种情况:

  • 当只输入用户名时,需要根据用户名进行模糊查询;
  • 当只输入邮箱时,根据邮箱进行完全匹配;
  • 当同时输入用户名和邮箱时,用这两个条件去查询匹配的用户。

如果仍然按照前面章节中介绍的方法去编写代码,可能会是下面的样子:

    <select id="selectByUser" resultType="cn.bjut.example.model.SysUser">

        select id,
        user_name userName,
        user_password userPassword,
        user_email userEmail,
        user_info userInfo,
        head_img headImg,
        create_time createTime
        from sys_user
        where
             user_name like concat('%', #{userName}, '%')
             and user_email = #{userEmail}

    </select>

当同时输入userName和userEmail这两个条件时,能查出正确的结果,但是当只提供userName的参数时,userEmail默认是null,这就会导致user_email=null也成为了查询条件,因而查不出正确的结果。

这时可以使用if标签来解决这个问题了,代码如下。

    <select id="selectByUser" resultType="cn.bjut.example.model.SysUser">

        select id,
        user_name userName,
        user_password userPassword,
        user_email userEmail,
        user_info userInfo,
        head_img headImg,
        create_time createTime
        from sys_user
        where 1 = 1
            <if test="userName != null and userName != ''">
                and user_name like concat('%', #{userName}, '%')
            </if>
            <if test="userEmail != '' and userEmail != null">
                and user_email = #{userEmail}
            </if>

    </select>

 if标签必填的一个test属性的值,是一个符合OGNL要求的判断表达式。表达式的结果只有0为false,所有的非0值都为true

 为了方便理解,在表达式中,建议只用true或false作为结果。

  • 判断条件 property !=null  或  property==null  适用于任何类型的字段,用于判断属性值是否为空。
  • 判断条件 property !=‘’      或  property==‘’   仅适用于String类型的字段,用于判断是否为空字符串。
  • and 和 or :当有多个判断条件时,使用and或or进行连接,嵌套的判断可以使用小括号分组,and相当于(&&),or相当于(||)

在本章所有例子中,字符串的判断几乎都包含是否为null和空‘’的判断,这两个条件不是必须写在一起。

从日志中可以看到,查询条件的不同组合最终执行的SQL和预期一样,这样就实现了动态条件查询

  • 注意SQL中 where 关键字后面的条件:  where 1 = 1

 由于两个条件都是动态的,所以如果没有1=1这个默认条件,当两个if判断都不满足时,最后生成的SQL就会以where结束,这样会报错不符合SQL规范。

加上1=1这个条件就可以避免SQL语法错误导致的异常。在4.3节中会介绍where标签的用法,可以替代这种不美观的写法。

  • 注意条件中的and(或or)
and user_name like concat('%', #{userName}, '%')

这里的and(或or)需要手动添加,当这部分条件拼接到where 1 =1 后面时仍然是合法的SQL。

4.1.2 在UPDATE更新列中使用if

现在要实现这样一个需求:只更新有变化的字段。需要注意,更新的时候不能将原来有值但没有发生变化的字段更新为空或null。

通过<if>标签可以实现这种动态列更新。

在MyBatis中一般把 选择性更新的方法名会以 Selective作为后缀。

接下来在UserMapper.xml中增加对应的SQL语句,代码如下。

    <update id="updateByIdSelective">
        
        update sys_user
        set
            <if test="userName != null and userName != ''">
                user_name = #{userName},
            </if>
            <if test="userPassword != null and userPassword != ''">
                user_password = #{userPassword},
            </if>
            <if test="userEmail != null and userEmail != ''">
                user_email = #{userEmail},
            </if>
            <if test="userInfo != null and userInfo != ''">
                user_info = #{userInfo},
            </if>
            <if test="headImg != null">
                head_img = #{headImg, jdbcType=BLOB},
            </if>
            <if test="createTime != null">
                create_time = #{createTime, jdbcType=TIMESTAMP},
            </if>
            id = #{id},
        
        where id = #{id}
    </update>

需要注意的有两点:第一点是每个if元素里面SQL语句后面的逗号;第二点就是where关键字前面的 id = #{id}这个条件。考虑以下两种情况:

  • 全部的查询条件都是null或者 空。

如果有id=#{id}这个条件,最终的SQL如下。

update sys_user set id=#{id} where id=#{id}

如果没有这个条件,最终的SQL如下。

update sys_user set    where id=#{id}

这个SQL很明显是错误的,set关键字后面没有内容。

  • 查询条件只有一个不是null也不是空(假设是userName)

如果有id=#{id}这个条件,最终的SQL如下。

update sys_user set user_name=#{userName} , id=#{id} where id=#{id}

如果没有id=#{id}这个条件,最终的SQL如下。

update sys_user set user_name=#{userName} ,   where id=#{id}

where关键字前面直接是一个逗号,这个SQL语句也是错的。

从上面两种情况来看,id=#{id}这个条件可以最大限度的保障方法不出错。除此之外,也可以通过where和set标签来解决这些问题。

4.1.3 在INSERT动态插入列中使用if标签

在数据库表中插入数据时候,如果某一列的参数值不为空,就使用传入的值,如果传入参数为空,就使用数据库中的默认值(通常是空),而不使用传入的空值。

使用if标签就可以实现这种动态插入列的功能。

 先修改sys_user表,在数据库中执行如下的SQL语句给user_email列增加默认值 [email protected]

下面直接修改SysUserMapper.xml中的insert2方法。

    <insert id="insert2" useGeneratedKeys="true" keyProperty="id">
        insert into sys_user(
        user_name, user_password,
        <if test="userEmail != null and userEmail != ''">
                user_email,    
        </if>
        user_info, head_img, create_time)
        values(
        #{userName}, #{userPassword},
        <if test="userEmail != null and userEmail != ''">
        #{userEmail},
        </if>
        #{userInfo}, #{headImg, jdbcType=BLOB}, #{createTime, jdbcType=TIMESTAMP})
    </insert>

 在INSERT中使用时要注意,若在列的部分增加if条件,则values的部分也要增加相同的if条件,必须保证上下可以互相对应,完全匹配。

 在新增的user中,我们并没有给userEmail属性赋值,这样就会使用数据库的默认值,执行测试后,输出日志如下。

======================

参考资料:

MyBatis中的OGNL教程

 

end

猜你喜欢

转载自www.cnblogs.com/MarlonKang/p/11624244.html