• 第8章 动态SQL


    8.1动态SQL中的元素

    8.2<if>元素

    举例,在映射文件中:

      <select id="findCustomerByNameAndJobs" parameterType="com.itheima.po.Customer" 
                                               resultType="com.itheima.po.Customer"> 
            select * from t_customer where 1=1
            <if test="username !=null and username !=''"> 
                <!-- and username like concat('%',#{username},'%')  -->
                and username like '%${username}%'
            </if> <if test="jobs !=null and jobs !=''"> 
                and jobs= #{jobs} 
            </if> 
        </select>

    test属性多用于判断,判断真假,大部分情况都是用作非空判断。有时候也需要判断字符串、数字和枚举等,如果传入的查询条件非空就进行动态SQL组装。

    大白话:此时的作用,就是,如果username不空 并且 不为空值。(一个是空,没有地址,也没有值;另一个是有地址,也有值,但是值是空),u符合条件就拼接and username like '%${username}%'这条语句,jobs同理。

    符合if条件拼起来的语句就是

    select * from t_customer where 1=1 and username like '%${username}%' and jobs= #{jobs} 

    '%${username}%':%表示通配符,可以表示无限个字符,0也可以,这里表示只要有包含username的值就会被查出来,username的值会在对象customer被创建出来后设置。

    在测试方法中:

    /**
         * 根据客户姓名和职业组合条件查询客户信息列表
         */
        @Test
        public void findCustomerByNameAndJobsTest(){
            
            // 通过工具类生成SqlSession对象
            SqlSession session = MybatisUtils.getSession();
            
            // 创建Customer对象,封装需要组合查询的条件
            Customer customer = new Customer();
            customer.setUsername("jack");
            customer.setJobs("teacher");
            
            // 执行SqlSession的查询方法,返回结果集
           List<Customer> customers = session.selectList("com.itheima.mapper" 
                   + ".CustomerMapper.findCustomerByNameAndJobs",customer);
           
            // 输出查询结果信息
            for (Customer customer2 : customers) {
                // 打印输出结果
                System.out.println(customer2);
            }
            
            // 关闭SqlSession
            session.close();
        }

    大白话:创建对象customer,然后对它设值,表示要在表中查询像这个对象的数据项,这里表示要查找 username中有包含有“jack”(无论在哪个位置)并且jobs的值是“teacher”的数据项。把java语句映射成SQL语句去数据库里查询,把查询得到的结果放在Customer列表中,然后输出。在关闭执行器。

    专业术语:使用Customer对象封装了用户名为jack且职业为teacher的查询条件,并通过SqlSession对象的selectList()方法执行多条件组合的查询操作。

    如果把两条set语句注释掉,再次执行执行的SQL语句是:select * from t_customer where 1=1,所有数据项都会被查出来。这里表明:当未传递任何参数时,程序会将数据表中所有的数据查出,这就是<if>元素的使用。

    如果在映射文件中去掉1=1这个真值,则SQL语句变成:select * from t_customer where and username like '%jack%' and jobs= ?

    显然where后直接跟and会有语法错误。1=1的作用是避免where后面第一个词是and或者or之类的关键词,导致报错。

    8.3 <choose>、<when>、<otherwise>元素

    这些类似于java语句中的switch、case和default语句。

    举例,在映射文件中:

        <!--<choose>(<when>、<otherwise>)元素使用 -->
        <select id="findCustomerByNameOrJobs" parameterType="com.itheima.po.Customer"
            resultType="com.itheima.po.Customer">
            select * from t_customer where 1=1
            <choose>
                <when test="username !=null and username !=''">
                    and username like concat('%',#{username}, '%')
                </when>
                <when test="jobs !=null and jobs !=''">
                    and jobs= #{jobs}
                </when>
                <otherwise>
                    and phone is not null
                </otherwise>
            </choose>
        </select>

    大白话:在<choose>元素中,有好多好多个<when>元素,如果第一个<when>元素的test为真,只动态组装第一个<when>元素里的SQL片段,否则看第二个<when>元素,如果第二个为真,则只拼接第二个<when>元素里的SQL片段,以此类推。当前面所有的<when>元素中的test都不为真时,只组装<otherwise>元素内的SQL片段。相当于switch和case、default。

    测试方法:

    /**
         * 根据客户姓名或职业查询客户信息列表
         */
        @Test
        public void findCustomerByNameOrJobsTest(){
            // 通过工具类生成SqlSession对象
            SqlSession session = MybatisUtils.getSession();
            // 创建Customer对象,封装需要组合查询的条件
            Customer customer = new Customer();
            customer.setUsername("jack");
            customer.setJobs("teacher");
            // 执行SqlSession的查询方法,返回结果集
            List<Customer> customers = session.selectList("com.itheima.mapper" 
                    + ".CustomerMapper.findCustomerByNameOrJobs",customer);
            // 输出查询结果信息
            for (Customer customer2 : customers) {
                // 打印输出结果
                System.out.println(customer2);
            }
            // 关闭SqlSession
            session.close();
        }

    运行的SQL语句:select * from t_customer where 1=1 and username like concat('%',?, '%') 

    如果只注释掉customer.setUsername("jack")语句则运行的SQL语句:select * from t_customer where 1=1 and jobs= ? 

    如果只注释掉customer.setJobs("teacher")语句则运行的SQL语句:select * from t_customer where 1=1 and username like concat('%',?, '%') 

    如果都注释掉,则运行的SQL语句:select * from t_customer where 1=1 and phone is not null 

    concat起连接作用,实现模糊查询,这里效果等同'%${username}%'。

    8.4 <where>、<trim>元素

    又想防止语法错误又不想写1=1。

    举例1,在映射文件中:

    <!-- <where>元素 没有1=1的,用where元素代替  -->

    <select id="findCustomerByNameAndJobs" parameterType="com.itheima.po.Customer" resultType="com.itheima.po.Customer"> select * from t_customer <where> <if test="username !=null and username !=''"> and username like concat('%',#{username},'%') </if> <if test="jobs !=null and jobs !=''"> and jobs= #{jobs} </if> </where> </select>

    不仅不要1=1,连where都换成元素了。<where>元素会自动判断组合条件下拼接的SQL语句,只有<where>元素内的条件成立时,才会在拼接SQL中加入where关键字,否则将不会添加;即使where之后的内容有多余的and或者or都会自动删除。

    举例2,在映射文件中:通过<trim>元素来定制需要的功能

      <select id="findCustomerByNameAndJobs" parameterType="com.itheima.po.Customer"
            resultType="com.itheima.po.Customer">
            select * from t_customer 
            <trim prefix="where" prefixOverrides="and">
                <if test="username !=null and username !=''">
                    and username like concat('%',#{username}, '%')
                </if>
                <if test="jobs !=null and jobs !=''">
                    and jobs= #{jobs}
                </if>
            </trim>
        </select>

    专业话:<trim>元素的作用是除去一些特殊的字符串,他的prefix属性代表的是语句的前缀(这里使用where来连接后面的SQL片段),而prefixOverrides属性代表的是需要除去的那些特殊字符串(这里定义除去SQL中的and),上面的写法和使用<where>元素基本是等效的。

    8.5<set>元素

    set是设置的意思,不是集合的意思

    在Hibernate中,想要更新某个对象,就需要发送所有的字段给持久化对象,这种想更新的每一条数据都要将其所有的属性都更新一遍的方法,其执行效率非常差的。为此,在MyBatis中可以使用动态SQL中的<set>元素进行处理:使用<set>和<if>元素对username和jobs进行更新判断,并动态组装SQL。这样就只需要传入想要更新的字段即可。

    举例,在映射文件中:

        <update id="updateCustomer" parameterType="com.itheima.po.Customer">
            update t_customer
            <set>
                <if test="username !=null and username !=''">
                    username=#{username},
                </if>
                <if test="jobs !=null and jobs !=''">
                    jobs=#{jobs},
                </if>
                <if test="phone !=null and phone !=''">
                    phone=#{phone},
                </if>
            </set>
            where id=#{id}<!--  id=#{id}-->
        </update>    

     测试方法:

    /**
         * 更新客户
         */
        @Test
        public void updateCustomerTest() {        
            // 获取SqlSession
            SqlSession sqlSession = MybatisUtils.getSession();
            // 创建Customer对象,并向对象中添加数据
            Customer customer = new Customer();
            customer.setId(3);
            customer.setPhone("13311111234");
            customer.setUsername("bossli");
            // 执行SqlSession的更新方法,返回的是SQL语句影响的行数
            int rows = sqlSession.update("com.itheima.mapper"
                    + ".CustomerMapper.updateCustomer", customer);
            // 通过返回结果判断更新操作是否执行成功
            if(rows > 0){
                System.out.println("您成功修改了"+rows+"条数据!");
            }else{
                System.out.println("执行修改操作失败!!!");
            }
            // 提交事务
            sqlSession.commit();
            // 关闭SqlSession
            sqlSession.close();
        }

    运行结果:

    如果映射文件中“where id=#”改成“where 1=1”则修改全部数据。

     8.6 <foreach>元素

       <!--<foreach>元素使用 -->
        <select id="findCustomerByIds" parameterType="List"
            resultType="com.itheima.po.Customer">
            select * from t_customer where id in
            <foreach item="id" index="index" collection="list" open="("
                separator="," close=")">
                #{id}
            </foreach>
        </select>

    item:配置的是循环中当前的元素。

    index:配置的是当前元素在集合的位置下标。

    collection:配置的是list传递过来的参数类型(首字母小写),它可以是一个array、list(或collection)、Map集合的键、POJO包装类中数组或集合类型的属性名等。

    open和close:配置的是以什么符号将这些集合元素包装起来。

    separator:配置的是各个元素的间隔符。

    测试方法:

    /**
         * 根据客户编号批量查询客户信息
         */
        @Test
        public void findCustomerByIdsTest(){
            // 获取SqlSession
            SqlSession session = MybatisUtils.getSession();
            
            // 创建List集合,封装查询id
            List<Integer> ids=new ArrayList<Integer>();
            ids.add(1);
            ids.add(2);
            ids.add(3);
            
            //等于下面
    //        for(int i=1;i<=3;i++)
    //            ids.add(i);
            
            
            // 执行SqlSession的查询方法,返回结果集
            List<Customer> customers = session.selectList("com.itheima.mapper"
                    + ".CustomerMapper.findCustomerByIds", ids);
            
            // 输出查询结果信息    
            for (Customer customer : customers) {
                System.out.println(customer);
            }
            
            // 关闭SqlSession
            session.close();
        }

    运行结果:

    在使用<foreach>时最关键也是最容易出错的就是collection属性,该属性是必须指定的(按实际情况进行配置),而且在不同情况下,该属性的值是不一样的。主要有以下3种情况:

    a)、如果传入的是单参数且参数类型是一个数组或者List的时候,collection属性值分别为array和list(或collection)。

    b)、如果传入的参数是多个的时候,就需要把它们封装成一个Map了,当然单参数也可以封装成Map集合,这时候collection属性值就为Map的键。

    c)、如果传入的参数是POJO包装类的时候,collection属性值就为该包装类中需要进行遍历的数组或集合的属性名。

    8.7 <bind>元素

    SQL语句:select * from t_customer where username like '%${value}%'

    不妥之处:

    a)、如果使用“${}”进行字符串拼接,则无法防止SQL注入问题;

    b)、如果改用concat函数进行拼接,则只针对MySQL数据库有效;

    c)、如果改用“||”进行字符串拼接,则只针对Oracle数据库有效。

    总之:能用#就别用$

    例如,在映射文件中:

    <!--<bind>元素的使用:根据客户名模糊查询客户信息 -->
        <select id="findCustomerByName" parameterType="com.itheima.po.Customer"
            resultType="com.itheima.po.Customer">
            <!--_parameter.getUsername()也可直接写成传入的字段属性名,即username -->
            <bind name="pattern_username" value="'%'+_parameter.getUsername()+'%'" />
            select * from t_customer
            where
            username like #{pattern_username}
        </select>

    专业话:<bind>元素定义了一个name为patter_username的变量,<bind>元素中value的属性值就是拼接的查询字符串,其中_parameter.getUsername()表示传递进来的参数(也可以直接写成对应的参数变量名,如username)。

    其中_parameter这个东西是固定的。

    测试方法:

    /**
         * bind元素的使用:根据客户名模糊查询客户信息 
         */
        @Test
        public void findCustomerByNameTest(){
            
            // 通过工具类生成SqlSession对象
            SqlSession session = MybatisUtils.getSession();
            
            // 创建Customer对象,封装查询的条件
            Customer customer =new Customer();
            customer.setUsername("j");
            
            // 执行sqlSession的查询方法,返回结果集
            List<Customer> customers = session.selectList("com.itheima.mapper"
                    + ".CustomerMapper.findCustomerByName", customer);
            // 输出查询结果信息    
            for (Customer customer2 : customers) {
                System.out.println(customer2);
            }
            // 关闭SqlSession
            session.close();
        }

    测试结果:

  • 相关阅读:
    题解 CF700E Cool Slogans
    题解 LOJ2065 「SDOI2016」模式字符串
    以guest账号无密码访问设置
    共享
    计算机的C$共享在哪里
    网卡工作原理
    iperf网络测试
    Jmeter安装与使用(压测)
    压测工具使用(vegeta)
    Alertmanager 安装(k8s报警)
  • 原文地址:https://www.cnblogs.com/shoulinniao/p/10982797.html
Copyright © 2020-2023  润新知