mybatis复习03,动态SQL,if,choose,where,set,trim标签及foreach标签的用法

简介: 文章介绍了MyBatis中动态SQL的用法,包括if、choose、where、set和trim标签,以及foreach标签的详细使用。通过实际代码示例,展示了如何根据条件动态构建查询、更新和批量插入操作的SQL语句。

动态SQL

mybatis的强大特性之一就是它的动态SQL。以下是mybatis的动态SQL在XML中支持的几种标签:

初始的数据库表

在这里插入图片描述

if标签

if标签的结构

<if test="判断条件为true|false">
    sql语句块
</if>

if标签中,test属性值为必填,其值为判断条件为真或者假,然后标签中就是sql语句。

根据输入条件动态查询

现在有一个需求,就是根据用户的输入不同来进行不同的条件查询:
用户只输入用户名时,根据用户名进行查询(模糊查询),当用户输入地址时,根据地址去匹配查询,同时输入用户名和地址时进行查询对应匹配的用户。
接着我们来实现,上面的需求,首先在mapper接口中编写对应的方法:

     /**
     * 多条件查询,支持用户的输入不同,查询出不同的信息
     * @param userInfo
     * @return
     */
    List<UserInfo> selectByUserInfo(UserInfo userInfo);

然后在Mapper接口对应的xml文件中,编写相应的sql:
这里使用了resultMap标签作为返回值映射,也可以使用resultType标签,就不多赘述。

    <resultMap id="selectByUserInfo" type="UserInfo">
        <id property="id" column="id"></id>
        <result property="userName" column="user_name"></result>
        <result property="password" column="password"></result>
        <result property="userSex" column="user_sex"></result>
        <result property="userAddress" column="user_address"></result>
    </resultMap>
    <!--List<UserInfo> selectByUserInfo(UserInfo userInfo);-->
    <select id="selectByUserInfo" resultMap="selectByUserInfo">
        select id,
               user_name,
               password,
               user_sex,
               user_address
        from user_info
        where  1=1
        <if test="userName != null and userName !=''">
            and user_name like concat('%',#{userName},'%')
        </if>
        <if test="userAddress != null and userAddress !=''">
            and user_address = #{userAddress}
        </if>
    </select>

观察完代码,注意到 where 1=1 ,这个的作用是,即使所有的if都不满足,也会执行。
所有的if都不满足即 select * from user_info where 1=1,可以想想一下不在where后面加 1=1,最终的sql会变成什么样,必然会出错,而且为了匹配多条件也必须需要有这样一个条件,因为每个查询中的and,你并不确定用户最后的输入查询条件是什么。(当然,后面的trim,where标签会解决这个问题,那样更优雅)。
测试用例编写:

     @Test
    public void testSelectByUserInfo(){
   
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);
        //伪造一个用户输入,只输入名称
        UserInfo userInfo1 = new UserInfo();
        userInfo1.setUserName("白");
        List<UserInfo> userInfos1 = mapper.selectByUserInfo(userInfo1);
        userInfos1.forEach(System.out::println);
        //伪造用户输入。只输入地址
        UserInfo userInfo2 = new UserInfo();
        userInfo2.setUserAddress("东河龙王庙");
        List<UserInfo> userInfos2 = mapper.selectByUserInfo(userInfo2);
        userInfos2.forEach(System.out::println);
        //伪造用户输入,输入姓名和地址
        UserInfo userInfo3 = new UserInfo();
        userInfo3.setUserName("叶问");
        userInfo3.setUserAddress("佛山街道办事处");
        List<UserInfo> userInfos3 = mapper.selectByUserInfo(userInfo3);
        userInfos3.forEach(System.out::println);
        //伪造一个啥也不输入的用户输入,返回应该是所有的数据
        UserInfo userInfo4 = new UserInfo();
        List<UserInfo> userInfos4 = mapper.selectByUserInfo(userInfo4);
        userInfos4.forEach(System.out::println);
    }

测试用例输出:
在这里插入图片描述

动态更新中使用if

需求:选择性更新,只更新用户所输入的数据。
编写接口中的方法:

    /**
     * 根据用户输入来更新,(只为了说明功能)
     * @param userInfo
     * @return
     */
    int updateByUserInfo(UserInfo userInfo);

