动态sql语句,是继结果集映射后,mybatis提供的又一个强大的功能。
以往在应用JDBC编写持久层代码时,由于业务的需要,不同的情况会对应不同的sql,这时,我们只能在Dao层写入大量恶心的if..else以及其他判断语句,这不仅给维护工作带来了极大困难,最重要的是程序猿们看了会非常不爽,以至于不愿意干活-_-!
mybatis利用jstl很好的解决了这个问题:在sqlMapper.xml中定义相关标签来实现sql有的动态拼接。
mybatis提供了八个标签来支持动态sql:
1.if
2.choose (3.when, 4.otherwise)
5.trim (6.where, 7.set)
8.foreach
下面对这几个标签做简要介绍。
if
该标签(<if test = "jstl_exp"/>
)与java中的if语意相同,通过为test属性指定一个jstl表示式即可,可以对多种参数的属性进行判断,以对空值的判断为例:
<select id="dynamicSelectIfOne"
parameterType="Pet" resultType="Pet">
select * from pet
<if test="name != null">
where name = #{name}
</if>
</select>
这样,若Pet中的name值为null,则在执行sql语句时只执行select * from pet
,而不会有后面的where语句。
注意,当用多个条件作为test
属性的值时,要用and
或or
连接,而不能用&&或||。
<select id="dynamicSelectIfOne"
parameterType="Pet" resultType="Pet">
select * from pet
<if test="name != null and sex = 'm'">
where name = #{name}
</if>
</select>
当where语句中有like语句是,在取得变量值的时候就不能用#{}
来完成,要用%${paramName}%
:
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like '%${title}%'
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
2.chose(when,otherwise)
当业务需求中有很多种情况会发生,但同时只会发生一种时,或许使用<if/>
可以完成需求,但这不是一种优雅的做法,这种情况应该尽量用<choose/>,<when/>,<otherwise/>
来完成:
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG WHERE state = ‘ACTIVE’
<choose>
<when test="title != null">
AND title like #{title}
</when>
<when test="author != null and author.name != null">
AND author_name like #{author.name}
</when>
<otherwise>
AND featured = 1
</otherwise>
</choose>
</select>
这类似于java中是 case..switch 语句,mybatis依次检查<when/>
标签,若有一个复杂test中的条件,则检查终止,生成租后的sql并执行。
3.trime
该标签用于自定义某一标签的行为(略)。
4.where
有时候利用<if/>
,<when/>
,<choose/>
,<otherwise/>
没有办法完成既定的需求,例如下面的sql语句:
select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
WHERE
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</select>
若所有<if/>
都没有匹配,那么最后被执行的sql就变成了SELECT * FROM BLOG WHERE
,这显然是一个错误的sql语句,mybatis当然不能允许这样的事情发生,所以mybatis提供了<where/>
标签来解决这个问题。
<select id="findActiveBlogLike"
resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
</select>
当没有<if/>
被匹配时,最后的sql会变成SELECT * FROM BLOG
,当第二个或第三个<if/>
被匹配时,mybatis会自动去掉多余的and。这是很方便的,如果有java来实现,绝对是一件让人无比头疼的事。
5.set
和上面的第一个sql一样,set语句也存在这样的问题:
<update id="updateAuthorIfNecessary">
update Author
set
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
where id=#{id}
</update>
当没有<if/>
被匹配时,最终是sql语句就变成了update Author
,这显然不会被执行成功,同where一样,mybatis提供了
set where id=#{id}<set/>
标签来代替硬编码:
<update id="updateAuthorIfNecessary">
update Author
<set>
<if test="username != null">username=#{username},</if>
<if test="password != null">password=#{password},</if>
<if test="email != null">email=#{email},</if>
<if test="bio != null">bio=#{bio}</if>
</set>
where id=#{id}
</update>
同样的,mybatis会自动去除多余的逗号,来生成正确是sql语句。
6.foreach
该标签提供了再sql语句配置时的循环遍历功能,应用起来同样很简单,下面举几个例子来说明一下:
1)参数格式为Map<String,List<POJO>>
<insert id="dynamicSqlBatchInsert" parameterType="hashmap">
insert into pet (name,species,sex) values
<foreach collection="pets" item="var" separator="," >
(#{var.name},#{var.species},#{var.sex})
</foreach>
</insert>
2)参数格式为List<POJO>
<insert id="dynamicSqlBatchInsert" parameterType="list">
insert into pet (name,species,sex) values
<foreach collection="list" item="index" separator="," >
(#{name},#{species},#{sex})
</foreach>
</insert>
3)参数格式为List<String>
<insert id="dynamicSqlBatchInsert" parameterType="list">
select <include refid="column"/> from pet where name in
<foreach collection="list" item="index" separator="," open="(" close = ")">
(#{index})
</foreach>
</insert>