【MyBatis】MyBatis动态SQL

MyBatis动态SQL

前言

在前面,我们已经学习了MyBatis的单表操作以及多表操作,在体验了MyBatis提供的对于结果集的封装之后,我们接下来来学习MyBatis中的动态SQL。

动态SQL

所谓的动态SQL,就是根据不同的情况来产生不同的SQL,比如在查询用户的时候,我们可以根据用户的ID、名称、邮箱来查询,当然,这三者也可以进行组合,如果要为每一种情况写一个查询,那工作量还是比较恐怖的,动态SQL在这个时候就充分发挥作用了。

在MyBatis中,提供了一下几个标签来实现动态SQL

  • if
  • choose
    • when
    • otherwise
  • where
  • set
  • trim
  • foreach

下面我们就来详细学习这几个标签

if标签

if标签中包含一个必须具备的属性test="",只有当test中的值为真时,if标签里面的内容才会被拼接到SQL语句中,如下代码所示。

<select id="selectByUser" resultType="domain.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 
    <!--注意这里,如果两者均为空,会出现 where之后啥都没有,SQL语法错误,不优雅,有更好地解决方案-->
    <!--如果userName非空,则使用userName来查询-->
    <if test="userName != null and userName != ''">
        and user_name like concat('%', #{userName}, '%')
    </if>
    <!--如果userEmail非空,则使用userEmail来查询-->
    <if test="userEmail != null and userEmail != ''">
        and user_email = #{userEmail}
    </if>
</select>

if语句的使用还是比较好理解的,需要注意的是,if语句可以用于select、update、insert中,后面两种情况的小例子如下所示

 <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>
        <!--注意这里,如果前面都为空,则会出现set where id = #{id},SQL语法错误 -->
        <!--如果只有一个条件,会出现 user_name = #{userName}, where id = #{id}, SQL语法错误-->
        id = #{id} 
        where id = #{id}
</update>

<!--注意insert语句中,插入的值以及字段部分均需要进行测试-->
<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 = TIMESTAMPS})
</insert>

choose标签

choose标签与if标签不同,多if标签的元素之间是没有关系的,而choose标签中的when以及otherwise则是互斥的关系,可以将choose及其子标签理解为if ... else if ... else....,其用法如下

<select id="selectByIdOrUserName" resultType="domain.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
    <choose>
        <when test="id != null and id != ''">
            and id = #{id}
        </when>
        <when test="userName != null and userName != '">
            and user_name = #{userName}
        </when>
        <otherwise>
            and 1 = 2
        </otherwise>
    </choose>
</select>

where、set、trim

在上面的if和choose标签中,我们都会根据情况加入以下辅助的操作,如1=1用于避免当所有的值都不存在的时候,拼接SQL出现错误,where以及set标签的出现,就是为了解决这样的问题

<select id="selectByUser" resultType="domain.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>
        <if test="userName != null and userName != ''">
            and user_name like concat('%', #{userName}, '%')
        </if>
        <if test="userEmail != null and userEmail != ''">
            and user_email = #{userEmail}
        </if>
    </where>
</select>

如果没有一个if匹配,则where也不会存在,解决了出现以where结尾的情况,如果有情况匹配,会自动去除where后面的and,避免出现where and的情况,比上面添加1 = 1的情况更加优雅一点.

如果set中的内容有值,就插入一个set,如果以逗号结尾,则删除该逗号。

<update id="updateByIdSelective">
    update sys_user
    <set>
        <if test="userName != null and userName != ''">
            user_name = #{userName},
        </if>
    </set>
    id = #{id}
    where id = #{id}
</update>

但是由于if中会以,结尾,所以感觉其实set标签没有什么用处,囧

where和set可以通过trim来实现,并且底层就是通过TrimSqlNode实现的

where版本的trim

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

set版本的trim

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

foreach标签

foreach可以对数组、map、或者实现了Iterable接口的对象进行遍历,然后将对应的内容拼接起来,就是通过循环来拼接SQL

<select id="selectByIdList" resultType="domain.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 id in
    <foreach collection="list" open="(" close=")" separator="," item="id" index="i">
        #{id}
    </foreach>
</select>

拼接之后的SQL为

select 
    id, user_name userName, 
    user_password userPassword, 
    user_email userEmail, user_info userInfo,
     head_img headImg, create_time createTime 
from sys_user where id in ( ? , ? ) 

根据生成的SQL来解释foreach的使用,应该还是比较好理解的。

foreach属性

  • collection,要迭代循环的属性名
    • 如果是数组,则是array
    • 列表,则是list
    • 其余为collection
    • 可以通过@Param("name")来指定,然后使用name
  • item:变量名,值为从迭代对象中取出的每一个值
  • index:索引,如果是map,则是key
  • open:内容开头的字符串
  • close:内容结束的字符串
  • separator:拼接时候的分隔符

可以通过下面的方式来实现批量插入

<insert id="insertList">
    insert into sys_user(
        user_name, user_password, user_email,
        user_info, head_img, create_tme)
    values
        <foreach collection = "list" item = "user" separator=",">
            ( #{user.userName}, #{user.userPassword}, #{user.userEmail}
            , #{user.userInfo}, #{user.headImg, jdbcType=BLOB}, #{user.createTime, jdbcType=TIMESTAMPS}
            )
        </foreach>
</insert>

当参数类型是map时,可以通过以下方式来动态操作,在迭代map时,item是map的值,index是key,需要注意这一点

<update id="updateByMap">
    update sys_user
    set
        <foreach colleciton="_parameter/使用@Param()指定的name" item="val" index="key" separator=",">
            ${key} = #{val}
        </foreach>
    where id = #{id}
</update>

总结

本小节主要学习了MyBaits中的动态SQL,包括了if、choose、when、otherwise、where、set、trim、foreach等标签,通过灵活组合这些标签,可以比较容易地实现根据不同的参数来拼接不同的SQL语句,不过,由于每次都需要拼接SQL,所以,对于性能还是有一定的影响的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,100评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,308评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,718评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,275评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,376评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,454评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,464评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,248评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,686评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,974评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,150评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,817评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,484评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,140评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,374评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,012评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,041评论 2 351

推荐阅读更多精彩内容