编写对应xml文件中的sql语句:

     <!--int updateByUserInfo(UserInfo userInfo);-->
    <update id="updateByUserInfo">
        update user_info
        set
            <if test="userName != null and userName !=''">
                user_name =#{userName},
            </if>
            <if test="userSex != null and userSex !=''">
                user_sex =#{userSex},
            </if>
            <if test="userAddress != null and userAddress !=''">
                user_address =#{userAddress},
            </if>
            id = #{id}
        where id =#{id}
    </update>

同理,这里的 id=#{id}与上一个where 1=1异曲同工之妙。同样是,因为在如果if都为假的话,不加id=#{id},sql就会是一个错误的sql,并且set中每个以逗号分隔,你也不确定哪一个条件会成立,所以,最后面还是得补id=#{id}
测试用例编写:

    @Test
    public void testUpdateUserInfos(){
   
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);

        UserInfo userInfo1 =new UserInfo();
        userInfo1.setId(2);
        mapper.updateByUserInfo(userInfo1);

        UserInfo userInfo2 =new UserInfo();
        userInfo2.setId(2);
        userInfo2.setUserName("叶问二");
        mapper.updateByUserInfo(userInfo2);

        UserInfo userInfo3 =new UserInfo();
        userInfo3.setId(2);
        userInfo3.setUserName("叶问");
        userInfo3.setUserAddress("佛山武馆");
        mapper.updateByUserInfo(userInfo3);
    }

测试用例输出:
在这里插入图片描述

choose标签

choose标签的结构

choose标签其实就是对if标签的一种补充,为了实现if…else这样的逻辑。
choose标签中包括两个子标签(when和otherwise)。一个choose标签中应至少包含一个when标签和0个或者1个otherwise标签。

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

choose用例

需求:根据用户名或者id输入来查询对应用户,如果二者都为空,则不执行任何查询。
首先,编写Mapper接口中的方法:

    /**
     * 根据用户名或者id来查询对应用户
     * @param userInfo
     * @return
     */
    UserInfo selectByIdOrUserName(UserInfo userInfo);

编写对应xml中的sql语句:

    <!--UserInfo selectByIdOrUserName(UserInfo userInfo);-->
    <select id="selectByIdOrUserName" resultType="UserInfo">
        select id id,
               user_name userName,
               password password,
               user_sex userSex,
               user_address userAddress
        from user_info
        where 1=1
        <choose>
            <when test="id !=null">
                and id =#{id}
            </when>
            <when test="userName !=null and userName !=''">
                and user_name = #{userName}
            </when>
            <otherwise>
                and 1=2
            </otherwise>
        </choose>
    </select>

看到这里,结合之前上面的代码是不是也明白了otherwise 中的 and 1=2的含义了,我就不过多解释了。
测试用例编写:

    @Test
    public void testSelectUsersByIdOrUserName(){
   
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);
        //先伪造一个数据都为空的用户
        UserInfo userInfo1 = new UserInfo();
        mapper.selectByIdOrUserName(userInfo1);//不会执行sql

        //伪造一个只含id和只含用户名的数据
        UserInfo userInfo2 = new UserInfo();
        userInfo2.setId(1);
        mapper.selectByIdOrUserName(userInfo2);

        UserInfo userInfo3 = new UserInfo();
        userInfo3.setUserName("叶问");
        mapper.selectByIdOrUserName(userInfo3);


    }

测试用例输出:
在这里插入图片描述

where、set、trim标签

where标签的结构

        <where>
            可嵌套其他标签使用
        </where>

where标签的作用就是,如果where标签中包含的元素有返回值,就在sql中插入一个where,并且如果where后面的字符串是以and或者or开头的,会自动剔除掉and和or。(这不就是上面说的完美解决,以后我们就不再需要where 1=1,这样的代码了).

where用例

就是根据用户的输入不同来进行不同的条件查询:
用户只输入用户名时,根据用户名进行查询(模糊查询),当用户输入地址时,根据地址去匹配查询,同时输入用户名和地址时进行查询对应匹配的用户。
还是上面的同一个需求,这次换个方法来实现,使用where标签。
在Mapper中编写,对应的方法(也可使用之前的方法,我为了不修改,就重新写一个):

     /**
     * 多条件查询,支持用户的输入不同,查询出不同的信息 使用where标签来实现
     * @param userInfo
     * @return
     */
    List<UserInfo> selectByUserInfoTwo(UserInfo userInfo);

