在mapper配置文件中,有时需要根据查询条件选择不同的SQL语句,或者将一些使用频率高的SQL语句单独配置,在需要使用的地方引用。Mybatis的一个特性:动态SQL,来解决这个问题。
mybatis动态sql语句是基于OGNL表达式的,主要有以下几类:
-
if 语句 (简单的条件判断)
-
choose (when,otherwize) ,相当于java 语言中的 switch ,与 jstl 中的choose 很类似
-
trim (对包含的内容加上 prefix,或者 suffix 等,前缀,后缀)
-
where (主要是用来简化sql语句中where条件判断的,能智能的处理 and or ,不必担心多余导致语法错误)、
-
set (主要用于更新时)
-
foreach (在实现 mybatis in 语句查询时特别有用)
if标签语句
if标签用来实现根据条件拼接sql语句,下面示例用来判断参数如果不为null,则拼接sql
示例:
<select id="ifTest" resultType="com.sl.po.Product">
select * from products where
<if test="ProductName!=null">
name like #{ProductName}
</if>
<if test="description!=null">
and description like CONCAT(CONCAT('%', #{Description, jdbcType=VARCHAR}),'%')
</if>
</select>
当参数ProductName和Description不为null,则正常拼接处sql语句:select * from products where name like ? and description like CONCAT(CONCAT(’%’, ?),’%’)
但是上面的SQL中如果传入的参数ProductName为null,则解析出错误的语句:select * from products where and description like CONCAT(CONCAT(’%’, ?),’%’) ,解决这个问题,需要用到where标签
where标签语句
当 where 中的条件使用的 if 标签较多时,这样的组合可能会导致错误, “where”标签会自动判断如果它包含的标签中有返回值的话,就在sql中插入一个‘where’,如果where标签最后返回的内容是以 and 或者or 开头的,也会被自动移除掉。
上面的示例用where标签改写后示例如下:
<select id="whereTest" resultType="com.sl.po.Product">
select * from products
<!-- where标签自动移除第一个and-->
<where>
<if test="Name!=null">
and name like #{Name}
<!--name like #{Name}-->
</if>
<if test="description!=null">
and description like #{Description}
</if>
</where>
</select>
set标签语句
set 标签是用在更新操作的时候,功能和 where 标签元素差不多,主要是在包含的语句前输出一个 set,然后如果包含的语句是以逗号结束的话将会把该逗号忽略,如果 set 标签最终返回的内容为空的话则可能会出错(update table where id=1)
使用set标签示例:
<!-- if + set 实现按条件更新-->
<update id="setTest">
update products
<!-- set标签将移除最后一个“,” -->
<set>
<if test="cityCode!=null">
citycode = #{cityCode} ,
</if>
<if test="Name!=null">
name = #{Name} ,
</if>
<if test="description!=null">
description = #{Description} ,
</if>
</set>
where id =#{id}
</update>
trim标签语句
trim 元素的主要功能是可以在自己包含的内容前加上某些前缀,也可以在其后加上某些后缀,与之对应的属性是 prefix 和 suffix;可以把包含内容的首部某些内容覆盖,即忽略,也可以把尾部的某些内容覆盖,对应的属性是 prefixOverrides 和 suffixOverrides;正因为 trim 有这样的功能,它可以用来实现 where 和 set 的效果。
前面where标签示例,此处使用trim代替:
<!-- if+trim 使用trim代理where-->
<select id="trimwhereTest" resultType="com.sl.po.Product">
select * from products
<!--
<where>
<if test="Name!=null">
and name like #{Name}
<!--name like #{Name}-->
</if>
<if test="description!=null">
and description like #{Description}
</if>
</where>
-->
<!-- 移除首部所有指定在 prefixOverrides 属性中的内容,并且插入 prefix 属性中指定的内容-->
<trim prefix="WHERE" prefixOverrides="AND |OR">
<if test="Name!=null">
and name like #{Name}
</if>
<if test="description!=null">
and description like #{Description}
</if>
</trim>
</select>
前面set标签示例,此处使用trim代替:
<!--if+trim 代替 使用trime代替set -->
<update id="trimsetTest">
update products
<!--
<set>
<if test="cityCode!=null">
citycode = #{cityCode} ,
</if>
<if test="Name!=null">
name = #{Name} ,
</if>
<if test="description!=null">
description = #{Description}
</if>
</set>
-->
<!-- 移除尾部所有指定在 suffixOverrides 属性中的内容,并且插入 prefix 属性中指定的内容-->
<trim prefix="set" suffixOverrides=",">
<if test="cityCode!=null and cityCode!=''">
citycode = #{cityCode} ,
</if>
<if test="Name!=null">
name = #{Name} ,
</if>
<if test="description!=null">
description = #{Description}
</if>
</trim>
where id=#{id}
</update>
choose (when, otherwise)标签
choose标签是按顺序判断其内部when标签中的test条件出否成立,如果有一个成立,则 choose 结束。当 choose 中所有 when 的条件都不满则时,则执行 otherwise 中的sql,类似于sql server语句(case when then)
示例:
<!-- choose + when + otherwise 只能选择一个作为查询条件 作用类似sql case when then -->
<select id="choosewhenotherwiseTest" resultType="com.sl.po.Product">
select * from products
<where>
<choose>
<when test="name!=null">
and name like #{Name}
</when>
<when test="description!=null">
and description like #{Description}
</when>
<otherwise>
and unitprice > #{UnitPrice}
</otherwise>
</choose>
</where>
</select>
如果name!=null,则解析出sql: select * from product where name like ?
Name==null&& description!=null,则解析出sql: select * from product where description like ?
否则:select * from product where unitprice >?
foreach标签语句
mybatis提供foreach标签,用来对一个集合进行遍历,通常是用来构建 IN 条件语句,也可用于其他情况下动态拼接sql语句。
foreach标签有以下几个属性collection, item,index,open,separator,close。
-
collection表示需要遍历的集合
-
item 表示每次遍历时生成的对象名
-
index表示在迭代过程中,每次迭代到的位置)
-
open表示开始遍历时要拼接的字符串
-
separator表示在每次遍历时两个对象直接的连接字符串
-
close表示结束遍历时要拼接的字符串
当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
在使用foreach的时候针对不同的参数类型, collection属性值要分为以下3种情况:
1.如果传入的是单参数且参数类型是一个List的时候,collection属性值为list
2.如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array
3.如果传入的参数是多个的时候,我们就需要把它们封装成一个Map或者Object。
示例:
<!-- 只有一个List参数时它的参数名为list,即collection="list" ; 如果参数类型时数组object[],则 collection="array" -->
<select id="foreachTest" resultType="com.sl.po.Product">
select * from products
<where>
<if test="list!=null">
<foreach item="id" index="index" collection="list" open="id in(" separator="," close=")">#{id}</foreach>
</if>
</where>
</select>
<!-- 通过pojo传递list, collection值为pojo中对应的属性名-->
<select id="foreachVoTest" resultType="com.sl.po.Product">
select * from products
<where>
<if test="name!=null"> and name like #{name} </if>
<if test="ids!=null">
<foreach item="item" index="index" collection="ids" open="and id in(" separator="," close=")">#{item}</foreach>
</if>
</where>
</select>
测试代码:
//@Test
public void testforeachTest() {
String statement = "com.sl.mapper.ProductMapper.foreachTest";
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
List<Product> listProduct = session.selectList(statement, list);
for (Product pro : listProduct) {
System.out.println(pro);
}
// 关闭会话
session.close();
}
//@Test
public void testforeachVoTest() {
String statement = "com.sl.mapper.ProductMapper.foreachVoTest";
ProductVo2 vo2 = new ProductVo2();
vo2.setName("%国际%");
List<Integer> ids = new ArrayList<Integer>();
ids.add(11);
ids.add(12);
ids.add(13);
vo2.setIds(ids);
List<Product> listProduct = session.selectList(statement, vo2);
for (Product pro : listProduct) {
System.out.println(pro);
}
// 关闭会话
session.close();
}
package com.sl.po;
import java.util.List;
public class ProductVo2 {
private String name;
private List<Integer> ids;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
}
Sql片段
Sql中可将重复的sql提取出来,使用时用include引用即可,最终达到sql重用的目的
示例:
<select id="sqlTest" resultType="com.sl.po.Product">
select * from products
<where>
<!-- 引用sql片段 -->
<include refid="sqltemp"/>
<!-- 提取sql片段
<if test="cityCode!=null">
and citycode = #{cityCode}
</if>
<if test="Name!=null">
and name like #{Name}
</if>
<if test="description!=null">
and description like #{Description}
</if>
-->
</where>
</select>
<!-- 定义sql片段 :将where条件提取 -->
<sql id="sqltemp">
<if test="cityCode!=null">
and citycode = #{cityCode}
</if>
<if test="Name!=null">
and name like #{Name}
</if>
<if test="description!=null">
and description like #{Description}
</if>
</sql>