zoukankan      html  css  js  c++  java
  • MyBatis学习——动态SQL

      开发人员在使用JDBC框架或者其他类似的框架进行数据库开发时,通常都要根据需求去手动拼接SQL,这样非常麻烦,而myBatis提供了对SQL语句动态组装的功能,恰好解决了这一问题。

    一,动态SQL中的元素

      动态SQL是MyBatis的强大特性之一,MyBatis 3后采用了基于OGNL的表达式来完成动态SQL,

      MyBatis动态SQL中的主要元素,如下:

    元素 说明
    <if> 判断语句,用于单条件分支判断
    <choose>(<when>,<otherwise>) 相当于Java中的switch..case...default
    <where>,<trim>,<set> 补助元素,用于处理一些SQL拼接,特殊字符问题
    <foreach> 循环语句,常用于in语句等例举条件中
    <bind> 从OGNL表达式中创建一个变量,并将其绑定到上下文中,常用于模糊查询的sql中

    二.

    1.<if>元素:通常用于通过多个条件来精确查询某个数据

      在MyBatis中,<if>元素是最常用的判断语句,它类似Java中的if语句,主要用于简单的条件判断。

      (1)在映射文件中使用<if>元素来编写根据客户名和客户职业组合信息来查询客户信息.

    <!-- 使用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},'%')
            </if>
            <if test="jobs != null and jobs !='' ">
                and jobs=#{jobs}
            </if>                                      
        </select>

      使用<if>元素的test属性对username和jobs进行了非空判断(test属性常用于条件判断中,用于判断真假,大部分时候是用于进行非空判断),如果传入的条件非空,就进行sql拼接。

      (2)在测试类中,编写测试方法findCustomerByNameAndJobs:

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

      (3)测试结果:

       

      (4)如果将上面代码中的设值注释掉,如下,就会将所有的信息都输出来: 

     1     /**
     2      * 根据用户名和用户的工作来查询用户
     3      */
     4     @Test
     5     public void findCustomerByNameAndJobsTest() {
     6         //通过工具类来生成SqlSession对象
     7         SqlSession sqlSession=MyBatisUtils.getSession();
     8         //创建Customer对象,封装需要组合查询的条件
     9         Customer customer=new Customer();
    10         //customer.setJobs("teacher");
    11         //customer.setUsername("jack");
    12         
    13         //执行SqlSession对象的查询方法,返回一个结果集
    14         List<Customer> customers=sqlSession.selectList("com.itheima.mapper.CustomerMapper.findCustomerByNameAndJobs", customer);
    15         //输出查询结果
    16         for(Customer customer2:customers) {
    17             System.out.println(customer2);
    18         }
    19         
    20         //关闭sqlSession
    21         sqlSession.close();
    22     }

         

          

     2.<choose>,<when>,<otherwise>:从多个选项中选一个去执行

      在<if>中,只要test属性中的条件为真,就去执行元素中的条件语句,但是有时候在实际生活中是需要从多个选项中选择一个去执行,例如

      当客户名不可空,则只根据客户名进行客户筛选,

      当客户名为空,职业不为空时就根据客户职业进行客户筛选,

      如果两者都为空,就查询出所有电话不为空的客户信息

      在此种情况下,是不能使用<if>元素的,针对如上情况,可以使用<choose>,<when>,<otherwise>元素组合实现上面的情况

      (1).在映射文件中配置如下

    <!-- <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>,<otherwise>组合,当第一个<when>元素中的条件为真时,则只动态地组装第一个<when>元素中的条件,否则就继续向下判断第二个<when>元素中的条件是否为真,依次类推,如果<when>中的条件都不为真时,就会执行<otherwise>内的SQL片段。

      (2)创建测试方法

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

      (3)测试结果

           

      (4)当把customer.setUsername("jack")注释掉后,结果为:

            

      (5)如果把customer.setJobs(“teacher”)也注释掉后,结果为:

           

      

    3.<where>,<trim>元素

      在前面的小节中编写的SQL后面都添加了“where 1=1 "这个条件,那么为什么要这么写呢?是应为如果将后面的”where 1=1“去掉,那么MyBatis拼接出来的SQL语句将会出错

      SELECT *from t_customer  where and usename like  concat( '%',#{username}, '%');

      在上面的语句中,where后面跟的是and,这样将出错,而加入了where 1=1 后,既保证了where后面的条件成立,也保证了避免where后面跟着是and或者or.

    那么在Mybatis中有什么办法是可以不加入”1=1“这个条件也可以呢?

      MyBatis提供了<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>

      在上面的语句中,使用<where>元素取代了”where 1=1“, where元素会自动判断组合条件下的拼接的sql语句,只有当<where>里的条件成立时,才会在拼接sql时,加入where元素,否则不会添加,即使where后面有多余的"and",“or”,<where>元素会自动将它们除去。

    4.<set>元素

      在Hibernate中,如果要更新一条数据,就需要发送所有的字段给持久化对象,然而在实际应用中,大多数情况下,是只跟新某一个字段或者几个字段,如果更新的每一条数据都要将所有的属性都更新一遍,那么其执行效率是非常的低,有没有办法使程序只更新需要更新的字段?

      在MyBatis中,提供了<set>元素来完成这一工作,<set>元素的作用用于更新操作,其主要作用是在动态包含的SQL语句前输出一个SET关键字,并且将SQL语句中最后一个多余的逗号去除。

      例如,在入门案例中,使用<set>元素对更新操作的代码进行修改如下:

    <!-- set元素的使用 -->
        <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}
            
        </update>

       在上面的sql语句中,使用了<set>和<if>语句相组合的方法将组装update语句,其中<set>元素会自动的动态前置SET关键字,同时也会消除sql语句的最后一个多余的逗号,<if>元素是用来判断相应的字段是否传入值,如果传入的更新字段非空,就将此字段进行动态的组装,并且更新此字段,否则此字段不更新。

      在测试类中编写如下代码:

        /**
         *使用set 更新客户
         */
        @Test
        public void updateCustomer1Test() {
            //1.通过工具类来生成SqlSession对象
            SqlSession sqlSession=MyBatisUtils.getSession();
            //2.创建Customer对象,并向对象中添加数据
            Customer customer=new Customer();
            customer.setId(3);
            customer.setPhone("444444");
            //3.执行sqlSession的更新方法,返回的是受影响的行数
            int rows=sqlSession.update("com.itheima.mapper.CustomerMapper.updateCustomer", customer);
            //4.根据返回的结果来判断是否更新成功
            if(rows>0) {
                System.out.println("更新成功!");
            }else {
                System.out.println("更新失败!");
            }
            
            //5.提交事务
            sqlSession.commit();
            //6.关闭事务
            sqlSession.close();
        }
        

      测试结果如下:

      

       注意:使用<set>和<if>元素组合进行update语句动态的SQL组装时,如果<set>中的内容都为空,则会出现sql语法错误,所以在<set>元素进行动态更新时,要确保传入的更新字段不为空。

    5.<foreach>元素

       该元素是用于数组和集合循环遍历。

      <foreach>元素是通常在构建IN条件语句时使用的,其使用方法如下

    <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>

      在上述代码中,使用了<foreach>元素对传入的集合进行了遍历并进行了动态的SQL组转,其属性如下:

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

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

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

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

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

      在测试类中编写如下代码,验证

    /**
         * 根据客户编号批量查询客户信息
         */
        @Test
        public void findCustomerByIdsTest() {
            //1.获取SqlSession对象
            SqlSession sqlSession=MyBatisUtils.getSession();
            //2.创建一个list集合,用来封装查询的id值
            List<Integer> ids=new ArrayList<Integer>();
            ids.add(2);
            ids.add(3);
            //3.执行SqlSession的查询方法,返回结果集
            List<Customer> customers=sqlSession.selectList("com.itheima.mapper.CustomerMapper.findCustomerByIds", ids);
            //4.输出查询的结果
            for(Customer customer:customers) {
                System.out.println(customer);
            }
            //5.关闭sqlSession
            sqlSession.close();
        }

      测试结果如下:

        

      注意:在使用<foreach>元素时,应注意的事项是collection属性值,该属性是必须要指定的,并且在不同的情况下,该属性的值是不同的,主要三种情况:

      (1)如果在调用该sql语句时,传入的是一个参数,并且参数的类型是一个数组或者是list的时候,该collection属性值就是array和list(或者collection)

      (2)如果传入的是多个参数的时候,就需要把它们封装成一个Map,单个参数也是可以这样封装的,这时候collection属性值就是Map的键

      (3)如果传进来的是POJO的包装类的时候,collection属性值就是该包装类中需要进行遍历的数组或者集合的属性名。

  • 相关阅读:
    MS CRM 2011的自定义和开发(10)——CRM web服务介绍(第一部分)——IDiscoveryService
    MS CRM 2011的自定义和开发(7)——视图编辑器(第二部分)
    MS CRM 2011 SDK 5.06版本已经发布
    MS CRM 2011的自定义和开发(11)——插件(plugin)开发(一)
    近来遇到的MS CRM 2011方面的几个问题
    MS CRM 2011的自定义与开发(6)——表单编辑器(第二部分)
    Microsoft Dynamics CRM 2011中,Lookup字段的赋值
    MS CRM 2011的自定义和开发(6)——表单编辑器(第三部分)
    Visual Studio 目标框架造成 命名空间“Microsoft”中不存在类型或命名空间名称“Crm”。是否缺少程序集引用中错误的处理
    一步步学习Reporting Services(二) 在报表中使用简单的参数作为查询条件
  • 原文地址:https://www.cnblogs.com/zhilili/p/11604494.html
Copyright © 2011-2022 走看看