如果是使用其它的框架,或者是传统的JDBC方式,很多时候需要我们我去根据需要拼装SQL语句,这是一个麻烦的事情,而Mybatis具有动态语句组装的能力,而且它只有几个基本元素,简单明了大量的判断都可以在mybatis中通过映射xml文件里的配置实现功能,这也发挥了mybatis的灵活性和高度可维护性的特点。
下面展示一段xml里面配置的映射代码,通过分析代码展示mybatis动态sql包括的几种元素:
1.if 元素:
if元素是我们最常用的判断语句,相当于java中的if语句,常常与test属性联合使用,在大部分情况下if使用较为简单,通过分析下面的代码了解if是如何使用的:
<select id="findRoles" parameterType="string" resultMap="roleResultMap">
select role_no,role_name,note from t_role where 1=1
<if test="roleName != null and roleName != ' ''">
and role_name like concat('%', #{roleName}, '%')
</if>
</select>
这句话的含义就是,但我们根据参数roleName进行查询的时候,需要将参数传入到映射器中,采取构造正对roleName的模糊查询,test条件起到一个判断的作用,它会判断这个要连接的语句要不要执行,符合里面的条件则会执行,不符合的化,这不去执行,这样写会节省很多用来拼接sql语句的时间,更多的将经历放在维护我们编写的xml文件上。
2.Choose、when、otherwise元素:
对于上面if元素,给人一种不是这样就是那样的感觉,但是很多时候我们面对的并不是一种选择,还需要做出更多的判断,也就是说,类似于Java中switch·····case·····default结构那样,对应的在mybatis中也有对应的逻辑过程的元素,那就是choose、when、otherwise元素。假设一个情景:
当角色的编号不能为空;
当角色的编号为空,而角色名称不为空的时候,则用角色名称作为条件进行查询;
当角色的编号和劫色的名称为空,则角色备注不为空。
那么代码该如何组织呢?
<select id="findRoles" parameterType="role" resultMap="roleResultMap">
select role_no,role_name,note from t_role
where 1 = 1
<choose>
<when test="roleNo != null and roleNo != ' ' '">
AND role_no = #{roleNo}
</when>
<when test="roleName != null and roleName != ' '">
AND role_name like concat('%' ,#{roleName},'%')
</when>
<otherwise>
AND note is not nll
</otherwise>
</choose>
</select>
可以看到,我们上面的逻辑需求通过使用使用choose标签作为选择总体,里面用when和otherwise进行判断,控制逻辑进程,所以如果这样进行配置的化,Mybatis会跟根据参数的设置进行判断动态的组装sql语句,以满足不同的业务逻辑需求。
3.trim、where、set元素:
可以发现我们上面写的代码中终会有一句where 1=1 这一句,总归给人感觉有点和动态的理念相违背,如果我们试着将这个语句去掉,就会出现执行错误,这是因为如果我们将这个语句去点,我们的sql语句将变为“······ where and ······”很明显,这个是一个错的语句,那我们该怎么处理呢?下面的元素会帮我们解决这个问题; <select id="findRoles" parameterType="string" resultMap="roleResultMap">
select role_no,role_name,note from t_role
<where>
<if test="\roleName != null and roleName != '' '">
and role_name like concat('%',#{roleName},'%')
</if>
</where>
</select>
这里的where元素里面条件成立的时候,才会加入where 这个sql关键字到组装的sql语句里面,否则就不会加入到sql里面,然而有时候我们需要去掉一些特殊的sql语法,比如常见的and 或者 or 。而使用trim元素可以达到我们的效果,如下面的代码所示:
<select id="findRoles" parameterType="string" resultMap="roleResultMap">
select role_no,role_name,note from t_role
<trim prefix="where" prefixOverrides="and">
<if test="roleName != null and roleName != ' '">
and role_name like concat('%',#{roleName},'%')
</if>
</trim>
</select>
trim元素意味着我们需要去掉一些日特殊的字符串,prefix代表的是语句的前缀,而prefixOverrides代表的是我们需要去掉的字符串,也就是相当于我们的语句 中,and 被where所代替了,这样上面的使用trim构建的sql语句和使用where构建的语句大体作用一致了。
有些时候,我们需要更新一个字段,如果发送所有的属性去更新一遍对网络带宽消耗很大,性能最佳的办法就是把主键和更新的字段的值传递给sql更新就可以了,例如角色有一个主键和两个字段,如果一个个去写需要2条sql语句,如若有1000个呢,然而在mybatis中我们可以使用set元素来完成这些功能:
<update id="updateRole" parameterType="role">
update t_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组装,这样既可以满足要求,又可以避免全部字段更新的麻烦。
4.foreach元素:
显然foreach元素是一个循环语句,它的作用是遍历集合,能够很好的支持数组和List、Set接口,并对此提供遍历功能,例如我们在数据库中需要查找女性和未知性别的用户或者是男性和未知性别的用户。我们定义1-男;2-女;0-未知;
<select id="findUserBySex" resultType="user">
select * from t_user where sex in
<foreach item="sex" index="index" collection="sesxList"
open="(" separator="," close=")">
#{sex}
</foreach>
</select>
collection 配置的是sexList出传递进来的参数名称,他可以是一个数组或者为一个List等的集合。
item 配置的是循环之中的元素。
index配置的是当前元素在集合中的位置。
open和close配置的事以什么符号将这些元素包装起来。
separator是各个元素之间的间隔符。
在SQL中对于in 语句我们常常使用,对于大量数据的in语句我们需要特别注意,因为他们会消耗大量的内存以及性能,还有一些数据库的SQL对执行的语句的长度也有限制,所以我们在使用它的时候要注意预估一下这个collection对象的长度。
5.test属性:
<select id="getRoleTest" parameterType="string" resultMap="roleResultMap">
select role_no,role_name,note from t_role
<if test="type = 'Y'"></if>
where 1=1
</select>
新版本中不再像老版本需要添加toString()方法,同样也可以判断数值型的参数。
6.bind元素:
<select id="findRole" resultType="RoleBean">
<bind name="pattten" value="'%' + _parameter + '%'"/>
select id,role_name as roleName ,create_date as createDate ,end_date as endDate,
end_flag as endFlag note
from t_role
where role name like #{patten}
</select>
有时候我们传递的参数不只有一个,我们可以使用bind元素来传递多个参数:
public List<RoleBean> findRole(@Param("roleName")String roleName,@Param("note")Stirng note);
然后根据接口定义映射文件,定义两个新的变量去执行模糊查询:
<select id="findRole" resultType="RoleBean">
<bind name="patten_roleName" value=" '%' + roleName + '%' "/>
<bind name="patten_note" value=" '%' + note + '%'"/>
select id,role_name as roleName,create_date as createDate,
end_date as endDate,end_flag as endFlag, note
from t_role
where role name like #{patten_roleName}
and note like #{patten_note}
</select>