MyBatis(八)------MyBatis动态SQL

本节内容

  • MyBatis动态SQL的基本使用
  • MyBatis动态SQL的基本元素:if、set、where、bind、foreach等元素
  • MyBatis的动态SQL的条件判断方法

前言

如果使用JDBC或者类似于Hibernate的其他框架,很多时候要根据需要去拼装SQL,这是一个麻烦的事情。因为某些查询需要许多条件,比如查询角色,可以根据角色名称或者备注等信息查询,当不输入名称时使用名称作条件就不合适了。通常使用其他框架需要大量的Java代码进行判断,可读性比较差,而MyBatis提供对SQL语句动态的组装能力,使用XML的几个简单的元素,便能完成动态SQL的功能。大量的判断都可以在MyBatis的映射XML里面配置,以达到许多需要大量代码才能实现的功能,大大减少了代码量,这体了MyBatis的灵活、高度可配置性和可维护性。


1、概述

元素 作用 备注
if 判断语句 单条件分支判断
choose(when、otherwise) 相当于java中的switch和case语句 多条件分支判断
tirm(where、set) 辅助元素,用于处理特定的SQL拼接问题,比如去掉多余的and、or等 用于处理SQL拼接问题
foreach 循环语句 在in语句中列举条件常用

动态SQL实际使用的元素并不多,它们带来了灵活性,减少了更多的工作量的同时,也在一定程度上提高了程序的可读性和可维护性。


2、if元素

if元素是常用的判断语句,相当于Java中的if语句,常与test属性联合使用。

举个栗子:根据用户名name或者用户ID查询一个角色信息,但是用户名是一个选填条件,不填写的时候,就不用它作为条件进行查询。