编写对应xml中的sql:

     <!-- List<UserInfo> selectByUserInfoTwo(UserInfo userInfo);-->
    <select id="selectByUserInfoTwo" resultType="UserInfo">
        select id id,
                user_name userName,
                password password,
                user_sex userSex,
                user_address userAddress
        from user_info
        <where>
            <if test="userName!=null and userName!=''">
                and user_name like concat('%',#{userName},'%')
            </if>
            <if test="userAddress!=null and userAddress!=''">
                and user_address = #{userAddress}
            </if>
        </where>
    </select>

一下子感觉清爽了许多!
测试用例编写:

@Test
    public void testselectByUserInfoTwo(){
   
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);
        //伪造一个用户输入,只输入名称
        UserInfo userInfo1 = new UserInfo();
        userInfo1.setUserName("白");
        List<UserInfo> userInfos1 = mapper.selectByUserInfo(userInfo1);
        userInfos1.forEach(System.out::println);
        //伪造用户输入。只输入地址
        UserInfo userInfo2 = new UserInfo();
        userInfo2.setUserAddress("东河龙王庙");
        List<UserInfo> userInfos2 = mapper.selectByUserInfo(userInfo2);
        userInfos2.forEach(System.out::println);
        //伪造用户输入,输入姓名和地址
        UserInfo userInfo3 = new UserInfo();
        userInfo3.setUserName("叶问");
        userInfo3.setUserAddress("佛山街道办事处");
        List<UserInfo> userInfos3 = mapper.selectByUserInfo(userInfo3);
        userInfos3.forEach(System.out::println);
        //伪造一个啥也不输入的用户输入,返回应该是所有的数据
        UserInfo userInfo4 = new UserInfo();
        List<UserInfo> userInfos4 = mapper.selectByUserInfo(userInfo4);
        userInfos4.forEach(System.out::println);

    }

测试用例输出:
在这里插入图片描述

set标签的结构

         <set>
           同样也可嵌入其他标签 
        </set>

set标签的作用与where基本类似,如果set标签元素中有返回值就插入一个set,同时如果set后面的sql是以逗号,结尾的,则将这个逗号剔除。

set用例

需求:选择性更新,只更新用户所输入的数据。(和上面的if第二个例子一样,这次是为了改进)。
编写Mapper接口中的方法:

     /**
     * 根据用户输入来更新,(只为了说明功能)-->使用set标签来做
     * @param userInfo
     * @return
     */
    int updateByUserInfoTwo(UserInfo userInfo);

编写对应xml文件中的sql:

    <!-- int updateByUserInfoTwo(UserInfo userInfo);-->
    <update id="updateByUserInfoTwo">
        update user_info
        <set>
            <if test="userName != null and userName !=''">
                user_name =#{userName},
            </if>
            <if test="userSex != null and userSex !=''">
                user_sex =#{userSex},
            </if>
            <if test="userAddress != null and userAddress !=''">
                user_address =#{userAddress},
            </if>
            id = #{id},
        </set>
        where id =#{id}
    </update>

测试用例编写:

    @Test
    public void testUpdateUserInfosTwo(){
   
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);

        UserInfo userInfo1 =new UserInfo();
        userInfo1.setId(2);
        mapper.updateByUserInfoTwo(userInfo1);

        UserInfo userInfo2 =new UserInfo();
        userInfo2.setId(2);
        userInfo2.setUserName("叶问二");
        mapper.updateByUserInfoTwo(userInfo2);

        UserInfo userInfo3 =new UserInfo();
        userInfo3.setId(2);
        userInfo3.setUserName("叶问");
        userInfo3.setUserAddress("佛山武馆");
        mapper.updateByUserInfoTwo(userInfo3);
    }

测试用例输出:
在这里插入图片描述

trim标签的结构

trim标签其实就是where和set标签的整合并且扩充了更多的功能,变得更灵活,trim通过其名字直译也就是剔除.

        <trim  prefix="" prefixOverrides="" suffix="" suffixOverrides="">
            ....
        </trim>
  • prefix:当trim元素内包含内容时,会给内容增加prefix 指定的前缀。
  • prefixOverrides:当trim元素内包含内容时,会内容中匹配的的前缀字符串去掉(where标签)。
  • suffix:当trim元素内包含内容时,会给内容增加prefix 指定的前缀。
  • suffixOverrides:当trim元素内包含内容时,会内容中匹配的的前缀字符串去掉(where标签)。

