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天前
|
弹性计算 人工智能 架构师
阿里云携手Altair共拓云上工业仿真新机遇
2024年9月12日,「2024 Altair 技术大会杭州站」成功召开,阿里云弹性计算产品运营与生态负责人何川,与Altair中国技术总监赵阳在会上联合发布了最新的“云上CAE一体机”。
阿里云携手Altair共拓云上工业仿真新机遇
|
6天前
|
机器学习/深度学习 算法 大数据
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
2024“华为杯”数学建模竞赛,对ABCDEF每个题进行详细的分析,涵盖风电场功率优化、WLAN网络吞吐量、磁性元件损耗建模、地理环境问题、高速公路应急车道启用和X射线脉冲星建模等多领域问题,解析了问题类型、专业和技能的需要。
2506 14
【BetterBench博士】2024 “华为杯”第二十一届中国研究生数学建模竞赛 选题分析
|
6天前
|
机器学习/深度学习 算法 数据可视化
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
2024年中国研究生数学建模竞赛C题聚焦磁性元件磁芯损耗建模。题目背景介绍了电能变换技术的发展与应用,强调磁性元件在功率变换器中的重要性。磁芯损耗受多种因素影响,现有模型难以精确预测。题目要求通过数据分析建立高精度磁芯损耗模型。具体任务包括励磁波形分类、修正斯坦麦茨方程、分析影响因素、构建预测模型及优化设计条件。涉及数据预处理、特征提取、机器学习及优化算法等技术。适合电气、材料、计算机等多个专业学生参与。
1519 14
【BetterBench博士】2024年中国研究生数学建模竞赛 C题:数据驱动下磁性元件的磁芯损耗建模 问题分析、数学模型、python 代码
|
8天前
|
编解码 JSON 自然语言处理
通义千问重磅开源Qwen2.5,性能超越Llama
击败Meta,阿里Qwen2.5再登全球开源大模型王座
530 13
|
1月前
|
运维 Cloud Native Devops
一线实战:运维人少,我们从 0 到 1 实践 DevOps 和云原生
上海经证科技有限公司为有效推进软件项目管理和开发工作,选择了阿里云云效作为 DevOps 解决方案。通过云效,实现了从 0 开始,到现在近百个微服务、数百条流水线与应用交付的全面覆盖,有效支撑了敏捷开发流程。
19282 30
|
1月前
|
人工智能 自然语言处理 搜索推荐
阿里云Elasticsearch AI搜索实践
本文介绍了阿里云 Elasticsearch 在AI 搜索方面的技术实践与探索。
18836 20
|
1月前
|
Rust Apache 对象存储
Apache Paimon V0.9最新进展
Apache Paimon V0.9 版本即将发布,此版本带来了多项新特性并解决了关键挑战。Paimon自2022年从Flink社区诞生以来迅速成长,已成为Apache顶级项目,并广泛应用于阿里集团内外的多家企业。
17524 13
Apache Paimon V0.9最新进展
|
8天前
|
人工智能 自动驾驶 机器人
吴泳铭:AI最大的想象力不在手机屏幕,而是改变物理世界
过去22个月,AI发展速度超过任何历史时期,但我们依然还处于AGI变革的早期。生成式AI最大的想象力,绝不是在手机屏幕上做一两个新的超级app,而是接管数字世界,改变物理世界。
457 48
吴泳铭:AI最大的想象力不在手机屏幕,而是改变物理世界
|
1天前
|
云安全 存储 运维
叮咚!您有一份六大必做安全操作清单,请查收
云安全态势管理(CSPM)开启免费试用
353 4
叮咚!您有一份六大必做安全操作清单,请查收
|
2天前
|
存储 关系型数据库 分布式数据库
GraphRAG:基于PolarDB+通义千问+LangChain的知识图谱+大模型最佳实践
本文介绍了如何使用PolarDB、通义千问和LangChain搭建GraphRAG系统,结合知识图谱和向量检索提升问答质量。通过实例展示了单独使用向量检索和图检索的局限性,并通过图+向量联合搜索增强了问答准确性。PolarDB支持AGE图引擎和pgvector插件,实现图数据和向量数据的统一存储与检索,提升了RAG系统的性能和效果。