Mybatis中的动态SQL

概述

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

动态SQL中的元素

使用动态SQL的好处:

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

动态SQL常用元素
元素说明
判断语句,用于单条件判断
(、)相当于Java中的switch…case…default语句,用于多条件判断
简化SQL语句中where的条件判断
可以灵活地去除多余的关键字
用于SQL语句的动态更新
循环语句,常用于in语句等列举条件中

条件查询操作

if元素

if元素的应用

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

通过一个具体的案例演示单条件判断下if元素的使用,案例具体实现步骤如下。

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

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方法。***

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,在映射文件中,根据客户姓名和年龄组合条件查询客户信息,使用元素编写该组合条件的动态SQL。

<!– 该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映射文件加载到程序中。

<mapper 
resource="com/itheima/mapper/CustomerMapper.xml">
</mapper>

创建获取SqlSession对象的工具类*。***

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(),该方法用于根据客户姓名和职业组合条件查询客户信息列表。

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();}
}

<choose、<when、<otherwise元素

choose>when>otherwise>使用场景

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

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

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

示例

在映射文件CustomerMapper.xml中,添加使用、、元素执行上述情况的动态SQL。

<!-- 只展示三个组合元素的部分-->
<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> 

在测试类MyBatisTest中,编写测试方法findCustomerByNameOrJobsTest(),该方法用于根据客户姓名或职业查询客户信息列表。

public void findCustomerByNameOrJobsTest() {
     SqlSession session=MyBatisUtils.getSession();
     Customer customer=new Customer();
     customer.setUsername("tom");customer.setJobs("teacher");
     List<Customer> customers=session.selectList("com.itheima.mapper" 
+ ".CustomerMapper.findCustomerByNameOrJobs",customer);
        for (Customer customer2 : customers) {
	      System.out.println(customer2);} 
session.close();
}

where>、trim>元素

<where<trim使用场景

​ 在映射文件中,编写的SQL后面加入了“where 1=1”的条件的话,既保证了where后面的条件成立,又避免了where后面第一个词是and或者or之类的关键字。

​ 例如下面这条Mybatis拼接出的SQL语句是不正确的。

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

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

<where元素
<select id="findCustomerByNameAndJobs" 
          parameterType="com.itheima.pojo.Customer"
          resultType="com.itheima.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>

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

<trim元素

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

属性说明
prefix指定给SQL语句增加的前缀
prefixOverrides指定SQL语句中要去掉的前缀字符串
suffix指定给SQL语句增加的后缀
suffixOverrides指定SQL语句中要去掉的后缀字符串
<select id="findCustomerByNameAndJobs" 
          parameterType="com.itheima.pojo.Customer"
          resultType="com.itheima.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>

​ 上述配置代码中,元素的作用是去除一些多余的前缀字符串,它的prefix属性代表的是语句的前缀(where),而prefixOverrides属性代表的是需要去除的前缀字符串(SQL中的“AND”或“OR”)。

更新操作

<set元素使用场景

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

通过一个案例演示如何使用<set元素更新数据库的信息,案例具体步骤如下。

在映射文件CustomerMapper.xml中,添加使用元素执行更新操作的动态SQL。

<update id="updateCustomerBySet" parameterType="com.itheima.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> 

编写测试方法updateCustomerBySetTest()。

public void updateCustomerBySetTest() {		
    SqlSession sqlSession = MyBatisUtils.getSession();
    Customer customer = new Customer();        customer.setId(3);
    customer.setPhone("13311111234");
    int rows = sqlSession.update("com.itheima.mapper"
            + ".CustomerMapper.updateCustomerBySet", customer);
    if(rows > 0) {System.out.println("您成功修改了"+rows+"条数据!");
    } else { System.out.println("执行修改操作失败!!!");
    }sqlSession.commit();sqlSession.close();
}
<set元素字段非空

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

使用<trim元素更新

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

复杂查询操作

