MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
虽然在以前使用动态 SQL 并非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射语句中的强大的动态 SQL 语言得以改进这种情形。
动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多元素需要花时间了解。MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。
本测试使用的数据库是book表:
注:创建maven项目、导包过程不做介绍。
- if标签
- choose标签
- set标签
- trim标签
- SQL片段
- foreach标签
一、if标签。
动态SQL通常要做的事情是有条件地包含where子句的一部分。所以在MyBatis中,元素是最常用的元素。它类似于Java中的if语句。
if标签用来实现根据条件拼接sql语句,下面示例用来判断参数如果不为null,则拼接sql。
下面来测试元素,具体过程如下:
- 添加SQL映射语句
- 添加数据操作接口方法
- 调用数据操作接口方法
- 测试动态SQL语句
1、在BookMapper.xml文件中,添加如下的SQL映射语句。
<!-- 查找书籍-->
<select id="queryBookIF" parameterType="map" resultType="com.wst.pojo.Book">
select * from book where 1=1
<if test="bookname!=null">
and bookname=#{bookname}
</if>
<if test="type!=null">
and type=#{type}
</if>
</select>
2、在BookMapper接口中,添加如下数据操作接口的方法。
//查询书籍
List<Book> queryBookIF(Map map);
3、编写MyTest的测试类及操作接口的方法。
public void queryBookIF(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
BookMapper mapper = sqlSession.getMapper(BookMapper.class);
HashMap map = new HashMap();
map.put("bookname","java");
map.put("type","1");
List<Book> books = mapper.queryBookIF(map);
for (Book book : books) {
System.out.println(book);
}
sqlSession.close();
}
4、运行进行测试。
注意:当但是上面的SQL语句中如果传入的参数bookname为NULL的时候,则解析出错误的语句:select * from book where and type=#{type},看到这个SQL语句明显的就知道了语句的错误,为了解决这个问题,这里引入了where标签。
当 bookname 值为 null 时,查询语句会出现 “where and” 的情况,解决该情况除了将"where"改为“where 1=1”之外,还可以利用 where标签。这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以 AND 或 OR 开头的,则它会自动的去掉。
于是改写了上面的例子,增加了where标签如下:
<!-- 查找书籍-->
<select id="queryBookIF" parameterType="map" resultType="com.wst.pojo.Book">
select * from book
<where>
<if test="bookname!=null">
and bookname=#{bookname}
</if>
<if test="type!=null">
and type=#{type}
</if>
</where>
</select>
二、choose标签 (when, otherwise)
有些时候,不想用到所有的条件语句,而只想从中择其一二。针对这种情况,MyBatis 提供了choose元素,它有点像Java中的 switch 语句。
- 该标签类似于 Java 中的 switch、case、default。
- choose标签是按顺序判断其内部when标签中的test条件出否成立,如果有一个成立,则 choose 结束。
- 当 choose 中所有 when 的条件都不满则时,则执行 otherwise 中的sql语句。
示例:
数据库接口:
List<Book> queryBookChoose(Map map);
SQL映射语句:
<!-- Choose的使用-->
<select id="queryBookChoose" parameterType="map" resultType="com.wst.pojo.Book">
select * from book
<where>
<choose>
<when test="bookname != null">
and bookname=#{bookname}
</when>
<when test="type!=null">
and type = #{type}
</when>
<otherwise>
and count = #{count}
</otherwise>
</choose>
</where>
</select>
注:若我们同时添加bookname和type的值,最终的SQL也只会添加第一个属性值。
map.put("bookname","java");
map.put("type","1");
则只会添加第一个属性的值bookname!
三、set标签。
在动态update语句中,可以使用元素动态的更新列。set元素主要是用在更新操作的时候,主要是在包含的语句前输出一个set,然后如果包含的语句被逗号结束的话将会把该逗号去掉,如果set包含我们的内容为空的话发生错误。有了set元素我们就可以动态的更新那些修改了的列。
数据库接口:
int updateBook(Map map);
SQL映射语句:
<!-- 更新书的信息-->
<update id="updateBook" parameterType="map">
update book
<set>
<if test="bookname != null">
bookname = #{bookname},
</if>
<if test="type != null">
type = #{type},
</if>
</set>
where id = #{id}
</update>
四、trim元素
元素的主要功能是可以在自己包含的内容前加上某些前缀,也可以在其后加上某些后缀,与之对应的属性是prefix和suffix;可以把包含内容的首部某些内容覆盖,即忽略,也可以把尾部的某些内容覆盖,对应的属性是prefixOverrides和suffixOverrides;正因为元素有这样的功能,所以也可以非常简单地利用来代替元素的功能。
trim元素有以下四个属性:
- prefix:前缀
- prefixOverrides:去掉第一个and或者是or
- suffix:后缀
- suffixOverrides:去掉最后一个逗号,也可以是其他的标记
<where>标签对应的trim实现:
<trim prefix="WHERE" prefixOverride="AND |OR ">
<set>标签对应的trim实现:
<trim prefix="SET" suffixOverrides=",">
数据库接口:
List<Book> selectBookByTrim(Map map);
SQL映射语句:
<select id="selectBookByTrim" resultType="com.wst.pojo.Book" parameterType="map">
select * from book
<trim prefix="where" prefixOverrides="and |or">
<if test="bookname !=null and bookname!=''">
and bookname like concat('%',#{bookname},'%')
</if>
<if test="type !=null and type!=''">
and type = #{type}
</if>
</trim>
</select>
五、SQL片段
在mybatis中通过使用SQL片段可以提高代码的重用性,下面是一个具体的实例:
数据库接口:
//查询书籍
List<Book> queryBookIF(Map map);
SQL映射语句:
<!-- 查找书籍-->
<sql id="select-book">
<if test="bookname!=null">
and bookname=#{bookname}
</if>
<if test="type!=null">
and type=#{type}
</if>
</sql>
<select id="queryBookIF" parameterType="map" resultType="com.wst.pojo.Book">
select * from book
<where>
<include refid="select-book"></include>
</where>
</select>
六、foreach 元素
元素主要用在构建in条件中,它可以在SQL语句中进行迭代一个集合。foreach元素的属性主要有item,index,collection,open,separator,close。
- item表示集合中每一个元素进行迭代时的别名,index指定一个名字,用于表示在迭代过程中,每次迭代到的位置。
- open表示该语句以什么开始。
- separator表示在每次进行迭代之间以什么符号作为分隔符。
- close表示以什么结束。
在使用时,最关键的也是最容易出错的是collection属性,该属性是必选的,但在不同情况下,该属性的值是不一样的,主要有以下3种情况:
如果传入的是单参数且参数类型是一个List的时候,collection属性值为list。
如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array。
如果传入的参数是多个时,需要把它们封装成一个Map,当然单参数也可以封装成Map。Map的key是参数名,collection属性值是传入的List或array对象在自己封装的Map中的key。
实例:
数据库接口:
List<Book> queryBookForeach(Map map);
SQL映射语句:
<select id="queryBookForeach" parameterType="map" resultType="com.wst.pojo.Book">
select * from book
<where>
<foreach collection="ids" item="id" open="(" close=")" separator="or">
id = #{id}
</foreach>
</where>
</select>