Mybatis由浅入深 - 03动态SQL

2022-12-02 15:22:25 浏览数 (1)

这是Mybatis由浅入深的第3节,前两节传送门:

【Mybatis由浅入深 -01入门】

【Mybatis由浅入深 - 02增删改查CRUD】

前言

动态SQL是Mybatis的亮点功能之一,解决了根据不同条件拼接SQL的繁琐痛苦问题,避免了java代码和sql混在一起,是非常常用的功能,也是本人非常喜欢的功能,点赞!

通过本文您将掌握xml映射文件的常用元素:

  1. if
  2. choose (when, otherwise)
  3. trim(where, set)
  4. foreach

使用环境

以下为基础前置条件:请参考其它博文自行安装。

  • JDK:1.8
  • Maven: 3.6.3
  • Mysql: 5.7 安装教程

创建Mysql测试表

代码语言:javascript复制
CREATE TABLE `user`  (
  `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `age` tinyint(1) UNSIGNED NOT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `user`(`id`, `name`, `age`) VALUES (1, '天罡gg', 18);
INSERT INTO `user`(`id`, `name`, `age`) VALUES (2, '天罡666', 28);
INSERT INTO `user`(`id`, `name`, `age`) VALUES (3, '666天罡', 38);
INSERT INTO `user`(`id`, `name`, `age`) VALUES (4, '天罡666', 48);

1、if

和java代码的if非常类似, 如果if test成立时执行if标签内的sql,用于各种条件判断:像拼接字段名、拼接where条件等等,最常见情景是根据条件包含 where 子句的一部分

代码语言:javascript复制
	// 查询user,如果name不为空则按name查询(全模糊)
	List<User> selectListIfName(String name);
代码语言:javascript复制
	<select id="selectListIfName" resultType="com.tiangang.dao.po.User">
        select * from user
        <if test="name != null and name != ''">
            where name like concat ('%', #{name},'%')
        </if>
    </select>

当传入的name=null时,最终执行的sql为:

代码语言:javascript复制
select * from user

当传入的name="666"时,最终执行的sql为:

代码语言:javascript复制
select * from user where name like 'f6%'

单元测试如下:

代码语言:javascript复制
	@Test
    public void selectListIfNameTest() {
        MyBatisManager.executeMapperMethod(true, UserDynamicSqlMapper.class, (mapper -> {
            List<User> userList = mapper.selectListIfName("666");
            System.out.println("1.查询name like 'f6%':n"   userList);

            userList = mapper.selectListIfName(null);
            System.out.println("2.查询全部:n"   userList);
        }));
    }

执行结果如下图:

2、choose (when, otherwise)

类似java的swicth case。choose标签下,任意whentest成立时执行when标签内的sql,choose结束; 当都不成立时,执行otherwise标签内的sql。

代码语言:javascript复制
	// 查询user,如果name不为空则按name查询(通过symbol选择是)
    List<User> selectListChooseName(@Param("name") String name, @Param("symbol") Integer symbol);
代码语言:javascript复制
	<select id="selectListChooseName" resultType="com.tiangang.dao.po.User">
        select * from user
        <if test="name != null and name != ''">
            where
            <choose>
                <when test="symbol == null">
                    name = #{name}
                </when>
                <when test="symbol==1">
                    name like concat ('%', #{name})
                </when>
                <when test="symbol==2">
                    name like concat (#{name},'%')
                </when>
                <otherwise>
                    name like concat ('%', #{name},'%')
                </otherwise>
            </choose>
        </if>
    </select>

单元测试如下:

代码语言:javascript复制
	@Test
    public void selectListChooseNameTest() {
        MyBatisManager.executeMapperMethod(true, UserDynamicSqlMapper.class, (mapper -> {
            List<User> userList = mapper.selectListChooseName("天罡", 1);
            System.out.println("1.查询name like '%天罡': n"   userList);

            userList = mapper.selectListChooseName("天罡", 2);
            System.out.println("2.查询name like '天罡%': n"   userList);

            userList = mapper.selectListChooseName("天罡", 3);
            System.out.println("3.查询name like '%天罡%': n"   userList);

            userList = mapper.selectListChooseName("天罡666", null);
            System.out.println("4.查询name='天罡666': n"   userList);

            userList = mapper.selectListChooseName("", null);
            System.out.println("5.查询全部: n"   userList);
        }));
    }

执行结果如下图:

3、trim(where, set)

前面几个例子已经让动态sql变的方便了,但是还不够!现在回到之前的 “if” 示例,这次我们同时name和age两个条件,看看会发生什么。

代码语言:javascript复制
<select id="selectListWhereNameAgeError" resultType="com.tiangang.dao.po.User">
    select * from user
    where
    <if test="name != null and name != ''">
        name like concat ('%', #{name},'%')
    </if>
    <if test="gleAge!= null">
        and age >= #{gleAge}
    </if>
</select>

如果没有匹配的条件会怎么样?最终这条 SQL 会变成这样:

代码语言:javascript复制
select * from user
where

这会导致查询失败。如果匹配的只是第二个条件又会怎样?这条 SQL 会是这样:

代码语言:javascript复制
select * from user
where
and age >= 18

这个查询也会失败。所以这里mybatis提供了非常好用的where元素, 示例:

代码语言:javascript复制
<select id="selectListWhereNameAge" resultType="com.tiangang.dao.po.User">
     select * from user
     <where>
         <if test="name != null and name != ''">
             and name like concat ('%', #{name},'%')
         </if>
         <if test="gleAge!= null">
             and age >= #{gleAge}
         </if>
     </where>
</select>

完美解决了这两个问题:

1. 当子元素不返回时, 不会插入 “WHERE” 子句

2. 若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

如果 where 元素与你期望的不太一样,你也可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

代码语言:javascript复制
	<trim prefix="WHERE" prefixOverrides="AND |OR ">
	  ...
	</trim>

prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。

另一个与where类似的元素是set,set 元素可以用于动态包含需要更新的列,忽略其它不更新的列。比如:

代码语言:javascript复制
    <update id="updateSelectiveById" parameterType="com.tiangang.dao.po.User">
        update user
        <set>
            <if test="name != null"> name = #{name}, </if>
            <if test="age != null"> age = #{age} </if>
        </set>
        where id = #{id}
    </update>

这个例子中,set元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号。

或者,你可以通过使用trim元素来达到同样的效果:

代码语言:javascript复制
	<trim prefix="SET" suffixOverrides=",">
	  ...
	</trim>

4、foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:

代码语言:javascript复制
    // 查询user,根据idList
    List<User> selectByIdList(@Param("idList") List<Integer> idList);
代码语言:javascript复制
<select id="selectByIdList" resultType="com.tiangang.dao.po.User">
    select * from user
    <if test="idList !=null and idList.size >0">
        where id in
        <foreach collection="idList" item="id" separator="," open="(" close=")" >
            #{id}
        </foreach>
    </if>
</select>

foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及集合项迭代之间的分隔符。这个元素也不会错误地添加多余的分隔符,看它多智能!

你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值

5、本文全部源码

https://download.csdn.net/download/scm_2008/86512089

0 人点赞