MyBatis标签实现的动态SQL语句

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014010512/article/details/82624309

MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。

动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多的元素需要来了解。MyBatis3 大大提升了它们,现在用不到原先一半的元素就可以了。MyBatis 采用功能强大的基于 OGNL 的表达式来消除其他元素。

MyBatis 的动态sql语句是基于OGNL表达式的。可以方便的在 sql 语句中实现某些逻辑。总体说来 MyBatis 动态 SQL 语句主要有以下几类:

  • if 语句 (简单条件判断)
  • where (用来简化sql语句中where条件判断的,能智能的处理 and or,不必担心多余导致语法错误)
  • set (主要用于更新时)
  • trim (对包含的内容加上 prefix,或者 suffix 等,前缀,后缀)
  • choose (when,otherwize) (相当于java 语言中的 switch ,与 jstl 中的choose 很类似)
  • foreach (实现 mybatis in 语句查询时特别有用)

1. if 语句

动态 SQL 通常要做的事情是有条件地包含 where 子句的一部分。

首先看一个很普通的查询:

<!-- 查询作者列表,like作者名字 -->
<select id="getAuthorListLikeName" parameterType="Author" resultMap="resultAuthorMap">
	SELECT * FROM author a 
	WHERE a.author_name LIKE CONCAT(CONCAT('%', #{authorName}),'%')
            AND a.mobile = #{mobile}
</select>

但是当 authorName 或 mobile 为 null 时,此语句很可能报错或查询结果为空。此时我们使用 if 动态 sql 语句先进行判断,如果值为 null 或等于空字符串,我们就不进行此条件的判断,增加灵活性。

<!-- 添加if(判断参数) - 将实体类 Author 不为空的属性作为 where 条件 -->
<select id="getAuthorListLikeName" parameterType="Author" resultMap="resultAuthorMap">
	SELECT * FROM author a
	WHERE
	<if test="authorName != null">
		a.author_name LIKE CONCAT(CONCAT('%', #{authorName}),'%')
	</if>
	<if test="mobile != null">
		AND a.mobile = #{mobile, jdbcType=VARCHAR}
	</if>
</select>

这条语句提供了一个可选的文本查找类型的功能。

2. where 语句

上面的if语句,如果所有条件都没有匹配上将会怎样?最终这条 SQL 会变成这样:

SELECT * FROM author a
    WHERE

这会导致查询失败。如果仅仅第二个条件匹配,最终SQL是这样:

SELECT * FROM author a
    WHERE
        AND a.mobile = #{mobile, jdbcType=VARCHAR}

这个查询也会失败。这个问题可以用一些方法解决,比如加一个永远为true的条件。

<select id="getAuthorListLikeName" parameterType="Author" resultMap="resultAuthorMap">
	SELECT * FROM author a
	WHERE 1=1
	<if test="authorName != null">
		AND a.author_name LIKE CONCAT(CONCAT('%', #{authorName}),'%')
	</if>
	<if test="mobile != null">
		AND a.mobile = #{mobile, jdbcType=VARCHAR}
	</if>
</select>

MyBatis 有一个简单的处理就能得到想要的效果:

<select id="getAuthorListLikeName" parameterType="Author" resultMap="resultAuthorMap">
	SELECT * FROM author a
	<where>
		<if test="authorName != null">
			a.author_name LIKE CONCAT(CONCAT('%', #{authorName}),'%')
		</if>
		<if test="mobile != null">
			AND a.mobile = #{mobile, jdbcType=VARCHAR}
		</if>
	</where>
</select>

where 元素知道只有在一个以上的if条件有值的情况下才去插入“WHERE”子句。而且,若最后的内容是“AND”或“OR”开头的,where 元素也知道如何将他们去除。

3. set 更新语句

当 update 语句中没有使用 if 标签时,如果有一个参数为 null,都会导致错误。

当在 update 语句中使用 if 标签时,如果前面的 if 没有执行,则或导致逗号多余错误。使用set标签可以将动态的配置 SET 关键字,并剔除追加到条件末尾的任何不相关的逗号。类似的用于动态更新语句的解决方案叫做 set。set 元素可以被用于动态包含需要更新的列,而舍去其他的。使用 if+set 标签修改后,如果某项为 null 则不进行更新,而是保持数据库原值。如下示例:

<!-- if/set(判断参数) - 将实体 Author 类不为空的属性更新 -->
<update id="updateAuthor" parameterType="Author">
	UPDATE author
	<set>
	    <if test="authorName != null and authorName != '' "> author_name = #{authorName}, </if>
	    <if test="mobile != null and mobile != '' "> mobile = #{mobile}, </if>
	</set>
	WHERE id = #{id};
</update>

set 标签元素主要是用在更新操作的时候,它的主要功能和 where 标签元素其实是差不多的,主要是在包含的语句前输出一个 set,然后如果包含的语句是以逗号结束的话将会把该逗号忽略,如果 set 包含的内容为空的话则会出错。有了 set 元素就可以动态的更新那些修改了的字段。

4. trim 语句

如果 where 或 set 元素没有按正常套路出牌,我们还是可以通过自定义 trim 元素来定制我们想要的功能。比如,和 where 元素等价的自定义 trim 元素为:

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

<trim prefix="SET" suffixOverrides=","> ... </trim>

prefixOverrides 属性会忽略通过管道分隔的文本序列(注意此例中的空格也是必要的)。它带来的结果就是所有在 prefixOverrides 属性中指定的内容将被移除,并且插入 prefix 属性中指定的内容。

suffixOverrides属性会忽略后缀中的值。即suffixOverrides属性中指定的内容会被移除,并插入prefix 属性中指定的内容。

trim 是更灵活用来去处多余关键字的标签,它可以用来实现 where 和 set 的效果。

<select id="getAuthorListLikeName" parameterType="Author" resultMap="resultAuthorMap">
	SELECT * FROM author a
	<trim prefix="WHERE" prefixOverrides="AND|OR">
		<if test="authorName != null">
			a.author_name LIKE CONCAT(CONCAT('%', #{authorName}),'%')
		</if>
		<if test="mobile != null">
			AND a.mobile = #{mobile, jdbcType=VARCHAR}
		</if>
	</trim>
</select>

<!-- if/set(判断参数) - 将实体 Author 类不为空的属性更新 -->
<update id="updateAuthor" parameterType="Author">
	UPDATE author
	<set>
		<if test="authorName != null and authorName != '' "> author_name = #{authorName}, </if>
		<if test="mobile != null and mobile != '' "> mobile = #{mobile}, </if>
	</set>
	WHERE id = #{id};
</update>

5. choose,when,otherwise语句

有时候我们并不想应用所有的条件,而只是想从多个选项中选择一二个。而使用if标签时,只要test中的表达式为 true,就会执行 if 标签中的条件。MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。if 标签是与(and)的关系,而 choose 是或(or)的关系。

choose 标签是按顺序判断其内部 when 标签中的 test 条件出否成立,如果有一个成立,则 choose 结束。当 choose 中所有 when 的条件都不满则时,则执行 otherwise 中的 sql。类似于Java 的 switch 语句,choose 等同于 switch,when 等同于 case,otherwise 则等同于 default。

如下面的例子。choose 会从上到下选择一个 when 标签的 test 为 true 的 sql 执行。安全考虑,我们使用 where 将 choose 包起来。

<!-- choose(判断参数) - 按顺序将实体类 Author 第一个不为空的属性作为:where条件 -->
<select id="getAuthorList" resultMap="resultAuthorMap" parameterType="Author">
	SELECT * FROM author a
	<where>
		<choose>
			<when test="authorName != null">
				a.author_name LIKE CONCAT(CONCAT('%', #{authorName}),'%')
			</when>
			<when test="mobile != null">
				AND a.mobile = #{mobile, jdbcType=VARCHAR}
			</when>
			<otherwise>
				a.mobile = "13666666666"
			</otherwise>
		</choose>
	</where>
</select>

when元素表示当 when 中的条件满足的时候就输出其中的内容,跟 JAVA 中的 switch 效果差不多的是按照条件的顺序,当 when 中有条件满足的时候,就会跳出 choose,即所有的 when 和 otherwise 条件中,只有一个会输出,当所有的条件都不满足的时候就输出 otherwise 中的内容。

6. foreach

动态 SQL 的一个常用的必要操作是需要对一个集合进行遍历,通常是在构建 IN 条件语句的时候。比如:

<select id="getAuthorListByIds" parameterType="java.util.List" resultMap="resultAuthorMap" >
	SELECT * FROM author
	WHERE id in
	<foreach collection="list" index="index" item="item" open="(" separator="," close=")">
               #{item}       
        </foreach>
</select>
	
<select id="getAuthorListByIds" parameterType="java.util.ArrayList" resultMap="resultAuthorMap">
	SELECT * FROM author
        WHERE id in
	<foreach collection="array" index="index" item="item" open="(" separator="," close=")">
		#{item}
	</foreach>
</select>

<select id="dynamicForeach3Test" parameterType="java.util.HashMap" resultMap="resultAuthorMap">
	SELECT * FROM author
	WHERE id in
	<foreach collection="ids" index="index" item="item" open="(" separator="," close=")">
		#{item}
	</foreach>
</select>

foreach 元素的功能是非常强大的,它允许你指定一个集合,声明可以用在元素体内的集合项和索引变量。它也允许你指定开闭匹配的字符串以及在迭代中间放置分隔符。这个元素是很智能的,因此它不会偶然地附加多余的分隔符。

注意 你可以将一个 List 实例或者数组作为参数对象传给 MyBatis,当你这么做的时候,MyBatis 会自动将它包装在一个 Map 中并以名称为键。List 实例将会以“list”作为键,而数组实例的键将是“array”。

猜你喜欢

转载自blog.csdn.net/u014010512/article/details/82624309