trim标签用例

然后我们就就近原则吧,用上面的例子(需求:选择性更新,只更新用户所输入的数据。)来编写一个使用trim标签的代码。
Mapper接口中的方法编写:

    /**
     * 根据用户输入来更新,(只为了说明功能)-->使用trim标签来做
     * @param userInfo
     * @return
     */
    int updateByUserInfoThree(UserInfo userInfo);

对应xml文件的sql:

    <!-- int updateByUserInfoThree(UserInfo userInfo);-->
    <update id="updateByUserInfoThree">
        update user_info
        <trim prefix="set" suffixOverrides=",">
            <if test="userName != null and userName !=''">
                user_name =#{userName},
            </if>
            <if test="userSex != null and userSex !=''">
                user_sex =#{userSex},
            </if>
            <if test="userAddress != null and userAddress !=''">
                user_address =#{userAddress},
            </if>
            id = #{id},
        </trim>
        where id =#{id}
    </update>

测试代码:

    @Test
    public void testUpdateUserInfosThree(){
   
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);

        UserInfo userInfo1 =new UserInfo();
        userInfo1.setId(2);
        mapper.updateByUserInfoThree(userInfo1);

        UserInfo userInfo2 =new UserInfo();
        userInfo2.setId(2);
        userInfo2.setUserName("叶问二");
        mapper.updateByUserInfoThree(userInfo2);

        UserInfo userInfo3 =new UserInfo();
        userInfo3.setId(2);
        userInfo3.setUserName("叶问");
        userInfo3.setUserAddress("佛山武馆");
        mapper.updateByUserInfoThree(userInfo3);
    }

测试用例输出:
在这里插入图片描述

foreach标签

foreach标签的结构

foreach与java中的基本一致,就不过多解释。foreach可以对数组,Map,实现类迭代器接口的对象进行遍历,数组遍历时会转换为list.

        <foreach collection="" separator="" item="" close="" open="" index="">
           ...... 
        </foreach>

foreach标签的属性:

  • collection:必填,值为要迭代的属性名
  • item:变量名,每次迭代出来的值
  • index:索引的属性名,迭代时候集合数组为当前的索引值,而Map为对应的key值
  • open:整个循环内容开头的字符串
  • close:整个循环内容末尾的字符串
  • separator:每次循环的分隔符

foreach标签的用例

用foreach实现in集合

第一个例子就是用foreach实现in集合,select * from user_info where id in (1,3,5) 类似这样的sql。
编写mapper接口中的方法:

    /**
     * 根据用户id集合查询
     * @param idList
     * @return
     */
    List<UserInfo> selectByIdList(List<Integer> idList);

对应xml中的sql:

    <!-- List<UserInfo> selectByIdList(int id);-->
    <select id="selectByIdList" resultType="UserInfo">
        select id id,
                user_name userName,
                password password,
                user_sex userSex,
                user_address userAddress
        from user_info
        where id in
        <foreach collection="list" separator="," item="idList"  open="(" close=")" index="i">
            #{idList}
        </foreach>
    </select>

测试用例:

@Test
    public void testSelectByIdList(){
   
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);
        List<Integer> idList = new ArrayList<>();
        idList.add(1);
        idList.add(2);
        idList.add(4);
        List<UserInfo> userInfos = mapper.selectByIdList(idList);
        userInfos.forEach(System.out::println);
    }

测试输出:
在这里插入图片描述

foreach实现批量插入

编写mapper接口中的方法:

    /**
     * 批量插入用户信息
     * @param userInfos
     * @return
     */
    int insertUserInfoList(List<UserInfo> userInfos);

对应xml中的sql:

    <!--int insertUserInfoList(List<UserInfo> userInfos);-->
    <insert id="insertUserInfoList" >
        insert into user_info
            (id,user_name,password,user_sex,user_address)
        values
            <foreach collection="list" separator="," item="userInfos">
               (#{userInfos.id},#{userInfos.userName},#{userInfos.password},#{userInfos.userSex},#{userInfos.userAddress})
            </foreach>
    </insert>

测试用例:

     @Test
    public void testInsertUserInfoList(){
   
        SqlSession sqlSession = SqlSessionUtil.getSqlSession();
        UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);
        List<UserInfo> userInfoList = new ArrayList<>();
        userInfoList.add(new UserInfo(5,"张三","1238968anc","男","青城土地庙"));
        userInfoList.add(new UserInfo(6,"lisi","bootstrap123","男","青城土地庙"));
        userInfoList.add(new UserInfo(7,"王孙","vuejavajs221","男","萨拉齐街道办事处"));
        mapper.insertUserInfoList(userInfoList);

    }

