MyBatis动态SQL

MyBatis能够在Mapper XML文件中使用<if><choose>等标签动态拼接SQL语句,这一功能非常灵活,我们可以通过不同的参数组合动态控制最终生成的SQL语句。实际上,动态拼接SQL不代表我们应该把软件的业务逻辑搬到XML配置文件中,这个功能只是为了让SQL能更好的完成每一个持久层的数据操作任务,因此动态SQL一般有固定的几个应用场景,比如动态查询、IN查询、批量SQL语句拼接等。XML配置文件中拼接SQL可读性极差而且完全无法debug,因此不要滥用这个功能。

下面我们简单介绍MyBatis的动态SQL标签的用法。

环境准备

这里我们将以例子的形式介绍动态SQL的写法,使用的数据库表和Java实体类如下。

CREATE TABLE t_user (
    user_id BIGINT AUTO_INCREMENT,
    username VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL,
    PRIMARY KEY (user_id)
);
@Data
public class User {
    private Long userId;
    private String username;
    private String email;
    private String password;
}

if

下面例子中,我们使用<if>实现基于多个动态字段的动态查询。

List<User> selectUserByUsernameAndEmail(@Param("username") String username, @Param("email") String email);
<select id="selectUserByUsernameAndEmail" resultType="com.gacfox.demo.model.User">
  SELECT user_id,username,email,password FROM t_user
  WHERE 1=1
  <if test="username != null and username != ''">
    AND username LIKE CONCAT('%',#{username},'%')
  </if>
  <if test="email != null and email != ''">
    AND email LIKE CONCAT('%',#{email},'%')
  </if>
</select>

<if>标签必须有test属性,其中的内容实际上是OGNL表达式(在Struts2章节我们曾介绍过),根据表达式结果为truefalse判断该分支是否拼接到SQL上。

注意:在SQL中包含一个奇怪的WHERE 1=1写法,这是因为每个匹配条件都是AND开头的,我们添加了WHERE 1=1作为WHERE子句的起始语句,这样拼接后的SQL不至于语法出错。然而在MyBatis中,WHERE 1=1不是好的写法,这种情况推荐使用<where>标签代替(后文将会介绍),这里只是出于演示目的这样编写的,实际开发中我们可能遇到别人写出类似写法,但我们自己不要这样写。

choose

if只能实现一个分支判断,choose可以实现多个,类似Java中的Switch...Case...语句。

<choose>
  <when test=""></when>
  <otherwise></otherwise>
</choose>

iftest属性一样,whentest也是OGNL表达式,此外这里when标签可以写多个。

where

这里再次使用之前<if>的例子,将其使用<where>进行改进。

<select id="selectUserByUsernameAndEmail" resultType="com.gacfox.demo.model.User">
  SELECT user_id,username,email,password FROM t_user
  <where>
    <if test="username != null and username != ''">
      AND username LIKE CONCAT('%',#{username},'%')
    </if>
    <if test="email != null and email != ''">
      AND email LIKE CONCAT('%',#{email},'%')
    </if>
  </where>
</select>

<where>标签就是配合动态拼接SQL条件子句使用的。<where>内部包裹的元素有返回值时,在SQL中将加上一个WHERE子句,否则不加;如果内部包裹的第一个条件元素以ANDOR开头,则会自动将其去掉,避免SQL语法错误。

set

<set>标签是配合UPDATE语句使用的。这个标签类比where标签理解即可,如果set包裹的元素有返回值时,就插入一个set;如果内部包裹元素最后以逗号,结尾,将逗号去掉。

trim

实际上面介绍的whereset标签都可以用trim标签实现,trim是一个更加通用的用于辅助动态拼接SQL的标签,下面例子使用<trim>实现了和<set>相同的功能。

<trim prefix="SET" suffixOverrides=","></trim>
  • prefixtrim内部包含内容时,会加上前缀
  • prefixOverridestrim内部包含内容时,会去掉该前缀
  • suffixtrim内部包含内容时,会加上后缀
  • suffixOverridestrim内部包含内容时,会去掉该后缀

foreach

<foreach>标签用于对实现了Iterable接口的Java对象进行遍历,下面例子中我们迭代了一个列表对象,它用于生成一个IN子句的范围。

<foreach collection="idList" open="(" close= ")" separator= "," item="id" index="i">
  #{id}
</foreach>
  • collection:要迭代的属性名
  • open:整个的前缀
  • close:整个的后缀
  • separator:分隔符
  • item:迭代的变量名
  • indexList是当前的索引值,Map是当前的键值

bind

bind标签用于创建一个值并绑定到变量上,比较少用。

<bind name="" value="" />
  • name:创建的变量名
  • value:创建的变量值
作者:Gacfox
版权声明:本网站为非盈利性质,文章如非特殊说明均为原创,版权遵循知识共享协议CC BY-NC-ND 4.0进行授权,转载必须署名,禁止用于商业目的或演绎修改后转载。