MyBatis学习 | 动态SQL

news2025/1/15 13:03:07

文章目录

  • 一、简介
  • 二、if标签
    • 2.1 if标签的简单使用
    • 2.2 where标签
    • 2.3 trim标签(了解)
  • 三、choose标签 & set标签
    • 3.1 choose标签
    • 3.2 set标签
  • 四、foreach标签
    • 4.1 foreach标签的简单使用
    • 4.2 批量插入
  • 五、内置参数
  • 六、bind标签
  • 七、sql标签 & include标签


学习地址🔗

  • https://www.bilibili.com/video/BV1mW411M737
  • https://www.bilibili.com/video/BV1NE411Q7Nx
  • 官网文档

一、简介

💬概述:动态SQL——Dynamic SQL,是MyBatis中强大功能之一,可以根据不同条件来拼接SQL语句

🔑实现动态SQL的几个重要标签

  • if
  • choose(when、otherwise)
  • trim(where、set)
  • foreach

🔑关于动态SQL中使用到的表达式语言——OGNL

  • 全称:Object Graph Navigation Language,对象图导航语言

  • 概述:一种强大的表达式语言,使用它可以方便地操作对象属性,类似于EL表达式

  • 使用

    作用表达式
    访问对象属性person.name
    调用方法person.getName()
    调用静态属性@java.lang.Math@PI
    调用静态方法@java.util.UUID@randomUUID
    调用构造方法new com.key.mybatis.entity.Person(“Key”).name
    算术运算符+,-,*,/,%等
    逻辑运算符in,not in,and,or,<,>,=,<=,>=,!=,==等
    拼接字符串‘str1’ + ‘str2’

    💡 在xml文件中,某些敏感字符需要使用其转移字符代替,如逻辑运算符中<>


二、if标签

2.1 if标签的简单使用

💬概述:在SQL映射文件的SQL标签中,可以添加<if>子标签,根据设置的条件动态拼接SQL语句

🔑作用:根据设置的条件动态直接拼接SQL语句,不进行修剪,一般用于动态拼接WHERE子句的查询条件

🔑属性——text(必须设置)

  • 作用:使用OGNL表达式来设置条件
  • 属性值:OGNL表达式

🔑使用:直接在SQL语句标签内添加<if>,并添加text属性设置条件,<if>标签体内添加动态的语句,当满足text中设置的条件时才会拼上该语句

<!-- id不为null时使用id查询用户信息 -->
<if text="userid != null">
    `user_id` = #{userid}
</if>

💡 text的属性值(OGNL表达式)中的userid对应#{Key}中的键Key,即获取参数时的键,需要根据键获取参数值后再来判断是否为null,不是数据库表中的字段名user_id

