mybatis sql标签_MyBatis手把手跟我做系列(三) --- 动态SQL标签

180f84c3005df756a2c081ec12c0b836.png

上一章已经实现了最简单的CRUD的操作,不过还是有一些细节问题需要进一步的学习

一.#{}${}

在前面的章节中,大家已经看过很多次#()这个符号了,虽然没有专门的去讲解,但是也应该能猜出#{}应该就是一个参数的占位符,是的,大家猜测的没错,不过这个占位符除了#{}之外,还有一个${},两者都是起到占位的作用,但又是完全不同的.
简单来说, #{}其实就相当于我们之前在JDBC代码中使用了PreparedStatement,并在SQL语句中使用了 ? 替代符

${}的占位,就相当于SQL语句直接使用了字符串拼接,而且还需要自己加上``哦.

上面的意思我们用伪代码形容一下

select * from t_user where username=#{username}
就相当于
select * from t_user where username=?
而且会在随后的代码执行中自动用你传入的 `username` 参数将`?`替代掉,比如`username`的值是`张三`
select * from t_user where username='张三'

select * from t_user where username=${username}
比如 参数 username 的值是张三,那就相当于
select * from t_user where username=张三
注意这里是错误的哦,因为张三是个字符串,你需要自己加上单引号
select * from t_user where username='${username}'才是正确的写法

所以,总结来说:#{}:解析为一个JDBC预编译语句(PreparedStatement)的参数标记,一个#()会被解析为一个参数占位符?,在数据库中发生参数的替换

${}: 仅仅为一个纯粹的String字符换的替换,而且在动态SQL的解析阶段就讲变量进行替换,而且如果参数就是一个简单类型的话(意思是不是一个自定义对象,就是一个int,或者String),那么参数的占位符只能使用value,也就是上面的 username=”张三”,如果就只有这么一个值,使用 ${} 替代符就只能使用value占位,也就是要写为 select * from t_user where username=${value}

因此一般情况下,我们当然优先使用#(),还能一定程度上防止SQL注入,但是有些时候使用${}却很方便,比如在模糊查询的时候

...
<!-- 根据名称模糊查询用户信息 -->
<select id="getUserByName" parameterType="string" resultMap="userMap">
    select * from t_user where username like '%${value}%'</select>
...

下面的截图,展示了上面文字描述的一些问题

a32e804f3403741a1bb3d16d47e196d3.png

39402b1959d5935bd81b08b76e2bc7d9.png

22ac873fa04f714e2c2e63e4e1d03320.png

f1a7b2e5a4e282adea27761838c91bd3.png

e4ed125b4f93e70fec5516287fec8d08.png

二.动态SQL

上面的例子只是根据用户的名字进行了模糊查询,这个时候问题就来了,如果要使用多条件查询呢?那就需要用到MyBatis提供的动态SQL功能了,通过 if, choose, when, otherwise, trim, where, set, foreach标签,可组合成非常灵活的SQL语句

1. whereif

还是直接通过应用场景来解释,相信大家之前都写过多条件查询的SQL语句了,基本的思考点就是到底有哪些条件?哪些条件不为空的时候才进行SQL拼接?多条件后面的and或者or该怎么搞定?这个问题,在大家的学习过程中,最早期的代码应该是这么写的

......
String sql = "select * from t_user where 1=1 "
if(username != null && !"".equals(username)){
    sql += " and username like '%" + username + "%'";
}if(userTel != null && !"".equals(userTel)){
    sql += " and userTel ='" + userTel + "'";
}
...

在早期的JDBC代码中,为了避免SQL语句后面拼接and的问题,一般都会人为的在前面加上一个条件1=1,使用MyBatis其实也跳不过这个问题,不过我们使用MyBatis自带的标签来解决这个问题

<!--多条件查询动态SQL-->
<select id="getUserBySelective" parameterType="user" resultMap="userMap">
    select * from t_user    
    <where>
        <if test="id >= 1">
            id=#{id}        
        </if>
        <if test="userTel != null">
            and user_tel like '%${userTel}%'        
        </if>
        <if test="username != null">
            and username like '%${username}%'        
        </if>
        <if test="registrationTime != null">
            and Date_Format(registration_time,'%Y-%m-%d')=#{registrationTime}        
        </if>
    </where>
