动态SQL是MyBatis的强大特征之一。极大的简化我们拼装SQL的操作。
动态SQL元素和使用JSTL或其他类似基于XML的文本处理器相似。
MyBatis采用功能强大的基于OGNL的表达式来简化操作。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
OGNL(Object Graph Navigation Language)对象导航图语言,这是一种强大的表达式语言,通过它可以非常方便的来操作对象属性。类似于我们的EL,SpEL等。
访问对象属性:person.name
调用方法:person.getName()
调用静态属性/方法:@java,lang.Math@PI
@java.util.UUID@randomUUID()
调用构造方法:new com.atguigu.bean.Person('admin').name
运算符:+,-,*,/,%
逻辑运算符:in,not in,>,>=,<,<=,==,!=
注:xml中特殊符号如“,>,<等这些都需要使用转义字符。
If
如果我们有这样一个SQL:
SELECT d.id did, d.dept_name dept_name,e.id eid,e.last_name last_name,
e.email email,e.gender gender from tbl_dept d
LEFT JOIN tbl_employee e on d.id = e.d_id WHERE d.id=1;
这个sql中有四个字段需要我们传入,但是如果我们并不是每个字段都会传入值,那么我们可以使用if标签来动态加载sql:
<select id="getEmpsBuConditionIf" resultType="com.test.mybatis.bean.Employee">
select * from tbl_employee where
<!--test:判断表达式(OGNL表达式),从参数中取值进行判断-->
<if test="id!=null">
id = #{id}
</if>
<!-- ognl会进行字符串与数字的转换判断 "0"==0 -->
<if test="gender==0 or gender==1">
and gender = #{gender}
</if>
</select>
trim(where,set)
这样实现也会有一个问题:如果我们的id是null,那么我们的sql语句就会在where后面多出一个and,于是我们想到两种解决方法:
1.给每个查询都写成where 1=1,以后的条件都写and xxx;
2.mybatis使用where标签来将所有的查询条件都包括在内。会将where标签中拼装的sql,多出来的and或者or去掉。
使用where标签:
<select id="getEmpsBuConditionIf" resultType="com.test.mybatis.bean.Employee">
select * from tbl_employee
<where>
<if test="id!=null">
id = #{id}
</if>
<if test="gender==0 or gender==1">
and gender = #{gender}
</if>
</where>
</select>
这样就不会有问题了。但是如果有人喜欢将and加在条件后面而不是前面呢?这样我们最后一个字段不传的时候,就会在最后多处一个and,于是我们想到了trim来自定义字符串的截取规则。
trim:自定义字符串的截取规则
prefix="":前缀,trim标签体中是整个字符串拼串后的结果。prefix给拼串后的整个字符串加一个前缀。
prefixOverrides="":前缀覆盖,去掉整个字符串前面多余的字符串。
suffix="":后缀,suffix给拼串后的结果加一个后缀
suffixOverrides="":后缀覆盖,去掉整个字符串后面多余的字符串。
<select id="getEmpsBuConditionTrim" resultType="com.test.mybatis.bean.Employee">
select * from tbl_employee
<trim prefix="where" suffixOverrides="and">
<if test="id!=null">
id = #{id} and
</if>
<if test="gender==0 or gender==1">
gender = #{gender}
</if>
</trim>
</select>
但是在实际项目中,我们对trim的使用并不多。
上面描述的是查询的情况,如果我们需要在更新数据时实现哪个传入值,就更新哪个的需求,我们就需要了解一下set标签。如果我们原来更新的sql语句实现如下:
<update id="updateEmp">
update tbl_employee set last_name = #{lastName},email = #{email},gender = #{gender} where id =#{id}
</update>
上面的代码,我们只能同时更新last_name,email和gender三个值,现在我们使用set标签,就可以实现传入哪个值就更新哪个值:
<update id="updateEmp" >
update tbl_employee
<set>
<if test="lastName != null">
last_name = #{lastName},
</if>
<if test="email != null">
email = #{email},
</if>
<if test="gender != null">
gender = #{gender}
</if>
</set>
where id =#{id}
</update>
我们也可以使用trim标签来实现:
<update id="updateEmp" >
update tbl_employee
<trim prefix="set" suffixOverrides=",">
<if test="lastName != null">
last_name = #{lastName},
</if>
<if test="email != null">
email = #{email},
</if>
<if test="gender != null">
gender = #{gender}
</if>
</trim>
where id =#{id}
</update>
choose(when,otherwise)
choose(when,otherwise):分支选择,类似于java的switch-case。对于上面的sql,这里我们如果有id,就会将id作为查询条件;如果有gender,就会将gender作为查询条件;如果id和gender都有,就会将第一个也就是id作为查询条件;也就是只会进入一个查询条件。我们需要加上<otherwise>1=1</otherwise>,这样在查询条件都没有的时候,我们会查询所有的。
<select id="getEmpsBuConditionChoose" resultType="com.test.mybatis.bean.Employee">
select * from tbl_employee
<where>
<choose>
<when test="id!=null">
id = #{id}
</when>
<when test="gender==0 or gender==1">
gender = #{gender}
</when>
<otherwise>
1=1
</otherwise>
</choose>
</where>
</select>
foreach
当我们需要对一个集合进行遍历的时候,我们可以使用foreach。
比如有这样一条sql:
select * from tbl_employee where id in(1,2,3);
当我们需要实现它的时候,我们可以使用foreach。
collection:指定要遍历的集合:list类型的参数会特殊处理封装在map中,map的key就叫list。 item:将当前遍历出的元素赋值给指定的变量。 separator:每个元素之间的分隔符。 open:遍历出所有结果拼接一个开始的字符。 close:遍历出的所有结果拼接一个结束的字符。 index:索引。遍历list的时候index是索引,item就是当前的值;遍历map的时候是index表示的就是map的key,item就是map的值。 #{变量名}就能取出变量的值也就是当前遍历出的元素
<select id="getEmpsByConditionForeach" resultType="com.test.mybatis.bean.Employee">
select * from tbl_employee where id in(
<foreach collection="ids" item="item_id" separator="," open="(" close=")" index="">
#{item_id}
</foreach>
)
</select>
这样就可以实现对集合进行遍历。
除了对集合进行遍历,foreach还可以实现批量保存。
比如有如下sql“
insert into tbl_employee(last_name,email,gender,d_id)
values('Mary','mary@test.com','1',1),('Tom','tom@test.com','1',2);
可以用foreach'来实现批量保存:
<insert id="addEmps" >
insert into tbl_employee(last_name,email,gender,d_id)
values
<foreach collection="emps" item="emp" separator=",">
(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id)
</foreach>
</insert>
因为支持values(),(),()....这种语法。(MySQL和SQL server支持这种语法)