第8章 动态SQL

Posted shoulinniao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第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();
    

测试结果:

技术图片

 

以上是关于第8章 动态SQL的主要内容,如果未能解决你的问题,请参考以下文章

第8章 动态渲染页面爬取

第8章 数据库后台编程技术

第8章:让开发板发出声音:蜂鸣器

SQL基础教程(第2版)第8章 SQL高级处理:8-1 窗口函数

8-4-存储紧缩-动态存储管理-第8章-《数据结构》课本源码-严蔚敏吴伟民版

8-1-边界标识法-动态存储管理-第8章-《数据结构》课本源码-严蔚敏吴伟民版