一:动态SQL语句
动态SQL可以说是Mybatis的一大特点。在使用过JDBC来写SQL语句,你应该能理解不同的条件有不同的SQL语句要拼接,往往一个字符串被东拼西凑,特别痛苦,而且还得保证拼接的时候2段SQL片段之间要保留空格等待,利用动态SQL可以保证彻底摆脱这种痛苦!
其实使用动态SQL也不是一件容易的事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。
如果有之前在学web的时候接触过JSTL或者任何基于类XML语言的文本处理器,,或许你对动态SQL有一定的似曾相识感觉,在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。
Mybatis中用于实现动态SQL元素主要有if、choose、trim、where、set、foreach
1:if标签
动态 SQL 通常要做的事情是有条件地包含 where 子句的一部分。简单说传来的值用if判断,有的话就拼接到where后面。
<!--根据性别和年龄查询--> <!--注意:必须要带一个where 1=1 否则sql语句少了where语句,,if判断某个值为空就不会拼接某个片段 --> <select id="findByAgeAndSex" parameterType="student" resultMap="studentMapper"> select * from student where 1=1 <if test="age!=0"> and sage=#{age} </if> <if test="sex!=null and sex!=''"> and ssex=#{sex} </if> </select>
2:choose、when、otherwise标签
这3个标签是一个整体和JSTL的一样,它们的本质都有点像java的switch分支语句,还是按照上面的例子,假设我传来年龄就按照年龄查询,传来性别就按照性别查询,年龄和性别都传来,就按照标签判断的先后顺序选择一个靠前的,然后下一个判断将不在执行,直接跳出,如果都不传则执行otherwise标签
<!--根据性别和年龄的其中一个查询--> <select id="findByAgeAndSexChoose" parameterType="student" resultMap="studentMapper"> <!--基本sql语句--> select * from student where 1=1 <!--switch开始--> <choose> <!--case :--> <when test="age!=0"> and sage=#{age} </when> <!--case :--> <when test="sex!=null and sex!=''"> and ssex=#{sex} </when> <!--default 如果都不成立则执行下面查询5条数据--> <otherwise> limit 5 </otherwise> </choose> </select>
3:where、set、trim标签
先说一下where,其实大家有没有发现,我前2个示例都有where 1=1,这是干嘛用的呢?其实这个是一个恒等式,加where 1=1和不加都一样,但是加上了,后面的拼接语句就可以使用 and xx=xx and xxx=xxx 如果没加,那么后面的if判断成立就会形成一个select * from student where and sage=xx;大家肯定发现错误了,其实Mybatis帮我们解决了where 1=1的拼接问题,我们可以不用自己手动写where属性了,我就改造一下第一个例子
<!--根据性别和年龄查询--> <!--加上where标签后就不用写恒等式了,就连前面的and也可写可不写 看下面的2个if里面--> <select id="findByAgeAndSex" parameterType="student" resultMap="studentMapper"> select * from student <where> <if test="age!=0"> sage=#{age} </if> <if test="sex!=null and sex!=''"> and ssex=#{sex} </if> </where> </select>
现在我们来聊聊trim,这个东西第一眼看上去特别难理解,以至于我都不知道是啥玩意,但是明白以后还是感觉不错的,就是用的很少,先不废话直接上代码看看
<!-- 使用trim标签完成where案例 trim标签有4个属性: prefix:前缀覆盖并增加其内容。也就是给中的sql语句加上前缀; suffix:后缀覆盖并增加其内容。给包裹的sql语句加上后缀; prefixOverrides:前缀判断的条件。取消指定的前缀,如where; suffixOverrides:后缀判断的条件。取消指定的后缀,如"and |or "逗号等。 --> <select id="findByAgeAndSex" parameterType="student" resultMap="studentMapper"> select * from student <trim prefix="where" prefixOverrides="or |and "> <if test="age!=0"> and sage=#{age} </if> <if test="sex!=null"> and ssex=#{sex} </if> </trim> </select> <!-- prefix代表前缀,它包裹着2个if标签,假设2个if都成立则语句是这样的 select * from student where and sage=#{age} and ssex=#{sex}; 这样的语句是不是有问题呢?仔细一看 where后面有个and,没错因为if判断成功, 就会拼接一个sql片段 and 也被拼接上了,但是别着急 prefixOverrides上场,这个代表前缀重写,prefixOverrides="and |or ",凡是前缀为 and或者or的全部重写,那重新成什么呢?别多想 说白了就是把匹配上的前缀删除了 所以我们再总结一下 : select * from student SQL语句 prefix="where" 开始添加前缀 where and sage=#{age} and ssex=#{sex} prefixOverrides="and |or "开始前缀重写 就是去除where后全部语句最前面匹配重写 where sage=#{age} and ssex=#{sex} 相对于的后缀和后缀重写也是一样的,只不过在后面 注意:"and▢|or▢"必须要有空格 否则匹配不上 -->
聊完trim我们就来解决最后一个set了,这个玩意比trim容易,虽然trim难点,但是它可以写出set和where的功能,现在我来介绍一下set
<!--更新学生姓名 地址--> <!--这个set标签的内部可以放更新的字段 可以自动去除尾部的逗号,--> <update id="update" parameterType="student"> update student <set> <if test="name!=null and name!=''">sname=#{name},</if> <if test="address!=null and address!=''">saddress=#{address},</if> </set> where sid=#{id} </update> <!-- 其实trim也可以完成对上面的操作 update student <trim prefix="set" suffixOverides=","> <if test="name!=null and name!=''">sname=#{name},</if> <if test="address!=null and address!=''">saddress=#{address},</if> </trim> where sid=#{id} -->
4:foreach标签
动态SQL的另一种常见的使用场景就是对集合进行遍历(尤其是遍历in条件语句的内部),在这我来给大家介绍一个mybatis的foreach循环集合标签,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
<!--查询包含的id--> <!-- open:整个循环内容开头的字符串。 close:整个循环内容结尾的字符串。 separator:每次循环的分隔符。 item:从迭代对象中取出的每一个值。 index:如果参数为集合或者数组,该值为当前索引值,如果参数为Map类型时,该值为Map的key。 collection:要迭代循环的属性名。 List<Student> findContionId(List<Integer> ids);这个是我接口的名称 大家好奇为什么collection值是list而不上ids呢?在这里就有明确的答案引用自别处博客--> <select id="findContionId" parameterType="Integer" resultMap="studentMapper"> select * from student <where> <foreach collection="list" item="x" open="sid in (" close=")" separator=","> #{x} </foreach> </where> </select>