<foreach元素中的属性

属性说明
item表示集合中每一个元素进行迭代时的别名。该属性为必选。
index在List和数组中,index是元素的序号,在Map中,index是元素的key。该属性可选。
open表示foreach语句代码的开始符号,一般和close=“)”合用。常用在in条件语句中。该属性可选。
separator表示元素之间的分隔符,例如,在条件语句中,separator=“,”会自动在元素中间用“,”隔开,避免手动输入逗号导致SQL错误,错误示例如in(1,2,)。该属性可选。
close表示foreach语句代码的关闭符号,一般和open="("合用。常用在in条件语句中。该属性可选。
collection用于指定遍历参数的类型。注意,该属性必须指定,不同情况下,该属性的值是不一样的。

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

  • List类型: 若入参为单参数且参数类型是一个List,collection属性值为list。
  • 数组类型: 若入参为单参数且参数类型是一个数组,collection属性值为array。
  • Map类型: 若传入参数为多参数,就需要把参数封装为一个Map进行处理,collection属性值为Map。若传入参数为多参数,就需要把参数封装为一个Map进行处理,collection属性值为Map。

<foreach元素迭代数组

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

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

<foreach元素迭代数组的实现具体如下

在映射文件CustomerMapper.xml中,添加使用元素迭代数组执行批量查询操作的动态SQL。

<select id="findByArray" parameterType="java.util.Arrays"
         resultType="com.itheima.pojo.Customer">select * from t_customer where id in
    <foreach item="id" index="index" collection="array" 
               open="(" separator="," close=")">	#{id}
    </foreach>
select>

在测试类MyBatisTest中,编写测试方法findByArrayTest()方法,实现客户信息的批量查询。

public void findByArrayTest() {
    SqlSession session = MyBatisUtils.getSession(); // 获取SqlSession
    Integer[] roleIds = {2,3}; // 创建数组,封装查询id
    // 执行SqlSession的查询方法,返回结果集
    List<Customer> customers = session.selectList("com.itheima.mapper"
            + ".CustomerMapper.findByArray", roleIds);	
    for (Customer customer : customers) {System.out.println(customer);}
    session.close();
}

执行MyBatisTest测试类的findByArrayTest()方法,控制台会输出结果。

<foreach元素迭代List

<foreach元素迭代List的实现步骤具体如下

在映射文件CustomerMapper.xml中,添加使用元素迭代List集合执行批量查询操作的动态SQL。

<select id="findByList" parameterType="java.util.Arrays"
         resultType="com.itheima.pojo.Customer">
    select * from t_customer where id in
    <foreach item="id" index="index" collection="list" 
               open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

在测试类MyBatisTest中,编写测试方法findByListTest(),用于批量查询客户信息。

public void findByListTest() {
    SqlSession session = MyBatisUtils.getSession();
    List<Integer> ids=new ArrayList<Integer>();
    ids.add(1);  ids.add(2);
    List<Customer> customers = session.selectList("com.itheima.mapper"
            + ".CustomerMapper.findByList", ids);	
    for (Customer customer : customers) {System.out.println(customer);
    } session.close();
}

执行MyBatisTest测试类的findByListTest()方法,控制台会输出结果。

<foreach元素迭代Map

下面通过一个案例演示如何使用<foreach元素迭代Map集合,实现多参数入参查询操作,案例具体实现步骤如下

在映射文件CustomerMapper.xml中,添加使用元素迭代Map集合执行批量查询操作的动态SQL。

<select id="findByMap" parameterType="java.util.Map"
        resultType="com.itheima.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>

在测试类MyBatisTest中,编写测试方法findByMapTest(),用于批量查询客户信息。

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.itheima.mapper"
            + ".CustomerMapper.findByMap", conditionMap);
        for (Customer customer : customers) { System.out.println(customer);}
       session.close();
 }

执行MyBatisTest测试类的findByMapTest()方法,控制台会输出结果。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值