一、定义
SQL语句会随着用户的输入或外部条件的变化而变化,我们称为动态SQL。在Mybatis中提供了很多实现动态SQL的标签,我们学习Mybatis中的动态SQL就是掌握这些动态SQL标签。
二、动态SQL标签
(一)if标签
<if>:用于判断条件是否成立。使用test属性进行条件判断,如果条件为true,则拼接SQL。
<if test="条件表达式"> 要拼接的sql语句 </if>
案例:
我们的前端页面如下图,在查询框中输入内容作为查询的条件,可以在每个框中输入内容也可在部分框中输入内容。
在XML文件中我们编写sql语句,如下:
<mapper namespace="com.itheima.mapper.EmpMapper">
<!--查询操作-->
<select id="getEmpList" resultType="com.itheima.pojo.Emp">
select * from emp
where name like concat('%',#{name},'%')
and gender = #{gender}
and entrydate between #{start} and #{end}
order by update_time desc
</select>
</mapper>
在查询框中我们输入的内容为:张 男 2010-01-01 2020-01-01
这样的输入条件可以得到正确的结果,查询结果如图:
当我们不把条件填写完整时,我们应该根据输入条件的不同修改查询语句后面的条件,这里就需要用到if标签,test属性里面是条件,如果条件为true,就把if标签包裹的sql语句展示,否则不展示:
<select id="getEmpList" resultType="com.itheima.pojo.Emp">
select * from emp
where
<if test="name != null">
name like concat('%',#{name},'%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="start != null and end != null">
and entrydate between #{start} and #{end}
</if>
order by update_time desc
</select>
其实在另一种情况下还会出现问题,对于上面的代码如果只有第二个if中的条件成立,我们的where条件就会变成 where and gender = ?,很显然and是多余的,这里就要使用where标,where
只会在子元素有内容的情况下才插入where子句,而且会自动去除子句的开头的and或or:
<select id="getEmpList" resultType="com.itheima.pojo.Emp">
select * from emp
<where>
<if test="name != null">
name like concat('%',#{name},'%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="start != null and end != null">
and entrydate between #{start} and #{end}
</if>
</where>
order by update_time desc
</select>
(二)set标签
<set>
:动态的在SQL语句中插入set关键字,并会删掉额外的逗号。(用于update语句中)
案例:完善更新员工功能,修改为动态更新员工数据信息
- 动态更新员工信息,如果更新时传递有值,则更新;如果更新时没有传递值,则不更新
- 解决方案:动态SQL
映射文件中的代码如下:
<mapper namespace="com.itheima.mapper.EmpMapper">
<!--更新操作-->
<update id="update">
update emp
<set>
<if test="username != null">
username=#{username},
</if>
<if test="name != null">
name=#{name},
</if>
<if test="gender != null">
gender=#{gender},
</if>
<if test="image != null">
image=#{image},
</if>
<if test="job != null">
job=#{job},
</if>
<if test="entrydate != null">
entrydate=#{entrydate},
</if>
<if test="deptId != null">
dept_id=#{deptId},
</if>
<if test="updateTime != null">
update_time=#{updateTime}
</if>
</set>
where id=#{id}
</update>
</mapper>
如果不使用set标签,而是直接使用set,遇到的问题就和案例1中直接使用where类似(出现多余的and或or),在这里会出现多余的逗号。
(三)foreach
语法:
<foreach collection="集合名称" item="集合遍历出来的元素/项" separator="每一次遍历使用的分隔符" open="遍历开始前拼接的片段" close="遍历结束后拼接的片段">
</foreach>
案例3:批量删除
映射文件中的代码:
<mapper namespace="com.itheima.mapper.EmpMapper">
<!--删除操作-->
<delete id="deleteByIds">
delete from emp where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
</mapper>
(四)sql和include
在书写映射文件时,可能会出现冗余的代码,比如:
显然上面两个模块的代码的褐色部分是冗余的。在写java代码时为了避免冗余我们会将相同的部分抽出来组成一个方法,在这里也使用类似的思想。我们可以对重复的代码片段进行抽取,将其通过
<sql>
标签封装到一个SQL片段,然后再通过<include>
标签进行引用。
-
<sql>
:定义可重用的SQL片段 -
<include>
:通过属性refid,指定包含的SQL片段
SQL片段: 抽取重复的代码
<sql id="commonSelect">
select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp
</sql>
然后通过<include>
标签在原来抽取的地方进行引用。操作如下:
<select id="list" resultType="com.itheima.pojo.Emp">
<include refid="commonSelect"/>
<where>
<if test="name != null">
name like concat('%',#{name},'%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="begin != null and end != null">
and entrydate between #{begin} and #{end}
</if>
</where>
order by update_time desc
</select>
三、总结
if标签,用来决定是否拼接其所包裹的sql,其中test属性即为条件,条件成立则拼接;
where标签,放置有多余的and或or出现;
set标签,用与update语句中,放置有多余的逗号出现;
foreach标签,当所传的参数为集合时,用来遍历集合,其中属性collection用来说明集合的名称,item用来说明集合遍历出来的元素/项,seperator说明每一次遍历使用的分隔符,open指遍历开始前输出的内容,close指遍历结束后输出的内容;
sql用来抽取sql语句相同的部分,可以指定一个id属性值便于后续引入;
include用来引入被抽出的sql语句,通过id值来指定被引入的sql。