动态SQL的处理

学习视频:3001 动态SQL中的元素_哔哩哔哩_bilibili

目录

1.1为什么学

1.2动态SQL中的元素

条件查询操作

[if 元素](#if 元素)

choose、when、otherwise元素

where、trim元素

更新操作

set元素使用场景

复杂查询操作

[foreach 元素中的属性](#foreach 元素中的属性)

​编辑

迭代数组

迭代List

迭代Map

1.3案例:学生信息查询系统

总结:


1.1为什么学

在实际项目的开发中,开发人员在使用JDBC或其他持久层框架进行开发时,经常需要根据不同的条件拼接SQL语句,拼接SQL语句时还要确保不能遗漏必要的空格、标点符号等,这种编程方式给开发人员带来了非常大的不便,而MyBatis提供的SQL语句动态组装功能,恰能很好地解决这一问题。


1.2动态SQL中的元素

使用动态SQL的好处

动态SQL 是MyBatis的强大特性之一,MyBatis采用了功能强大的基于OGNL (Object Graph Navigation Language)的表达式来完成动态SQL。在MyBatis的映射文件中,开发人员可通过动态SQL元素灵活组装SQL语句,这在很大程度上避免了单一SQL语句的反复堆砌,提高了SQL语句的复用性。

动态SQL常用元素

条件查询操作

<if>元素

在MyBatis中,<if>元素是最常用的判断元素,它类似于Java中的if语句,主要用于实现某些简单的条件判断。在实际应用中,我们可能会通过某个条件查询某个数据。例如,要查找某个客户的信息,可以通过姓名或者年龄来查找客户,也可以不填写年龄直接通过姓名来查找客户,还可以都不填写而查询出所有客户,此时姓名和年龄就是非必须条件。类似于这种情况,在MyBatis中就可以通过<if>元素来实现。

数据库准备

在名称为mybatis的数据库中,创建一个t_customer表,并插入几条测试数据。

sql 复制代码
USE mybatis;
CREATE TABLE t_customer (
    id int(32) PRIMARY KEY AUTO_INCREMENT,
    username varchar(50),
    jobs varchar(50),
    phone varchar(16));
INSERT INTO t_customer VALUES ('1', 'joy', 'teacher', '13733333333');
INSERT INTO t_customer VALUES ('2', 'jack', 'teacher', '13522222222');
INSERT INTO t_customer VALUES ('3', 'tom', 'worker', '15111111111');

POJO类准备

创建持久化类Customer,在类中声明id、username、jobs和phone属性,及属性对应的getter/setter方法。

java 复制代码
public class Customer {
     private Integer id;   private String username; // 主键ID、客户名称
     private String jobs;  private String phone;      // 职业、电话
     // 省略getter/setter方法
     @Override
     public String toString() {
           return "Customer [id=" + id + ", username=" + username + ", jobs=" + jobs + ", phone=" + phone + "]"; }
}

创建映射文件

创建映射文件CustomerMapper.xml,在映射文件中,根据客户姓名和年龄组合条件查询客户信息,使用<if>元素编写该组合条件的动态SQL。

XML 复制代码
<!-- 该xml文件中只列出了if元素的动态SQL-->
<if test="username !=null and username !=''">
       and username like concat('%',#{username}, '%')
</if>
<if test="jobs !=null and jobs !=''">
     and jobs= #{jobs}
</if>

修改核心配置文件

在配置文件mybatis-config.xml中,引入CustomerMapper.xml映射文件,将CustomerMapper.xml映射文件加载到程序中。

XML 复制代码
<mapper 
resource="com/it/mapper/CustomerMapper.xml">
</mapper>

创建获取SqlSession对象的工具类

java 复制代码
public class MybatisUtils {
	private static SqlSessionFactory sqlSessionFactory = null;
	// 初始化SqlSessionFactory对象
	static {
		try {
			// 使用MyBatis提供的Resources类加载MyBatis的配置文件
			Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
			// 构建SqlSessionFactory工厂
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	// 获取SqlSession对象的静态方法
	public static SqlSession getSession() {
		return sqlSessionFactory.openSession();
	}
}

编写测试类

在测试类MyBatisTest中,编写测试方法findCustomerByNameAndJobsTest(),该方法用于根据客户姓名和职业组合条件查询客户信息列表。

java 复制代码
public class MyBatisTest { 
   @Test
    public void findCustomerByNameAndJobsTest(){SqlSession session = 	MyBatisUtils.getSession();Customer customer = new Customer();
        customer.setUsername("jack");  customer.setJobs("teacher");
        List<Customer> customers = session.selectList("com.itheima.mapper"
                + ".CustomerMapper.findCustomerByNameAndJobs",customer);
         for (Customer customer2 : customers) { System.out.println(customer2); }
        session.close();}
}

MyBatisTest.java

java 复制代码
package com.it.test;

import com.it.pojo.Customer;
import com.it.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class MyBatisTest {
    @Test
        public void findCustomerByUsernameAndJobsTest()
        {
            SqlSession sqlSession= MyBatisUtils.getSession();
            Customer customer=new Customer();
            customer.setUsername("jack");
            customer.setJobs("teacher");
            //执行sql
        List<Customer> customers=sqlSession.selectList("com.it.mapper.CustomerMapper.findCustomerByUsernameAndJobs",customer);

        for (Customer c:customers)
        {
            System.out.println(c);
        }
            //释放资源
            sqlSession.close();

        }


}

CustomerMapper.xml

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<!--
      mapper为映射的根节点,用来管理DAO接口
      namespace指定DAO接口的完整类名,表示mapper配置文件管理哪个DAO接口(包.接口名)
      mybatis会依据这个接口动态创建一个实现类去实现这个接口,而这个实现类是一个Mapper对象
   -->
<mapper namespace="com.it.mapper.CustomerMapper">
    <!--
          id = "接口中的方法名"
          parameterType = "接口中传入方法的参数类型"
          resultType = "返回实体类对象:包.类名"  处理结果集 自动封装
          注意:sql语句后不要出现";"号
              查询:select标签
              增加:insert标签
              修改:update标签
              删除:delete标签
      -->
     <select id="findCustomerByUsernameAndJobs" parameterType="com.it.pojo.Customer"  resultType="com.it.pojo.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>
</mapper>

mybatis-config.xml

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd" >
<!--配置mybatis环境-->
<configuration>
    <!--配置连接使用的相关参数
          default为默认使用的环境:development 测试环境
                                product     生产环境
      -->
    <properties resource="jdbc.properties"></properties>
    <environments default="development">
        <!--测试环境-->
        <environment id="development">
            <!--事务管理类型:指定事务管理的方式 JDBC-->
            <transactionManager type="JDBC"/>
            <!--数据库连接相关配置,动态获取config.properties文件里的内容-->
            <!--数据源类型:POOLED 表示支持JDBC数据源连接池
                            UNPOOLED 表示不支持数据源连接池
                            JNDI 表示支持外部数据源连接池
              -->
            <dataSource type="POOLED">
                <!--此处使用的是MySQL数据库,使用Oracle数据库时需要修改,仔细检查各项参数是否正确,里面配置了时区、编码方式、SSL,用以防止中文查询乱码,导致查询结果为null及SSL警告等问题-->
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url"
                          value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <!--下面编写mapper映射文件↓↓↓↓↓ 参考格式:<mapper resource="dao/UserMapper.xml"/> -->
        <mapper resource="com/it/mapper/CustomerMapper.xml"></mapper>
    </mappers>

</configuration>

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

<choose><when>otherwise>使用场景

在使用<if>元素时,只要test属性中的表达式为true,就会执行元素中的条件语句,但是在实际应用中,有时只需要从多个选项中选择一个去执行。

例如下面的场景:"当客户名称不为空,则只根据客户名称进行客户筛选;当客户名称为空,而客户职业不为空,则只根据客户职业进行客户筛选。当客户名称和客户职业都为空,则要求查询出所有电话不为空的客户信息。"

针对上面情况,使用<if>元素进行处理是不合适的。MyBatis提供了<choose>、<when>、<otherwise>元素进行处理,这三个元素往往组合在一起使用,作用相当于Java语言中的if...else if...else。

XML 复制代码
 <select id="findCustomerByUsernameOrJobs"  parameterType="com.it.pojo.Customer" resultType="com.it.pojo.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>
java 复制代码
@Test
    public void findCustomerByUsernameOrJobsTest()
    {
        SqlSession sqlSession= MyBatisUtils.getSession();
        Customer customer=new Customer();
        customer.setUsername("jack");
        customer.setJobs("teacher");
        //执行sql
        List<Customer> customers=sqlSession.selectList("com.it.mapper.CustomerMapper.findCustomerByUsernameOrJobs",customer);

        for (Customer c:customers)
        {
            System.out.println(c);
        }
        //释放资源
        sqlSession.close();

    }

<where>、<trim>元素

<where><trim>使用场景

在映射文件中,编写的SQL后面加入了"where 1=1"的条件的话,既保证了where后面的条件成立,又避免了where后面第一个词是and或者or之类的关键字。 例如下面这条Mybatis拼接出的SQL语句是不正确的。

sql 复制代码
select * from t_customer where and username like
 concat('%',?, '%') and jobs = #{jobs}

上述SQL语句中,where后直接跟的是and,这在运行时会报SQL语法错误,针对这种情况,可以使用MyBatis提供的<where>元素和<trim>元素进行处理。

XML 复制代码
 <select id="findCustomerByUsernameAndJobs"  parameterType="com.it.pojo.Customer" resultType="com.it.pojo.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>

如果2个if都不满足,where会被忽视

上述代码配置中,<where>元素会自动判断由组合条件拼装的SQL语句,只有<where>元素内的某一个或多个条件成立时,才会在拼接SQL中加入where关键字,否则将不会添加;即使where之后的内容有多余的"AND"或"OR",<where>元素也会自动将他们去除。


<trim>元素

<trim>元素用于删除多余的关键字,它可以直接实现<where>元素的功能。<trim>元素包含4个属性。

XML 复制代码
    <select id="findCustomerByUsernameAndJobs"  parameterType="com.it.pojo.Customer" resultType="com.it.pojo.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>

更新操作

<set>元素使用场景

在Hibernate框架中,如果想要更新某一个对象,就需要发送所有的字段给持久化对象,然而在实际应用中,大多数情况下都是更新某一个或几个字段。如果更新的每一条数据都要将其所有的属性都更新一遍,那么执行效率是非常差的。为了解决更新数据的效率问题,MyBatis提供了<set>元素。<set>元素主要用于更新操作,它可以在动态SQL语句前输出一个SET关键字,并将SQL语句中最后一个多余的逗号去除。<set>元素与<if>元素结合可以只更新需要更新的字段。

映射文件

XML 复制代码
<update id="updateCustomerBySet" parameterType="com.it.pojo.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>

测试

java 复制代码
 @Test
    public void updateCustomerBySetTest()
    {
        SqlSession session =MyBatisUtils.getSession();

        Customer customer=new Customer();
        customer.setId(3);
        customer.setPhome("12345678912");
      int rows=  session.update("com.it.mapper.CustomerMapper.updateCustomerBySet",customer);
      if(rows>0)
      {
          System.out.println("修改了"+rows+"条数据");
      }
      else
      {
          System.out.println("没有修改数据");
      }


        session.commit();
        session.close();

    }

<set>元素字段非空

在映射文件中使用<set>元素和<if>元素组合进行update语句动态SQL组装时,如果<set>元素内包含的内容都为空,则会出现SQL语法错误。因此,在使用<set>元素进行字段信息更新时,要确保传入的更新字段不能都为空。

使用<trim>元素更新

除了使用<set>元素外,还可以通过<trim>元素来实现更新操作。其中, <trim>元素的prefix属性指定要添加的<trim>元素所包含内容的前缀为set,suffixOverrides属性指定去除的<trim>元素所包含内容的后缀为逗号 。

XML 复制代码
<update id="updateCustomerBySet" parameterType="com.it.pojo.Customer">update t_customer
        <trim prefix="set" suffixOverrides=",">
            <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>
        </trim> where id=#{id}
    </update>

复杂查询操作

<foreach>元素中的属性

<collection>属性的取值

在遍历参数时,<collection>属性的值是必须指定的。不同情况下,该属性的取值也是不一样的,主要有以下三种情况:List类型、数组类型、Map类型。

List->list

数组类型->array

Map->Map


<foreach>元素迭代数组

<foreach>实现入参为数组类型的遍历

例如,要从数据表t_customer中查询出id为1、2、3的客户信息,就可以利用数组作为参数,存储id的属性值1、2、3,并通过<foreach>元素迭代数组完成客户信息的批量查询操作。

XML 复制代码
<select id="findByArray"
            resultType="com.it.pojo.Customer">
    select * from t_customer where id in
    <foreach item="id" index="index" collection="array"
             open="(" separator="," close=")">	#{id}
    </foreach>
    </select>
java 复制代码
@Test
    public void SelectByArrayTest()
    {
        SqlSession session =MyBatisUtils.getSession();

        //准备查询的条件
        Integer[] ids={2,3};

        List<Customer>  customers=  session.selectList("com.it.mapper.CustomerMapper.findByArray",ids);
        for(Customer customer:customers)
        {
            System.out.println(customer);
        }

        session.close();

    }

<foreach>元素迭代List

XML 复制代码
 <select id="findByList"
            resultType="com.it.pojo.Customer">
        select * from t_customer where id in
        <foreach item="id" index="index" collection="list"
                 open="(" separator="," close=")">	#{id}
        </foreach>
    </select>
java 复制代码
@Test
    public void SelectByListTest()
    {
        SqlSession session =MyBatisUtils.getSession();

        //准备查询的条件
        List<Integer> a=new ArrayList<Integer>();
        a.add(1);
        a.add(2);

        List<Customer>  customers=  session.selectList("com.it.mapper.CustomerMapper.findByList",a);
        for(Customer customer:customers)
        {
            System.out.println(customer);
        }

        session.close();

    }

<foreach>元素迭代Map

XML 复制代码
<select id="findByMap" parameterType="java.util.Map"
            resultType="com.it.pojo.Customer">
        select * from t_customer where jobs=#{jobs} and id in
        <foreach item="roleMap" index="index" collection="id" open="(" 	separator="," close=")"> #{roleMap}
        </foreach>
    </select>
java 复制代码
 @Test
    public void findByMapTest() {
        SqlSession session = MyBatisUtils.getSession();
        List<Integer> ids=new ArrayList<Integer>();
        ids.add(1); ids.add(2); ids.add(3);
        Map<String,Object> conditionMap = new HashMap<String, Object>();
        conditionMap.put("id",ids); conditionMap.put("jobs","teacher");
                List<Customer> customers = session.selectList("com.it.mapper"
                        + ".CustomerMapper.findByMap", conditionMap);
        for (Customer customer : customers) { System.out.println(customer);}
        session.close();
    }

1.3案例:学生信息查询系统

多条件查询

当用户输入的学生姓名不为空,则只根据学生姓名进行学生信息的查询;

当用户输入的学生姓名为空,而学生专业不为空,则只根据学生专业进行学生的查询;

XML 复制代码
  <select id="findStudentByNameAndMajor"
            resultType="com.it.pojo.Student"
    >
        select * from dm_student where 1=1
        <choose>
            <when test="name!=null and name!=''">
                and name like concat('%',#{name},'%')
            </when>
            <when test="major!=null and major!=''">
                and major=#{major}
            </when>
            <otherwise>
                and sno is not null
            </otherwise>
        </choose>



    </select>
java 复制代码
 @Test
    public void findStudentByNameAndMajorTest()
    {
        SqlSession session= MyBatisUtils.getSession();
        Student student=new Student();
        student.setName("张三");
        student.setMajor("英语");
        List<Student> students = session.selectList("com.it.mapper.StudentMapper.findStudentByNameAndMajor",student);
        for (Student student2 : students)
        {
            System.out.println(student2);
        }


        session.close();
    }

单条件查询

查询出所有id值小于5的学生的信息;

XML 复制代码
    <select id="findByList" parameterType="java.util.List"
            resultType="com.it.pojo.Student">
        select * from dm_student where id in
        <foreach item="id" index="index" collection="list"
                 open="(" separator="," close=")">
            #{id}
        </foreach>
    </select>
java 复制代码
  @Test
    public void findByListTest() {
        SqlSession session = MyBatisUtils.getSession();
        List<Integer> ids=new ArrayList<Integer>();
        for(int i =1;i<5;i++){
            ids.add(i);}
        List<Student> students = session.selectList("com.it.mapper.StudentMapper.findByList", ids);
        for (Student student : students) { System.out.println(student);}
        session.close();
    }

总结:

!!!

创建这个包和配置文件的时候,千万不要在设置的时候写com.it.mapper应该用com/it/mapper,否则等着报错吧!

相关推荐
wljt10 小时前
公司二-面试题
java·开发语言·mybatis
VimorphTech13 小时前
文本已经改了,旧的音频生成结果还能回写吗?
java·数据库·mybatis
麻瓜code18 小时前
双写一致性:为什么是“删缓存“,而不是“改缓存“
java·缓存·mybatis
郭小李2 天前
mybatis 动态 sql
java·sql·mybatis
知守观2 天前
技术考古:iBATIS 2.3.4 时代的持久层,是如何用 Map 玩转 307 个存储过程的?
java·后端·mybatis
captain3762 天前
MyBatis Plus(1)
java·服务器·mybatis
代码不停2 天前
MyBatis进阶
spring boot·mybatis
captain3762 天前
MyBatis(入门5)其他查询操作
java·数据库·spring boot·mybatis
captain3762 天前
MyBatis(进阶1)
mybatis
郭小李3 天前
mybatis的 Mapper.xml语法
xml·java·mybatis