🔑测试:使用不为null的参数(字段)来查询对应用户信息

  • 分析:在书写SQL语句时,不能直接知道传进来的参数值哪一个为null,所以需要对查询的条件进行动态拼接

  • 用户持久层接口中添加对应方法

    // 根据if标签的条件来查询用户信息
    List<User> getUsersByIfCondition(User user);
    
  • 用户映射文件

    <!-- 根据if标签条件查询 -->
    <select id="getUsersByIfCondition" resultType="com.key.mybatis.entity.User">
        select *
        from `user`
        where
        <!-- 使用if标签动态拼接查询条件 -->
    
        <!-- id不为null,用id查询 -->
        <if test="userid != null">
            `user_id` = #{userid}
        </if>
        <!-- 姓名不为null,用姓名查询 -->
        <if test="username != null and username.trim() != ''">
            and `user_name` = #{username}
        </if>
        <!-- 密码不为null,用密码查询 -->
        <if test="password != null">
            and `password` = #{password};
        </if>
    </select>
    

    💡 因为持久层接口的对应方法中形参为User,即POJO,所以在映射文件中可以直接将User类的属性名作为键Key获取参数值

  • 测试方法1.0——只有用户id不为null,则只根据用户id查询

    • 方法

      // 创建一个用户对象,只有用户id不为null
      User user = new User(2, null, null);
      
      // 调用持久层接口方法
      List<User> users = mapper.getUsersByIfCondition(user);
      users.forEach(System.out :: println);
      
    • 打印结果test-if-condition-result01

  • 测试方法2.0——用户姓名为null,id和密码不为null,则根据id和密码来查询

    • 方法

      // 创建一个用户对象,用户姓名为null,id和密码不为null
      User user = new User(2, null, "12444");
      
      // 调用持久层接口方法
      List<User> users = mapper.getUsersByIfCondition(user);
      users.forEach(System.out :: println);
      
    • 打印结果test-if-condition-result02

  • 测试方法3.0——用户id和密码都为null,只有姓名不为null,根据姓名查询

    • 方法

      // 创建一个用户对象,用户id和密码都为null,只有姓名不为null
      User user = new User(null, "周慧敏", null);
      
      // 调用持久层接口方法
      List<User> users = mapper.getUsersByIfCondition(user);
      users.forEach(System.out :: println);
      
    • 打印结果test-if-condition-result-error

    • ❌报错原因分析:

      ① 在id为null且姓名不为null情况下,判断id是否为null的<if>标签体内的查询条件(user_id = #{userid})就不会拼接到SQL语句中,姓名对应的<if>标签体内容会完整拼接到SQL语句中

      ② 姓名对应的<if>标签体内容是and user_nam = #{username},查询条件前面还多了个and,而它前面已经没有其他查询条件可以拼接,因此最终拼接而成的SQL语句中,where和查询条件之间也会多一个and,该SQL语句很明显不合法,因此报错if-condition-error-reason

      🔺 解决方法

      ① 在每一个<if>标签体内的查询条件前面都加上and,然后在where子句后加上一个1=1的条件,这样就能保证where子句后面拼上的是条件1=1,而不是多余的and,而且条件1=1恒成立,不会对后面通过and拼起来的查询条件有任何影响,但这种方法仅适用于通过与and连接的查询条件
      ② 使用<where>标签👇

2.2 where标签

❓ 问题引入:在上面对<if>标签的测试方法3.0中,遇到拼接后的SQL语句不合法的问题,如果只使用<if>标签动态拼接SQL语句中的查询条件,就很难避免这个问题,因此需要使用<where>标签来解决(MyBatis推荐)

💬概述<where>也是<select>标签的子标签,它能代替查询SQL语句的where子句

🔑作用:代替where子句,将查询条件作为其标签体内容,使查询条件也能动态拼接,即将合法的查询条件前面多余的内容去掉,剩下合法、有用的内容拼接到where子句后

🔑使用限制<where>只能去掉查询条件前面多余的内容,如果多余的内容在合法查询条件的后面,则无法去掉

🔑测试1.0:传入的用户id和密码都为null,只有姓名不为null,则根据姓名查询用于信息(上面的测试3.0)

  • dao方法不变

    List<User> getUsersByIfCondition(User user);
    
  • 用户映射文件

    <!-- 根据if标签条件查询 -->
    <select id="getUsersByIfCondition" resultType="com.key.mybatis.entity.User">
        select *
        from `user`
    
        <!-- 使用where标签代替where子句
    		 - where标签代替了where子句,因此where就不用再添加
    		 - if标签放在where标签里面
    	-->
        <where>
            <!-- id不为null,用id查询 -->
            <if test="userid != null">
                `user_id` = #{userid}
            </if>
            <!-- 姓名不为null,用姓名查询 -->
            <if test="username != null and username.trim() != ''">
                and `user_name` = #{username}
            </if>
            <!-- 密码不为null,用密码查询 -->
            <if test="password != null">
                and `password` = #{password};
            </if>
        </where>
    </select>
    
  • 测试方法

    // 创建一个用户对象,姓名为null
    User user = new User(null, "周星驰", null);
    
    // 调用持久层接口方法
    List<User> users = mapper.getUsersByIfCondition(user);
    users.forEach(System.out :: println);
    
  • 打印结果test-if-condition-where-result

🔑测试2.0:修改<if>标签中查询内容的书写,将and放在合法的查询条件后面

  • dao方法与测试方法与上面👆一样

  • 映射文件中

    <!-- 根据if标签条件查询 -->
    <select id="getUsersByIfCondition" resultType="com.key.mybatis.entity.User">
        select *
        from `user`
    
        <!-- 使用where标签代替where子句 -->
        <where>
            <!--  
    			* 注意这里的查询条件中,将'and'放到后面
    		-->
            
            <!-- id不为null,用id查询 -->
            <if test="userid != null">
                `user_id` = #{userid} and
            </if>
            <!-- 姓名不为null,用姓名查询 -->
            <if test="username != null and username.trim() != ''">
                `user_name` = #{username} and
            </if>
            <!-- 密码不为null,用密码查询 -->
            <if test="password != null">
                `password` = #{password};
            </if>
        </where>
    </select>
    
  • 打印结果test-if-condition-where-result-error

  • 分析结果:有打印结果可知,<where>标签不会将合法的查询条件后面多余的内容去掉,只能去掉前面的,因此最终拼接而成的SQL语句后面还是会有一个多余的and,这也是不合法的SQL语句,因此也报错

2.3 trim标签(了解)

❓ 问题引入:在上面的测试2.0中,可以看到<where>使用限制——查询条件中多余的内容只能放在前面,如果放在后面就无法去掉,因此最终得到的SQL语句仍然是不合法的,此时可以使用<trim>标签解决

💬概述<trim>也是SQL语句标签的子标签,与<where>标签使用类似

🔑作用:trim是修剪的意思,也是将查询条件作为其标签体内容,对查询条件中的前缀后缀内容进行修改后再拼接到最终的SQL语句中,因此合法查询条件的前面和后面的多余内容都能去掉

🔑 <trim>中四个属性

属性名作用
prefix指定拼接后查询条件的前缀
suffix指定拼接后查询条件的后缀
prefixOverrides指定需要覆盖(去掉)的拼接后查询条件的前缀
suffixOverrides指定需要去掉的拼接后查询条件的后缀

💡 这里的“拼接后查询条件”是指直接拼接而成、没有被修饰过的拼接结果,可能会带有多余的前缀和后缀

🔑测试:将<if>标签中的and放在合法的查询条件后面,作为后缀,且不添加where子句和<where>标签(在<where>标签测试2.0基础上修改)

  • dao方法和测试方法都与上面👆一样

  • 映射文件

    <!-- 根据if标签条件查询 -->
    <select id="getUsersByIfCondition" resultType="com.key.mybatis.entity.User">
        select *
        from `user`
    
        <!-- 使用trim标签,修改查询条件的前后缀
     		 - prefix="where":表示在拼接后的查询结果前加上 where
    		 - suffixOverrides="and":表示去掉拼接后的查询条件后面的 and
    	-->
        <trim prefix="where" suffixOverrides="and">
            <!-- id不为null,用id查询 -->
            <if test="userid != null">
                `user_id` = #{userid} and
            </if>
            <!-- 姓名不为null,用姓名查询 -->
            <if test="username != null and username.trim() != ''">
                `user_name` = #{username} and
            </if>
            <!-- 密码不为null,用密码查询 -->
            <if test="password != null">
                `password` = #{password};
            </if>
        </trim>
    </select>
    
  • 打印结果test-if-condition-trim-result


三、choose标签 & set标签

3.1 choose标签

💬概述<choose>标签也是SQL语句标签的子标签,表示分支选择的意思,与switch...case使用类似

🔑作用:选择出符合指定条件的唯一内容,即只选择出一种情况

🔑子标签<choose>标签中无属性,但有两个重要的子标签

子标签名属性解释是否必须添加
whentext:使用OGNL表达式指定选择的条件指定选择的条件,标签体为选择的内容;类似于case;MyBatis会根据<when>标签的添加顺序来匹配条件,一旦有一个满足条件,就不会再匹配后面的<when>标签条件
otherwise指定默认选择的内容,当所有<when>中指定条件都不满足时就选择<otherwise>标签体中的内容,类似于default

🔑使用:直接在SQL语句标签中添加<choose>子标签,然后在<choose>中再添加<when>标签指定选择的条件,根据需要添加<otherwise>标签指定默认选择

<!-- 使用choose根据条件选择其中一种情况 -->
<choose>
    <when test="userid != null">
        `user_id` = #{userid}
    </when>
    <when test="username != null">
        `user_name` like #{username}
    </when>
    <!-- 默认选择,如果when条件都不满足,就查询全部信息 -->
    <otherwise>
        1=1
    </otherwise>
</choose>

💡 因为<choose>标签只会选择出一种情况,因此每一个<when>标签体内的查询条件中不用添加连接词(andor

🔑测试:查询用户信息,如果id不为null就只用id查询,如果id为null就只用姓名模糊查询,如果姓名也为空就只用密码查询,如果都为null,则就直接将所有用户信息查出

  • 用户dao接口方法

    List<User> getUsersByChooseCondition(User user);
    
  • 用户映射文件

    <!-- 根据choose标签查询 -->
    <select id="getUsersByChooseCondition" resultType="com.key.mybatis.entity.User">
        select *
        from `user`
        <!-- 以后都写where标签 -->
        <where>
            <!-- 使用choose根据条件选择其中一种情况 -->
            <choose>
                <when test="userid != null">
                    `user_id` = #{userid}
                </when>
                <!-- 姓名是模糊查询,用like关键字 -->
                <when test="username != null and username.trim() != ''">
                    `user_name` like #{username}
                </when>
                <when test="password != null">
                    `password` = #{password}
                </when>
                <!-- 默认选择,如果when条件都不满足,就查询全部信息 -->
                <otherwise>
                    1=1
                </otherwise>
            </choose>
        </where>
    </select>
    
  • 测试方法:id为null,用户姓名带有‘周’字,密码11baa

    @Test
    public void testChooseCondition() {
        // 获取SqlSession
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
    
        // 获取mapper对象
        UserDao mapper = sqlSession.getMapper(UserDao.class);
    
        // 创建一个用户对象,id为null,用户姓名带有‘周’字,密码11baa
        User user = new User(null, "%周%", "11baa");
    
        // 调用dao方法
        List<User> users = mapper.getUsersByChooseCondition(user);
    
        users.forEach(System.out :: println);
    
        // 关闭sqlSession
        sqlSession.close();
    }
    
  • 打印结果test-choose-condition-result

    🔺 结果分析:由打印结果可以看出,最终拼出的SQL语句中只有根据姓名模糊查询的条件,即使传入的参数中密码’password’也不为null,但密码对应的<when>选择条件在姓名后面,因此就不会去匹配密码对应的<when>选择条件

3.2 set标签

💬概述<set>标签是<update>标签中的子标签,用于代替更新SQL语句中的set子句

🔑作用:代替更新语句中的set子句,选择性拼接set后面的更新内容,可以去掉拼接后的多余内容,一般是,

🔑使用:在<update>标签中添加<set>标签,代替set子句,同时使用<if>根据指定条件动态获取更新内容

<!-- 使用set标签动态拼接更新内容 -->
<set>
    <!-- 使用if标签动态获取更新内容 -->
    <if test="username != null">
        `user_name` = #{username},
    </if>
    <if test="password != null">
        `password` = #{password}
    </if>
</set>

❓ 关于去掉拼接后的多余内容

  • 如果更新语句中不使用<set>标签,而是直接使用set子句和<if>标签动态拼接更新内容,则最终拼接的SQL语句有可能在后面多了个,,造成SQL语句不合法。与<where>解决的情况类似
  • 对于去掉多余的拼接内容,一样能使用<trim>标签来实现,此时需要添加的前缀prefix="set",需要去掉的后缀suffixOverrides=","(不推荐使用)

🔑测试:修改id为5的用户信息,只修改不为null的字段信息

  • 用户dao接口方法

    int updateUserBySetCondition(User user);
    
  • 用户映射文件

    <!-- 使用set标签更新用户信息 -->
    <update id="updateUserBySetCondition">
        update `user`
        <!-- 使用set标签动态拼接更新内容 -->
        <set>
            <!-- 使用if标签动态获取更新内容 -->
            <if test="username != null">
                `user_name` = #{username},
            </if>
            <if test="password != null">
                `password` = #{password}
            </if>
        </set>
        <!-- 最后还有查询条件的拼接,使用where -->
        <where>
            `user_id` = #{userid}
        </where>
    </update>
    
  • 测试方法

    @Test
    public void testSetCondition() {
        // 获取SQlSession
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
    
        // 获取mapper
        UserDao mapper = sqlSession.getMapper(UserDao.class);
    
        // 创建一个用户对象
        User user = new User(5, "周海媚", null);
    
        // 调用dao方法
        int result = mapper.updateUserBySetCondition(user);
    
        if (result == 0) {
            System.out.println("更新失败!");
        } else {
            System.out.println("更新成功!");
        }
    
        // 提交事务
        sqlSession.commit();
    
        // 关闭sqlSession
        sqlSession.close();
    }
    
  • 打印结果test-set-condition-result


四、foreach标签

4.1 foreach标签的简单使用

💬概述<foreach>标签也是SQL语句标签的一个子标签,可以实现循环遍历的功能

🔑作用:循环遍历指定的数组或集合,并将遍历后的结果拼接到SQL语句中

🔑属性<foreach>有下列主要属性

属性名属性值解释是否必须添加
collection属性值为需要遍历的集合名称,对应dao接口方法集合类型形参标识出<foreach>中所遍历的集合或数组;如果在dao接口方法中没有为形参命名,则collection属性值只能使用默认键——【argN、collection、list]
item自定义的变量名标识当前遍历的元素,通过该属性值能获取每一个元素以及每一个元素的属性
separator自定义的分隔符为遍历的集合或数组的每一项之间添加一个分割符
open自定义字符为循环结束后拼接的语句前面添加一个开始字符
close自定义字符为循环结束后拼接的语句后面添加一个结束字符
index自定义的变量名①如果遍历的是List集合或数组,则该属性就是每一项的索引值;②如果遍历的是Map集合,则该属性就是Map中的键Key

🔑使用

  • 在SQL语句标签中添加<foreach>标签,<foreach>标签内添加相关属性
  • 使用#{}${}获取集合中每一项的值
<foreach collection="ids" item="uId" separator="," open="(" close=")">
    #{uId}
</foreach>

🔑测试:查询id为1-4之间的用户信息

  • 分析

    • 查询不同id的用户信息,可以使用in ()语句来实现,而()里面的各个id可以封装成一个id集合,然后再SQL语句中遍历出来即可
    • dao接口方法中为形参命名,在映射文件取参数时就可以直接通过自定义名来获取
    • 因为()里面的不同参数之间用,隔开,因此在<foreach>遍历时也需要使用separator属性为每一项之间添加分隔符,不能在循环体中直接写,,因为这样写会使遍历的最后一项后面也有一个,,从而造成SQL语句不合法
    • 因为要遍历的集合在()里面,即<foreach>遍历结束后拼接的内容添加到()里面,有两种实现方式
      ① 将左括号(写在在<foreach>前面,右括号)写在后面,相当于用()<foreach>包裹起来
      ② 使用<foreach>标签中的openclose属性,为循环遍历结束后拼接的内容前面加上开始字符(,后面加上结束字符)(推荐)
  • 用户dao接口方法

    List<User> getUsersByForeachCondition(@Param("ids") List<Integer> ids);
    
  • 用户映射文件

    <!-- 根据指定的id集合查询出对应的用户信息 -->
    <select id="getUsersByForeachCondition" resultType="com.key.mybatis.entity.User">
        select *
        from `user`
        <where>
            `user_id` in
            <foreach collection="ids" item="uId" separator="," open="(" close=")">
                #{uId}
            </foreach>
        </where>
    </select>
    
  • 测试方法

    @Test
    public void testForeachCondition() {
        // 获取SqlSession
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
    
        // 获取mapper
        UserDao mapper = sqlSession.getMapper(UserDao.class);
    
        // 调用dao方法
        List<User> users = mapper.getUsersByForeachCondition(Arrays.asList(1, 2, 3, 4));
    
        users.forEach(System.out :: println);
    
        // 关闭sqlSession
        sqlSession.close();
    }
    

    💡 测试中直接使用工具类的方法创建id集合——Arrays.asList()

  • 打印结果test-foreach-condition-result

4.2 批量插入

💡 下面只针对MySQL数据库支持的批量插入方式

💬概述:使用<foreach>标签还可以遍历多个对象记录,实现向数据库表中批量插入多条记录

🔑批量插入方式1.0——在插入SQL语句中的values后添加多个(),多个()之间用,分隔开

  • 用户dao接口方法

    int batchInsertUsersByForeach(@Param("users") List<User> users);
    
  • 用户映射文件

    <!-- 批量插入多条用户记录1.0 -->
    <insert id="batchInsertUsersByForeach">
        insert into `user`(`user_name`, `password`)
        values
        <!-- 使用foreach循环遍历每一条插入数据 -->
        <foreach collection="users" item="u" separator=",">
            (#{u.username}, #{u.password})
        </foreach>
    </insert>
    

    💡 这里是将()写在<foreach>标签体(循环体)中,因为要遍历的每一项包含了(),而不是每一项在()

  • 测试方法

    @Test
    public void testBatchInsertByForeach() {
        // 获取SqlSession
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
    
        // 获取mapper
        UserDao mapper = sqlSession.getMapper(UserDao.class);
    
        // 创建一个用户集合
        List<User> users = new ArrayList<>();
        users.add(new User(null, "宋江", "song123"));
        users.add(new User(null, "武松", "wu123"));
        users.add(new User(null, "潘金莲", "pan123"));
    
        int result = mapper.batchInsertUsersByForeach(users);
    
        if (result == 0) {
            System.out.println("批量插入失败!");
        } else {
            System.out.println("批量插入成功!");
        }
    
        // 提交事务
        sqlSession.commit();
    
        // 关闭
        sqlSession.close();
    }
    
  • 打印结果test-foreach-batch-insert-result01

🔑批量插入方式2.0——直接执行多次insert语句,每条insert语句之间用;分隔开

  • 用户dao接口方法同上

  • 用户映射文件

    <!-- 批量插入2.0 -->
    <insert id="batchInsertUsersByForeach">
        <!-- 直接使用foreach遍历多次insert语句,每条语句之间用;隔开 -->
        <foreach collection="users" item="u" separator=";">
            insert into `user`(`user_name`, `password`)
            values(#{u.username}, #{u.password})
        </foreach>
    </insert>
    
  • 在jdbc属性文件中添加allowMultiQueries参数,并设置为true

    prop.url=jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
    

    💡 此操作必不可少,因为参数allowMultiQueries默认为false,即MySQL默认不支持多条SQL语句通过;分隔开并同时执行

  • 测试方法

    // 创建一个用户集合
    List<User> users = new ArrayList<>();
    users.add(new User(null, "吴用", "wu456"));
    users.add(new User(null, "武大郎", "da123"));
    users.add(new User(null, "扈三娘", "hu123"));
    
    int result = mapper.batchInsertUsersByForeach(users);
    
    if (result == 0) {
        System.out.println("批量插入失败!");
    } else {
        System.out.println("批量插入成功!");
    }
    
  • 打印结果test-foreach-batch-insert-result02


五、内置参数

💬概述:MyBatis中不仅可以获取dao接口方法传递过来的参数值,还能使用内置参数来获取值,且在OGNL表达式中可以直接使用内置参数

🔑两个内置参数

  • _parameter

    • 概述

      ① 如果dao接口只传入单个简单类型的形参,则_parameter就表示这个形参,可以直接根据它来获取对应参数值;② 如果形参中传入多个参数,MyBatis会将dao接口方法中的形参封装成一个Map集合,而内置参数_parameter就表示这个Map集合

    • 作用:表示任意传进来的单个参数或参数Map集合,可以直接获取对应参数值

    • 使用:在<if>标签的text属性中判断传入的形参是否为null,如果不为null才获取对应参数

      <!-- 直接根据内置参数_parameter判断传入形参是否为null -->
      <if text="_parameter != null">
      	where `user_id` = #{userid}
      </if>
      
    • 使用细节

      1. _parameter一般使用在判断条件中(如果<if>标签的text属性里面),即OGNL表达式中,不使用在#{}获取参数
      2. 如果传入单个对象类型的形参,则可以直接使用_parameter.属性名来获取对象的属性值
      3. 如果传入多个形参,则可以使用_parameter.键Key的方式来获取对应参数值,包括默认键——argN和paramN
      4. 如果传入单个普通类型参数,则可以直接使用_parameter就能获取到参数值,或者使用_parameter.键Key
  • _databaseId

    • 概述:如果全局配置文件中设置了<databaseIdProvider>标签,并为各个数据库厂商标识设置了别名,则内置参数_databaseId就表示当前环境下数据库厂商标识的别名

    • 作用:可以直接在SQL语句标签体中获取当前环境的数据库厂商标识,并可以根据不同环境下的数据库厂商设置不同的SQL语句

    • 使用:在<if>标签的text属性中判断当前环境的数据库厂商,然后使用对应的SQL语句

      <!-- 根据数据库厂商标识的别名来选择查询数据库表 -->
      <if test="_databaseId == 'mysql'">
          <!-- mysql下查询user表 -->
          select * from `user`
      </if>
      <if test="_databaseId == 'oracle'">
          <!-- oracle下查询employeeb -->
          select * from `employee`
      </if>
      

六、bind标签

💬概述<bind>也是SQL语句标签的子标签,bind是绑定的意思,顾名思义就是用来绑定数据或值

🔑作用:使用<bind>标签,可以为自定义的值绑定一个变量名,根据变量名就能获取到对应的值

🔑<bind>标签的两个属性

属性名属性值解释
name自定义的变量名为自定义的新参数绑定一个变量名,SQL语句中通过#{Key}获取参数值时,就可以直接将该变量名作为键Key来获取新的参数值
value自定义的新参数value属性中可以使用OGNL表达式来自定义新的参数,比如可以直接根据键Key获取传进来的形参对应的参数值,然后将参数值修改成新的参数

🔑使用:根据姓名模糊查询用户信息,模糊查询时直接传入关键字,不传入通配符(%_),在映射文件中再为传入的关键字添加上通配符,形成一个新参数,然后使用<bind>为该新参数绑定一个变量名,SQL语句中根据该变量名来获取对应参数值

  • dao接口方法

    List<User> listUsersByBind(String nameKey);
    
  • 映射文件

    <!-- 根据姓名关键字模糊查询 -->
    <select id="listUsersByBind" resultType="com.key.mybatis.entity.User">
        <!-- 使用bind标签绑定新的参数 
    		 - name="newName":为自定义的新参数绑定一个变量名
    		 - value="...":自定义新的参数,也是使用OGNL表达式
    	-->
        <bind name="newName" value="'%' + nameKey + '%'"/>
        select *
        from `user`
        where `user_name` like #{newName};
    </select>
    
  • 测试方法

    @Test
    public void testBind() {
        // 获取SqlSession
        SqlSession sqlSession = MyBatisUtil.getSqlSession();
    
        // 获取mapper
        UserDao mapper = sqlSession.getMapper(UserDao.class);
    
        List<User> users = mapper.listUsersByBind("周");
    
        users.forEach(System.out :: println);
    
        // 关闭sqlSession
        sqlSession.close();
    }
    

七、sql标签 & include标签

🔑sql标签

  • 概述:<sql>标签是与其他SQL语句标签同级的标签,在<sql>标签中也能书写SQL语句
  • 作用:抽取可重用的SQL语句,一般用于抽取插入语句、更新语句时的数据库表的列名
  • 使用细节:<sql>标签中也可以使用动态SQL语句中各种条件标签,如<if><where>
  • 属性——id:唯一标识,根据sql-id可以引用其抽取的SQL语句

🔑include标签

  • 概述:<include>标签是SQL语句标签的子标签,与<sql>标签是合作伙伴

  • 作用:引用<sql>中抽取的可重用SQL语句

  • 作用位置:穿插在SQL语句中,取代被抽取的部分SQL语句

  • 使用细节:在<include>标签中还可以通过<property>子标签自定义属性<sql>标签中就可以使用${}获取对应属性

    💡 <sql>标签中获取<include>定义的属性时,只能使用${},不能使用#{}

  • 属性——refid:对应<sql>标签中的唯一标识sql-id,根据该id标识了对应<sql>标签后,<include>所在的位置就会被<sql>中抽取的SQL语句所代替,最终拼接成完整的SQL语句

🔑sql标签和include标签结合使用

  • 使用1.0——使用<sql>抽取插入语句中的各个数据库表列名,然后在SQL语句标签中使用<include>引用对应SQL

    <!-- 使用sql标签和include标签 -->
    <insert id="insertUserBySqlAndInclude">
        insert into `user` (
        	<!-- include标签直接写在SQL语句中,取代抽取的SQL -->
        	<include refid="insertColumn"/>
        )
        values(#{userid}, #{username}, #{password})
    </insert>
    
    <!-- 抽取可重用SQL语句 -->
    <sql id="insertColumn">
        `user_name`, `password`
    </sql>
    
  • 使用2.0——在<include>中为密码字段’password’再自定义一个名称,然后在<sql>中使用自定义名来获取对应字段名

    <!-- 使用sql标签和include标签 -->
    <insert id="insertUserBySqlAndInclude">
        insert into `user` (
            <include refid="insertColumn">
                <!-- 自定义属性 -->
                <property name="pwd" value="password"/>
            </include>
        )
        values(#{userid}, #{username}, #{password})
    </insert>
    
    <!-- 抽取可重用SQL语句 -->
    <sql id="insertColumn">
        <!-- 使用${}获取include设置的属性值 -->
        `user_name`, ${pwd}
    </sql>
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/111958.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2163. 删除元素后和的最小差值 堆解法解析

2163. 删除元素后和的最小差值 给你一个下标从 0 开始的整数数组 nums &#xff0c;它包含 3 * n 个元素。 你可以从 nums 中删除 恰好 n 个元素&#xff0c;剩下的 2 * n 个元素将会被分成两个相同大小的部分。 前面 n 个元素属于第一部分&#xff0c;它们的和记为 sumfirs…

Fabric.js 保存自定义属性

本文简介 点赞 关注 收藏 学会了 之前有些工友留言&#xff1a;在 fabric.js 中怎么保存元素的自定义属性&#xff1f; 比如&#xff0c;创建一个矩形&#xff0c;这个矩形有自己的 ID 属性&#xff0c;在执行序列化操作出来的结果却看不到 ID 属性了。 如何在序列化时输出…

项目实战之旅游网(三)后台用户管理(下)

目录 一.查询用户角色 二.修改用户角色 三.修改用户状态 一.查询用户角色 一个用户可以有多个角色&#xff0c;我们也可以给某个用户分配某些角色&#xff0c;所以我们还需要新建一个实体类&#xff08;这个实体类需要放到bean下&#xff0c;因为这个实体类和数据据库不是对…

【Effective_Objective-C_3接口与API设计】

文章目录前言15.用前缀避免命名空间冲突要点总结16.提供全能初始化方法全能初始化要点17.实现description方法description以字典形式输出descriptiondebugDescription要点18.尽量使用不可变对象要点19.使用清晰协调的命名方式方法命名类与协议命名要点20.为私有方法名加前缀21.…

人-机器人交互导论

【编者按&#xff1a;变主体性才是智能的真正厉害之处&#xff0c;能够设身处地地转换角色、角度、视角看待交互对象中的不确定性&#xff0c;并使用相应的同理同情共主观性机制机理进行处理&#xff0c;例如梅西过人中的真假变化。在个体/群体智能中的变主体性控制也是人机环境…

【圣诞节】飘雪圣诞树

一、前言 马上2023年的圣诞节&#x1f384;要到了&#xff0c;作为一个程序员&#xff0c;没什么可以送给大家的&#xff0c;就给大家画一个圣诞树&#x1f384;&#xff0c;作为礼物来送给大家吧。 二、创意名 明月当空飘雪圣诞树 三、效果展示 四、实现步骤 主要是利用three.…

详细设计说明书(GB8567——88)基于协同的在线表格forture-sheet

详细设计说明书 1引言 1.1编写目的 该文档在概要设计的基础上&#xff0c;进一步的细化系统结构&#xff0c;展示了软件结构的图标&#xff0c;物理设计、数据结构设计、及算法设计、详细的介绍了系统各个模块是如何实现的&#xff0c;包括涉及到的算法&#xff0c;逻辑流程…

【LeetCode每日一题:1754. 构造字典序最大的合并字符串~~~双指针+贪心算法】

题目描述 给你两个字符串 word1 和 word2 。你需要按下述方式构造一个新字符串 merge &#xff1a;如果 word1 或 word2 非空&#xff0c;选择 下面选项之一 继续操作&#xff1a; 如果 word1 非空&#xff0c;将 word1 中的第一个字符附加到 merge 的末尾&#xff0c;并将其…

《Unified Structure Generation for Universal Information Extraction》论文阅读

文章目录文章介绍文章方案用于统一结构编码的结构化抽取语言&#xff08;SEL&#xff09;用于可控IE结构生成的结构模式指导使用UIE生成预训练任务微调任务总结参考文章地址&#xff1a; https://arxiv.org/abs/2203.12277文章介绍 目前对于自然语言处理中的信息抽取任务如关系…

业聚医疗港交所上市:市值76亿港元 为钱永勋家族企业

雷递网 雷建平 12月23日血管介入器械公司――业聚医疗集团有限公司&#xff08;OrbusNeich Medical Group Limited&#xff09;&#xff08;简称“业聚医疗”&#xff0c;股票代码为&#xff1a;6929 &#xff09;今日在港交所上市。业聚医疗发行价为8.8港元&#xff0c;募资净…

SpringCloudGateway源码(四)限流组件

前言 如果不使用Alibaba Sentinel的网关流控规则&#xff0c; 是否可以选择使用SpringCloudGateway基于Redis的限流组件&#xff1f; 基于这个问题&#xff0c;笔者想了解一下scg自带限流组件的实现原理。 一、使用案例 1、pom 注意要加入redis-reactive依赖。 <depe…

OA系统遇到的问题

目录 一、开始时间与结束时间之差 二、弹出层的大小以及位置设置 2.1、高度设置body-style 2.2、位置设置dialogStyle 三、vue2安装引入Ant Design Vue 四、按钮控制盒子的显示与隐藏 五、表单生成器思想 5.1、点击左侧控件库生成中间的控件元素 5.2、点击中间的控件&…

Flink-状态编程的基本概念

文章目录Flink 中的状态1.1 有状态算子1.2 状态的管理1.3 状态的分类&#x1f48e;&#x1f48e;&#x1f48e;&#x1f48e;&#x1f48e; 更多资源链接&#xff0c;欢迎访问作者gitee仓库&#xff1a;https://gitee.com/fanggaolei/learning-notes-warehouse/tree/master Fli…

springcloud-gateway简介

目录 1. gateway简介 1.1 是什么 1.2 作用 1.3 主要特征 1.4 与zuul的主要区别 1.5 主要组件 1.6 架构图 2. 开发示例 2.1 创建一个gateway模块 2.2 与nacos结合使用 2.2.1 默认规则 2.2.2 通过配置文件配置路由 2.2.3 动态路由 1. gateway简介 1.1 是什么 SpringC…

一文带你深入理解【Java基础】· 网络编程(上)

写在前面 Hello大家好&#xff0c; 我是【麟-小白】&#xff0c;一位软件工程专业的学生&#xff0c;喜好计算机知识。希望大家能够一起学习进步呀&#xff01;本人是一名在读大学生&#xff0c;专业水平有限&#xff0c;如发现错误或不足之处&#xff0c;请多多指正&#xff0…

Kali Linux中安装IDLE的方法

1 IDLE简介 IDLE是Integrated Development and Learning Enviroment即集成开发和学习环境的简称&#xff0c;是Python的集成开发环境。在Kali Linux中&#xff0c;可以通过IDLE进行Python编程。 2 Kali Linux中安装IDLE 2.1 查看Kali Linux中是否安装IDLE 在Kali Linux终端…

WEB1.0起源:全球首个网站info.cern.ch

伯纳斯李&#xff08;图&#xff09;1990年创立第一个网站。 info.cern.ch是世上第一个网站&#xff0c;提供有关万维网的资料。 info.cern.ch这个网站依然运作如常。 英国科学家蒂姆伯纳斯-李 (Tim Berners-Lee) 于 1989 年在 CERN 工作期间发明了万维网 (WWW)。Web 最初的构思…

基于Vue+SpringBoot智慧校园疫情防控系统(PC端、手机端)--附源码

介绍 智慧校园疫情防控系统——PC 手机端 多端并行 项目源码下载&#xff1a;https://download.csdn.net/download/DeepLearning_/87340321 软件架构 手机端信息系统——日常健康信息填报系统&#xff08;前端手机端 文件夹&#xff09;电脑端智疫图 —— 数据可视化界面 &…

一种新的语义分割思路

这两天看到一篇挺有意思的论文&#xff0c;虽然不是语义分割方面的但是挺有意思的&#xff0c;因此在这里跟大家分享一下&#xff0c;这个也是一种语义分割的思路和方法。 Paper&#xff1a;Fully-Convolutional Siamese Networks for Object Tracking. SiamFC是深度学习目标…

【深入浅出 Yarn 架构与实现】4-2 RM 管理 Application Master

上一篇文章对 ResourceManager 整体架构和功能进行了讲述。本篇将对 RM 中管理 Application Master 的部分进行深入的讲解。 下面将会介绍 RM 与 AM 整体通信执行流程&#xff0c;并对 RM 中涉及的对应服务进行具体讲解。 为了更好的学习本篇知识&#xff0c;建议先熟悉以下知识…