</select>

上面的语句就用到了MyBatis的where和if标签,具体标签的含义其实不用多做解释大家也能看懂,而且最好的地方是完美的解决了之前and拼接的问题,下面的截图给大家展示了这些效果

e6b16b58c26fc1995515df3cd725dd36.png

dacb1c7af32be97e394f7aa89998bf95.png

8ce0ac8648befde1583a9d6266526fac.png

2. trimif

where标签也完全可以用trim标签替代,一般情况下的写法是这个样子的

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

其实关键就是prefixprefixOverrides这两个属性prefix: 前缀,这里的意思就是SQL语句加上whereprefixOverrides: 去掉第一个and或者是or,就是后面的拼接语句中出现了and或者or,如果出现在第一个条件中就去掉

上面的代码,我们通过trim标签替换

<trim prefix="WHERE" prefixOverrides="AND |OR ">
    <if test="id >= 1">
        id=#{id}    
    </if>
    <if test="userTel != null">
        and user_tel like '%${userTel}%'    
    </if>
    <if test="username != null">
        and username like '%${username}%'    
    </if>
    <if test="registrationTime != null">
        and Date_Format(registration_time,'%Y-%m-%d')=#{registrationTime}    
    </if>
</trim>

f23bbb9a8c7bb6777f3e54d695ffbbc2.png

3. setif

既然查询是多条件的,我们回过头看看之前写过的新增insert和修改update方法,按照之前的写法,如果有条件不传,会出现什么问题,我们看一下之前的修改方法

4ca934e44b23a25a57db5880ea7e4c25.png

这样肯定和实际情况不符,我们新加一个修改方法,根据传入的值进行判断修改,这样改的关键点其实还是判断要修改的值是否为空,不为空再进行修改,问题的关键点还是多个条件判断的时候,最后一个逗号(,)截取的问题,在MyBatis中,使用set标签

bbc807283389021526e0c9ea10bd1ca2.png

412975b9bb37633dccea11620d38f957.png

同样,<trim>标签也可以完全替换<set>

