Mybatis动态SQL的示例

265 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 19 天,点击查看活动详情

动态SQL作用:根据不同条件拼接 SQL 语句。

IF 标签

IF 语句用来判断某条件是否符合。若条件符合则拼接该 SQL,例如下面这个 SQL中的<if test="name != null">的意思就是判断传入参数user这个对象中是否包含了name,如果包含name就会通过 if 判断,拼接SQL,否则,将不会拼接AND name like #{name}

<select id="findByCondition" parameterType="user" resultType="user">
  SELECT * FROM User tbl_user id = #{id}
  <if test="name != null">
    AND name like #{name}
  </if>
</select>

WHERE 标签

上述的SQL中:WHERE id = #{id},表示该id这个参数是必须要传入的,如果我们要将该条件也用 IF 包起来的话:

<select id="findByCondition" parameterType="user" resultType="user">
  SELECT * FROM tbl_user WHERE
  <if test="id != null">
      id = #{id}
  </if>
  <if test="name != null">
    AND name like #{name}
  </if>
</select>

如果两个条件都不满足了,就会变成这样的 SQL,SELECT * FROM User WHERE,导致 SQL 报错。

为解决问题,我们需要用到<WHERE>标签, <WHERE> 元素只会在子元素返回任何内容的情况下才插入 WHERE 。这样就避免了两个条件都不满足的情况。

<select id="findByConditions" parameterType="user" resultType="user">
    SELECT * FROM tbl_user
    <where>
        <if test="id != null">
            id = #{id}
        </if>

        <if test="name != null">
            AND name like #{name}
        </if>
    </where>
</select>

这里可能出现一个疑问:若第一个条件不满足,第二个条件满足呢?SQL 语句会变成了:SELECT * FROM User WHERE AND name like #{name},这又出现了问题,我们尝试一下:

...DEBUG er.UserMapper.findByConditions  - ==>  Preparing: SELECT * FROM tbl_user WHERE name like ? 

看官网的介绍:<WHERE>  元素只会在子元素返回任何内容的情况下才插入 WHERE 。(这个我们已经知道到了) 若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。 所以,放心的使用<where>标签吧。

TRIM 标签

该标签会提供一些属性,让你去自定义一些前缀,或者屏蔽掉某些内容。

  1. prefix :在整个标签【前面】附加指定内容。
  2. suffix :在整个标签【最后】追加指定内容。
  3. prefixOverrides :去掉【标签体内第一个指定】的关键字。
  4. suffixOverrides :去掉标【签体内最后一个指定】的关键字。

我们将上面的代码重新用<trim>标签组织一下:

<where>
    <if test="id != null">
        id = #{id}
    </if>

    <if test="name != null">
        AND name like #{name}
    </if>
</where>

组织结果如下:

<trim prefix="where" prefixOverrides="AND">
    <if test="id != null">
        id = #{id}
    </if>

    <if test="name != null">
        AND name like #{name}
    </if>
</trim>

所以该标签很强大,不光可以实现<where>同样的效果,<set>也是如此。

CHOSE

条件中我只取一个,如果传过来的都不满足,那我设置一个保底。

<select id="findAdmin" parameterType="user" resultMap="user">
    select * from tbl_user
    <choose>
        <when test="id != null and id != '">
            where id = #{id};
        </when>
        <when test="name != null and name != ''">
            where name = #{name};
        </when>
        <otherwise>
          where name = '老程'
        </otherwise>
    </choose>
</select>

该SQL中只会取一个where条件,如果前面的idname都缺省了,那么就会默认查询name为'老程'的管理员。

FOREACH

如果我的参数是一个列表,就像SQL语句的IN操作,这就需要FOREACH标签。

举个IN的例子:

首先我们定义一个接口,设定一个方法:findAllUseForeach,它的参数是传入一个数组,名字我限定使用@Param修饰,该参数在XML文件中的别名叫ids

  • Mapper接口:
List<User> findAllUseForeach(@Param("ids") List<String> ids);

在XML文件中,用parameterType标明参数是列表类型;使用<fireach>标签遍历整个数组参数,这里我们介绍一下属性的作用:

  1. collection:放传入参数(集合/数组)的名称。
  2. item:遍历(集合/数组)中每个元素,我们取个名字,例如这里,我们叫做:id
  3. open:表示要用什么符号作为foreach标签的前缀。
  4. close:表示用什么符号作为foreach标签的后缀。
  5. separator:表示中间的#{id}是使用什么分隔符隔开。
  • XML文件:
<select id="findAllUseForeach" parameterType="list" resultType="user">
    select * from tbl_user
    where id in
    <foreach collection="ids" item="id" open="(" close=")" separator=",">
        #{id}
    </foreach>
</select>
  • 测试
@Test
public void findAllUseForeach() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    List<String> list = new ArrayList<>();
    list.add("09ec5fcea620c168936deee53a9cdcfb");
    list.add("09ec5fcea620c168936deee53a9cdcff");
    List<User> allUseForeach = mapper.findAllUseForeach(list);
    for (User useForeach : allUseForeach) {
        System.out.println(useForeach);
    }
}

这里再给出一个,单条的批量修改的SQL(传入的参数是一个:类转的Map),请自行学习其中的奥秘,关键信息已用注解标注

<!--键值对的 key 是 index ,value 是 item-->
<!--${key} 是引用属性,不能用#{},不然会以?占位,这里我们需要的是name = ?-->
<update id="updateUserByMap" parameterType="map">
    update tbl_user
    <foreach collection="beanMap" index="key" item="value" open="set " separator=",">
        <if test="value != null">
            ${key} = #{value}
        </if>
    </foreach>
    where id = #{id}
</update>

它的测试语句如下:

@Test
public void updateUserByMap() {
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = new User();
    user.setName("老四");
    BeanMap beanMap = BeanMap.create(user);
    System.out.println(beanMap);
    // {birthday=null, name=老四, id=null, department=null, version=null, age=null}

    Map<String, Object> departmentMap = new HashMap<>(2);
    departmentMap.put("id", "09ec5fcea620c168936deee53a9cdcff");
    departmentMap.put("beanMap", beanMap);
    mapper.updateUserByMap(departmentMap);
    sqlSession.commit();
}

BIND

bind 元素允许你在 SQL 表达式以外创建一个变量,并将其绑定到当前的上下文。比如:

<!--bind类似声明一个变量-->
<select id="findAllUserUseBind" parameterType="user" resultType="user">
    <bind name="namelike" value="'%' + _parameter.getName() + '%'"/>
    select * from tbl_user
    where name like #{namelike}
</select>

通常用来简化处理模糊查询的%拼接工作。

SET

SET标签非常简单,就是<set><if>的拼接,也可以用<trim>替换相同的功能,就不展开介绍了,尝试一下是否掌握了吧~