<select id="selectById" parameterType="String">
    SELECT * FROM sys_user A
    WHERE
    <if test="id!= null and id!= ''">
         A.ID= #{id}
    </if>
    <if test="userName != null and userName != ''">
       AND A.USER_NAME like concat('%', #{userName}, '%')
    </if>
</select>

 当入参userName传递进mapper映射器时,如果参数不为空,则采取构造对userName的模糊查询,否则就不去构造这个条件。通常MyBatis的if元素省去了许多拼接SQL的工作,集中在XML中维护。


3、choose、when、otherwise元素

如果说if元素相当于if语句,即不是这个就是那个,那么有时候还需要另一种选择,甚至更多选择,也就是需要类似switch...case...default...功能的语句。

在映射器的动态语句中choose、when、otherwise这3个元素承担了这个功能。

举个栗子:

  • 如果用户名不为空,则只用用户编号ID作为条件查询;
  • 如果用户名为空,而昵称不为空,则用昵称作为条件进行模糊查询;
  • 如果用户名和昵称都为空,则要求用户联系电话不能为空。

使用choose、when、otherwise元素:

<select id="selectUsers" parameterType="sysUser" resultMap="baseResultMap">
    SELECT USER_NAME,NICK_NAME,USER_TEL
    FROM sys_user A
    WHERE 1=1
    <choose>
        <when test="userName != null and userName != ''">
            AND A.USER_NAME = #{userName}
        </when>
        <when test="nickName != null and nickName != ''">
            AND A.NICK_NAME like concat('%', #{nickName}, '%')
        </when>
        <otherwise>
            AND A.USER_TEL IS NOT NULL
        </otherwise>
    </choose>
</select>

4、tirm、where、set元素

这里,我们会发现上边的SQL语句中有一个条件where 1=1,如果没有这个条件,则SQL语句将变为:

SELECT
    USER_NAME,NICK_NAME,USER_TEL
FROM
    sys_user A
WHERE 
    AND A.USER_NAME = #{userName}
    AND A.NICK_NAME like concat('%', #{nickName}, '%')
    AND A.USER_TEL IS NOT NULL

显然,上边的SQL将抛语法异常。 where也可以通过<where>元素加入。

扫描二维码关注公众号,回复: 10134913 查看本文章

而有的时候要去掉的是一些特殊的SQL语法,比如常见的and、or,而使用的tirm元素可以达到预期。

<select id="selectById" parameterType="String">
    SELECT ID, USER_NAME, NICK_NAME FROM sys_user A
    <tirm prefix="where" prefixOverride="and">
        <if test="id!= null and id!= ''">
             A.ID= #{id}
        </if>
        <if test="userName != null and userName != ''">
           AND A.USER_NAME like concat('%', #{userName}, '%')
        </if>
    </tirm>
</select>

分析:tirm元素表示要去掉一些特殊的字符串,当时prefix表示语句的前缀,而prefixOverride表示要去掉哪种字符串。当时prefix代表的是语句的前缀,而prefixOverrides代表的是需要去掉哪种字符串。上面的写法基本与where是等效的。

在MyBatis中,set元素使用:

<update id="updateRole" parameterType="role">
    UPDATE sys_role
    <set>
        <if test="roleName != null and roleName !=''">
            ROLE_NAME = #{roleName},
        </if>
        <if test="note != null and note !=''">
            NOTE = #{note}
        </if>
    </set>
    WHERE ROLE_NO = #{roleNo}
</update>

set元素遇到逗号,它会把对应的逗号去掉,如果我们自己编写那将是多少次的判断?当我们只想更新备注时,只需要传递备注信息和角色编号即可,而不需要再传递角色名称。MyBatis会根据参数的规则进行动态SQL组装,便能满足需求,从而避免全部字段更新的问题。


5、foreach元素

foreach元素是一个循环语句,它的作用是遍历集合,能够很好地支持数组、List、Set接口的集合,对此提供遍历功能。它往往用于SQL中的in关键字。

比如批量删除一张表中的记录:

<!--[7]批量删除数据-->
<delete id="deleteByIdList" parameterType="String">
    delete from tb_goods where ID IN
    <foreach collection="list" item="id" index="index" open="(" separator="," close=")" >
        #{id}
    </foreach>
</delete>

分析:

  • collection:配置的list是传进来的参数名称(以list集合作为入参传递),它可以是一个数组、List、Set等集合;
  • item:配置的是循环中当前的元素;
  • index:配置的是当前元素在集合的位置下标;
  • open和close配置的是以什么符号将这些集合元素包装起来;
  • separator:表示入参的元素的间隔符。

在SQL中常常用到in语句,但是对于大量数据的in语句要特别注意,因为它会消耗大量的性能。此外,还有一些数据库的SQL对执行的SQL长度有限制,所以使用它时要预估一下collection对象的长度,避免出现类似问题。


6、用test的属性判断字符串

test元素相当于条件判断语句,用于判断真假。大部分场景下,它都是用以判断空和非空的。有时需要判断字符串、数字和枚举等。

举个栗子:

    <update id="updateInfo" parameterType="com.hl.study.model.SysUser">
        update sys_user A
        <set>
            <if test="userName != null and userName != ''">
                A.USER_NAME = #{userName},
            </if>
            <if test="nickName != null and nickName != ''">
                A.NICK_NAME = #{nickName},
            </if>
            <if test="headImgUrl != null and headImgUrl != ''">
                A.HEAD_IMG_URL = #{headImgUrl},
            </if>
            <if test="password != null and password != ''">
                A.PASSWORD = #{password},
            </if>
            <if test="phone != null and phone != ''">
                A.PHONE = #{phone},
            </if>
            <if test="telephone != null and telephone != ''">
                A.TELPHONE = #{telephone},
            </if>
            <if test="email != null and email != ''">
                A.EMAIL = #{email},
            </if>
            <if test="birthday != null and birthday != ''">
                A.BIRTHDAY = #{birthday},
            </if>
            <if test="sex != null and sex != ''">
                A.SEX = #{sex},
            </if>
            <if test="status != null and status != ''">
                A.STATUS = #{status},
            </if>
        </set>
        where A.ID = #{id}
    </update>

如上所述,我们可以很简单的对字段是否为空进行判断,当然,对于字符串的判断,可以通过toString()的方法进行比较。它可以判断数值型的参数。对于枚举类而言,取决于使用何种typeHandler。


7、bind元素

bing元素的作用是通过OGNL表达式去自定义一个上下文变量,这样更方便使用。在进行模糊查询时,如果是MySQL数据库,常用到的是一个concat,它用%和参数相连。然而在Oracle数据库则没有,Oracle数据库用连接符号||,这样SQL就需要提供两种形式去实现。有了bind元素,就不必使用数据库语言,而是使用MyBatis的动态SQL即可完成。

比如,按照角色名称进行模糊查询,可以使用bind元素把映射文件写成如下:

<select id = "selectByame" parameterType="String" resultTpe="sysRole">
    <bind name="pattern" value="'%' + roleName + '%'" />
        SELECT ID, ROLE_NAME AS roleName, NICK_NAME AS nickName FROM sys_role A
        WHERE A.ROLE_NAME like #{roleName}
</select>

这里bind元素中入参roleName和通配符(%)连接后赋给了pattern,然后就可以在select语句中使用这个变量进行模糊查询了。无论是MySQL还是Oracle都可以使用这样的语句,提高了代码的可移植性。

MyBatis支持多个参数使用bind元素的用法,所以传递多个参数也没有问题。

举个栗子:

/**
* 根据编号和用户名查询
* @param id 用户编号
* @param name 用户名
* @return
*/
T selectByIdAndName(@Param("id") String id, @Param("userName") String name);
    

这个时候只需在映射文件中传递两个变量作为查询条件即可:

<select id = "selectByIdAndNme" resultTpe="sysRole">
    <bind name="pattern_id" value="'%' + id+ '%'" />
    <bind name="pattern_roleName" value="'%' + roleName + '%'" />
        SELECT ID, ROLE_NAME AS roleName, NICK_NAME AS nickName 
        FROM sys_role A
        WHERE A.ID like #{id}
        AND A.ROLE_NAME like #{roleName}
</select>

这里绑定了两个新的变量pattern_id和pattern_roleName,就可以在SQL的其他地方使用了。


愿你就像早晨八九点钟的太阳,活力十足,永远年轻。

发布了93 篇原创文章 · 获赞 136 · 访问量 12万+

猜你喜欢

转载自blog.csdn.net/qq_27706119/article/details/102789256