测试输出:
在这里插入图片描述

在这里插入图片描述
好嘞!!!以上就是今天复习内容!!!陆续复习中…
在这里插入图片描述

相关文章
|
10月前
|
SQL Java 数据库连接
【YashanDB知识库】解决mybatis的mapper文件sql语句结尾加分号";"报错
【YashanDB知识库】解决mybatis的mapper文件sql语句结尾加分号";"报错
|
8月前
|
SQL XML Java
菜鸟之路Day35一一Mybatis之XML映射与动态SQL
本文介绍了MyBatis框架中XML映射与动态SQL的使用方法,作者通过实例详细解析了XML映射文件的配置规范,包括namespace、id和resultType的设置。文章还对比了注解与XML映射的优缺点,强调复杂SQL更适合XML方式。在动态SQL部分,重点讲解了`&lt;if&gt;`、`&lt;where&gt;`、`&lt;set&gt;`、`&lt;foreach&gt;`等标签的应用场景,如条件查询、动态更新和批量删除,并通过代码示例展示了其灵活性与实用性。最后,通过`&lt;sql&gt;`和`&lt;include&gt;`实现代码复用,优化维护效率。
830 5
|
10月前
|
SQL Java 数据库连接
【YashanDB 知识库】解决 mybatis 的 mapper 文件 sql 语句结尾加分号";"报错
【YashanDB 知识库】解决 mybatis 的 mapper 文件 sql 语句结尾加分号";"报错
|
3月前
|
存储 JavaScript Java
(Python基础)新时代语言!一起学习Python吧!(四):dict字典和set类型;切片类型、列表生成式;map和reduce迭代器;filter过滤函数、sorted排序函数;lambda函数
dict字典 Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度。 我们可以通过声明JS对象一样的方式声明dict
295 1
|
6月前
|
存储 缓存 JavaScript
Set和Map有什么区别?
Set和Map有什么区别?
492 1
|
3月前
|
存储 算法 容器
set_map的实现+set/map加持秒杀高频算法题锻炼算法思维
`set`基于红黑树实现,支持有序存储、自动去重,增删查效率为O(logN)。通过仿函数可自定义排序规则,配合空间配置器灵活管理内存。不支持修改元素值,迭代器失效需注意。`multiset`允许重复元素。常用于去重、排序及查找场景。
|
7月前
|
存储 JavaScript 前端开发
for...of循环在遍历Set和Map时的注意事项有哪些?
for...of循环在遍历Set和Map时的注意事项有哪些?
359 121
|
10月前
|
编译器 C++ 容器
【c++丨STL】基于红黑树模拟实现set和map(附源码)
本文基于红黑树的实现,模拟了STL中的`set`和`map`容器。通过封装同一棵红黑树并进行适配修改,实现了两种容器的功能。主要步骤包括:1) 修改红黑树节点结构以支持不同数据类型;2) 使用仿函数适配键值比较逻辑;3) 实现双向迭代器支持遍历操作;4) 封装`insert`、`find`等接口,并为`map`实现`operator[]`。最终,通过测试代码验证了功能的正确性。此实现减少了代码冗余,展示了模板与仿函数的强大灵活性。
286 2
|
7月前
|
存储 C++ 容器
unordered_set、unordered_multiset、unordered_map、unordered_multimap的介绍及使用
unordered_set是不按特定顺序存储键值的关联式容器,其允许通过键值快速的索引到对应的元素。在unordered_set中,元素的值同时也是唯一地标识它的key。在内部,unordered_set中的元素没有按照任何特定的顺序排序,为了能在常数范围内找到指定的key,unordered_set将相同哈希值的键值放在相同的桶中。unordered_set容器通过key访问单个元素要比set快,但它通常在遍历元素子集的范围迭代方面效率较低。它的迭代器至少是前向迭代器。前向迭代器的特性。
330 0