<update id="updateUserByIdSelective" parameterType="user">
  update t_user  
    <trim prefix="set" suffixOverrides=",">
        <if test="userTel != null">
          user_tel=#{userTel},      
        </if>
        <if test="username != null">
          username=#{username},      
        </if>
        <if test="password != null">
          password=md5(#{password}),      
        </if>
        <if test="registrationTime != null">
          registration_time=#{registrationTime}      
        </if>
  </trim>
  where id=#{id}
</update>

suffixOverrides: 去掉最后一个逗号(,)后缀

在新增语句中,也是一样,如果只是选择性的要插入一些字段(当然前提是数据库中该字段可以为null),看一下之前新增数据的效果

e943c03218c4a581744433824b1de41a.png

如果要选择性的拼接SQL,新增的SQL语句拼接会涉及到表字段,字段对应的值以及左括号,逗号,右括号,这样拼接判断的时候复杂度稍微高一些,所以,这里使用<trim>标签是最好的选择

<!--根据传入的参数新增用户信息-->
<insert id="insertUserSelective" parameterType="user" useGeneratedKeys="true" keyProperty="id">
    insert into t_user    
    <trim prefix="(" suffix=")" suffixOverrides="," >
        <if test="id != null" >
            id,        
        </if>
        <if test="userTel != null">
            user_tel,        
        </if>
        <if test="username != null" >
            username,        
        </if>
        <if test="password != null" >
            password,        
        </if>
        <if test="registrationTime != null" >
            registration_time,        
        </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides="," >
        <if test="id != null" >
            #{id,jdbcType=INTEGER},        
        </if>
        <if test="userTel != null" >
            #{userTel,jdbcType=VARCHAR},        
        </if>
        <if test="username != null" >
            #{username,jdbcType=VARCHAR},        
        </if>
        <if test="password != null" >
            md5(#{password,jdbcType=VARCHAR}),        
        </if>
        <if test="registrationTime != null" >
            #{registrationTime,jdbcType=VARCHAR},        
        </if>
    </trim>
</insert>

828e2bbb6d1efc080221f43fbd30f503.png

4. SQLinclude

有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
比如上面新增语句的字段条件判断,插入语句实在写的太多,可以将这部分的代码提出来,直接写成代码片段,到时候再用<include>标签引用就可以了

e9f3e6d2ee9124fc0bc94fab21104e43.png

这种用法特别是在写查询语句的时候,字段名较多的情况下,把经常要现实出来的字段用SQL标签引入,在写SQL语句的时候,再直接通过include引入字段就可以了

5. choose(when,otherwise)

上面的whereset标签都是在和if标签配合,判断标签除了if之外,还有choose(when,otherwise)标签,choose(when,otherwise)其实就类似于java的switch语句,把之前的查询语句,从where...if...的组合换成 where...choose(when,otherwise),但是要注意两者之间的区别,choose(when,otherwise)同时只能成立一个条件

<!--多条件查询动态SQL,使用where...choose(when...otherwise)组合-->
<select id="getUserBySelective2" parameterType="user" resultMap="userMap">
    select * from t_user    
     <where>
        <choose>
            <when test="id >= 1">
              id=#{id}            
             </when>
            <when test="userTel != null">
              and user_tel like '%${userTel}%'            
             </when>
            <when test="username != null">
              and username like '%${username}%'            
             </when>
            <otherwise>
              and Date_Format(registration_time,'%Y-%m-%d')=#{registrationTime}            
            </otherwise>
        </choose>
    </where>
</select>

c65978b3a383805facb4ed1578d2d8eb.png

6. foreach

先来捋一捋代码场景,比如同时要查询多个值的场景,意思是SQL语句要写成类似于下面的这个样子:

select * from t_user where id=1 or id=4 or id=5 or id=8

或者

select * from t_user where id in(1, 4, 5, 8)
注意:
这个样子的SQL语句,为了传输值方便,不破坏User类,我们再封装一个Bean类,就叫做查询Bean,这个Bean就是为了查询而服务的,什么意思呢?界面上可能会有各种条件的查询,比如注册的开始日期,结束日期,根据这种情况进行查询,但是我们现在的User类其实只是完全和数据库对应的类,User类里只有一个注册日期,没有什么注册开始日期和结束日期的概念,但是在查询的时候却需要查询这些东西,所以干脆就专门建一个实体类,来处理封装界面上要查询的内容,这样就可以方便的将界面上的数据传输给Dao层.这其实是分层架构中VO,PO,DO,DTO等等领域模型的概念,有兴趣的可以自己查询了解一下

无论如果,为了封装方便,这里新创建一个类QueryBean.java:

//封装要查询的多个用户的id
import java.util.List;public class QueryBean {    
    private List<Integer> ids;    
    public List<Integer> getIds() {        
        return ids;
    }    
    public void setIds(List<Integer> ids) {        
        this.ids = ids;
    }
}

在UserMapper.xml中添加根据多个id查询的方法

<select id="getUserByQueryIds" parameterType="queryBean" resultMap="userMap">
    select * from t_user    
    <where>
        <!--
            collection:指定输入对象中的集合属性
            item:每次遍历生成的对象
            open:开始遍历时的拼接字符串
            close:结束时拼接的字符串
            separator:遍历对象之间需要拼接的字符串
            select * from t_user where (id=1 or id=4 or id=5 or id=8)
          -->
        <foreach collection="ids" item="id" open="(" close=")" separator="or">
            id=#{id}        
        </foreach>
    </where>
</select>

注意下图中,foreach标签中每个属性的作用

5bf0ab870679c397bd680a3b7f1be686.png

也可以稍微换一种写法

<select id="getUserByQueryIds" parameterType="queryBean" resultMap="userMap">
    select * from t_user    
    <where>
        <!--
            collection:指定输入对象中的集合属性
            item:每次遍历生成的对象
            open:开始遍历时的拼接字符串
            close:结束时拼接的字符串
            separator:遍历对象之间需要拼接的字符串
            select * from t_user where (id=1 or id=4 or id=5 or id=8)

        <foreach collection="ids" item="id" open="(" close=")" separator="or">
            id=#{id}
        </foreach>
        -->

        <!-- select * from t_user where id in (1, 4, 5, 8) -->
        <foreach collection="ids" item="id" open="id in (" close=")" separator=",">
            #{id}        
        </foreach>
    </where>
</select>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值