MyBatis--7.动态SQL

866 阅读5分钟

1.引言

​ 上一篇文章我们实现了MyBatis的多条件查询,然而这样程序还是不够完美,因为设置了多条件查询后我们就必须按照所有的要求进行输入,如:name,age,一旦我们只输入了name,而没有输入age,程序就会报错,所以正确的处理方式应该是我们输入了name,就查询name,如果没有输入name就不判断name的内容,age也是一样,本章使用动态SQL来解决这个问题。

2.<if>标签

​ 使用if标签实现以上的问题,编写mapper代码如下:

	<select id="selectStudentByConditionIf" resultType="com.mybatis.beans.Student">
		select id,name,age,score 
		from student 
		where 
		<if test="name != null and name != ''">
		 	 name like '%' #{name} '%' 
		</if>
		<if test="age > 0 ">
			and	age > #{age}
		</if>
	</select>

但是这样写还是会出问题,因为当第一个if不满足而第二个if满足时,则会出现以下SQL语句

select id,name,age,score from student where and age > ? 

此时因为第一个if标签不满足,所以就会出现where后面直接接and的语法错误,接着改进mapper代码:

	<select id="selectStudentByConditionIf" resultType="com.mybatis.beans.Student">
		select id,name,age,score 
		from student 
		where 1=1
		<if test="name != null and name != ''">
		 	and name like '%' #{name} '%' 
		</if>
		<if test="age > 0 ">
			and	age > #{age}
		</if>
	</select>

在where先添加一个1=1就能解决这个问题,后面不管是哪个条件满足都不会让SQL出错,但是代码还是不够好,因为1=1 ,就意味着所有的记录都会做一个判断,当数据量很大时对数据库的负担太大,所以我们需要继续改进代码。

3.<where>标签<trim>标签<set>标签

​ 前面使用if标签后可以实现对应的功能,但是代码还是有问题,所以引进where标签进行进一步改进;

	<select id="selectStudentByConditionWhere" resultType="com.mybatis.beans.Student">
		select id,name,age,score 
		from student 
		<where>
		<if test="name != null and name != ''">
		 	and name like '%' #{name} '%' 
		</if>
		<if test="age > 0 ">
			and	age > #{age}
		</if>
		</where>
	</select>

​ 添加where标签后,MyBatis在拼接时会自动删除掉多余的and 或者or关键字。

​ 但是需要注意的是,where只会删除多余的and,不会补充缺失的and,所以在编写的时候在if中都要加上and;

除了where外,还有两个标签有同样的功能,分别是<set>标签<trim>标签,set标签适用于update操作中删除掉多余的“,”,而trim标签则是where标签和set标签的结合体;

set标签可以将多余的逗号删除掉

	<update id="updateStudentByConditonSet" parameterType="Student">
		update student
		<set>
			<if test="name != null and name != ''">
				name = #{name},
			</if>
			<if test="age > 0">
				age = #{age},
			</if>
			<if test="score > 0">
				score = #{score},
			</if>
		</set>
		where id = #{id}
	</update>

​ trim标签的作用就相当于where标签和set标签的整合,它既可以删除多余的“and” 、“or”关键字,也可以删除多余的逗号;trim标签在使用的时候需要指定where还是set;

<select id="selectStudentByConditionTrim" resultType="com.mybatis.beans.Student">
		select id,name,age,score
		from student
    	<!--设置前缀是where标签,并且删除前面多余的and-->
		<trim prefix="WHERE" prefixOverrides="and || or">
			<if test="name != null and name != ''">
				and name like '%' #{name} '%' 
			</if>
			<if test="age > 0 ">
				and age > #{age} 
			</if>
		</trim>
	</select>
<update id="updateStudentByConditonTrim" parameterType="Student">
		update student
    	<!--设置前缀是set,后缀式where id = #{id},并且删除后缀多余的逗号-->
		<trim prefix="set" suffixOverrides="," suffix="where id = #{id}">
			<if test="name != null and name != ''">
				name = #{name},
			</if>
			<if test="age > 0">
				age = #{age},
			</if>
			<if test="score > 0">
				score = #{score},
			</if>
		</trim>
	</update>

4.<choose>标签

​ choose标签的用法和JSTL里的一样,其作用相当于switch,其内部有两个标签<when>和<otherwise>,一个choose中可以有多个when标签,但是只能有一个otherwise标签。

例,要求实现以下功能:如果输入的name不为空就按名称搜索,如果name为空就按年龄查询,如果两个都为空时就不返回任何记录;

	<select id="selectStudentByConditionChoose" resultType="com.mybatis.beans.Student">
		select id,name,age,score
		from student
		<where>
			<choose>
				<when test="name != null and name != ''">
					and name like '%' #{name} '%'
				</when>
				<when test="age > 0">
					and age > #{age}
				</when>
				<otherwise>
					and 1=2
				</otherwise>
			</choose>
		</where>
	</select>

5.<foreach>标签

​ 在MyBatis中支持使用foreach对传入的参数列表或者数组进行遍历;但是需要注意其collection关键字;

常见使用场景:通过多个id获取数据,批量插入数据等;

传入参数类型 collection关键字
List list
Array array
Map(将以上两种数据手动封装到map中) map中对应数据的key
<select id="selectStudentByConditionForeachList" resultType="com.mybatis.beans.Student">
		select id,name,age,score
		from student
		where id in 
		<foreach collection="list" item="id" open="(" separator="," close=")">
			#{id}
		</foreach>
	</select>
<select id="selectStudentByConditionForeachArray" resultType="com.mybatis.beans.Student">
		select id,name,age,score
		from student
		where id in 
		<foreach collection="array" item="id" open="(" separator="," close=")">
			#{id}
		</foreach>
	</select>
<select id="selectStudentByConditionForeachMap" resultType="com.mybatis.beans.Student">
		select id,name,age,score
		from student
		where id in 
    	<!--这里使用的myList就是我们将一个List<T>数据封装到map中的key-->
		<foreach collection="myList" item="id" open="(" separator="," close=")">
			#{id}
		</foreach>
	</select>
	Map<String , Object> map = new HashMap<String , Object>();
		List<Integer> ids = new ArrayList<Integer>();
		ids.add(1);
		ids.add(20);
		ids.add(21);
		//将一个List封装到map中,在MyBatis中的collection属性就为当前ids的key:myList
		map.put("myList", ids);

6.<sql>标签

sql标签适用于所有的SQL语句,其作用是执行字符串的替换,与include标签配套使用;

	<sql id="columns">
		id , name , age , score
	</sql>
	<select id="selectStudentByConditionIf" resultType="com.mybatis.beans.Student">
		select <include refid="columns"/>
		from student
		where 1=1
		<if test="name != null and name != ''">
			and name like '%' #{name} '%'
		</if>
		<if test="age > 0 ">
			and age > #{age}
		</if>
	</select>