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的更多相关文章

  1. 第五章 动态SQL 批量操作

    用于实现动态SQL的元素主要有 if trim where set choose(when.otherwise) foreach MyBatis  缓存 一级缓存 在test类中 调用相同的方法 第二 ...

  2. MyBatis从入门到精通(第4章):MyBatis动态SQL【foreach、bind、OGNL用法】

    (第4章):MyBatis动态SQL[foreach.bind.OGNL用法] 4.4 foreach 用法 SQL 语句中有时会使用 IN 关键字,例如 id in (1,2,3).可以使用 ${i ...

  3. MyBatis从入门到精通(第4章):MyBatis动态SQL【if、choose 和 where、set、trim】

    (第4章):MyBatis动态SQL[if.choose 和 where.set.trim] MyBatis 的强大特性之一便是它的动态 SQL.MyBatis 3.4.6版本采用了功能强大的OGNL ...

  4. Java EE数据持久化框架 • 【第4章 MyBatis动态SQL】

    全部章节   >>>> 本章目录 4.1 MyBatis动态标签 4.1.1  MyBatis动态标签介绍 4.1.2 < if >标签 4.1.3 update语 ...

  5. iBatis第四章:动态SQL的用法

    一.什么是动态SQL,以及使用动态SQL的好处 所谓动态SQL,是针对静态SQL而言的,静态SQL的SQL语句是固定的,使用动态SQL是为了增强SQL的灵活性和复用性,可以用一个动态SQL达到在不同条 ...

  6. 一步步学Mybatis-怎么样实现动态SQL查询(6)

    上一章我们已经讲完了关于Mybatis的分页用法,其实MyBatis 还具有的一个强大的特性之一通常是它的动态 SQL 能力. 如果你有使用 JDBC 或其他 相似框架的经验,你就明白要动态的串联 S ...

  7. MyBatis 源码分析——动态SQL语句

    有几年开发经验的程序员应该都有暗骂过原生的SQL语句吧.因为他们不能一句就搞定一个业务,往往还要通过代码来拼接相关的SQL语句.相信大家会理解SQL里面的永真(1=1),永假(1=2)的意义吧.所以m ...

  8. T-SQL动态查询(4)——动态SQL

    接上文:T-SQL动态查询(3)--静态SQL 前言: 前面说了很多关于动态查询的内容,本文将介绍使用动态SQL解决动态查询的一些方法. 为什么使用动态SQL: 在很多项目中,动态SQL被广泛使用甚至 ...

  9. mybatis中的动态SQL

    在实际开发中,数据库的查询很难一蹴而就,我们往往要根据各种不同的场景拼接出不同的SQL语句,这无疑是一项复杂的工作,我们在使用mybatis时,mybatis给我们提供了动态SQL,可以让我们根据具体 ...

随机推荐

  1. JavaScript生成一个不重复的ID

    /** * 生成一个用不重复的ID */ function GenNonDuplicateID():String{ } 先看看下面的几个方法 1.生成[0,1)的随机数的Math.random,例如 ...

  2. FPGA 开发板入手途径有哪些呢?

    买到一块 FPGA 开发板,你如何入手呢? 根据博主的经验,你可以通过如下途径来学习: 1.如果你是淘宝上买的,那么可以在淘宝上搜索你的开发板(一般 FPGA 开发板生厂商在淘宝上卖都会附带教程,如米 ...

  3. TJOI2018简要题解

    Day1T1数学计算 按照时间轴建一棵线段树即可,复杂度为\(O(m \log m)\) #include <bits/stdc++.h> #define N 100005 #define ...

  4. CSP2019: Lost and Found

    再不更就成鸽子了= = Day -INF to Day 0 不记得发生了什么 Day 1 今年HN用网上提交的方式收题?再也不怕因为交代码太晚增加省队名额了 开考看了一眼T1好像是模拟题,特地注意了\ ...

  5. SQL系列(五)—— 排序(order by)

    对查询结果进行排序是日常应用开发中最为常见的需求,在SQL中通过order by实现.order by是select语句中一部分,即子句. 1.order by 1.1 单列排序 其实,检索出的数据并 ...

  6. 封装:Windows系统文件图标

    原文:封装:Windows系统文件图标 用途:用于获取文件系统默认图标 using System; using System.Collections.Generic; using System.Dra ...

  7. Java程序优化细节

    1. 尽量在合适的场合使用单例 使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面:    1).控制资源的使用,通过线程同 ...

  8. 【总结】web工作代码分类整理(持续更新)

    文件.blob 文件下载失败,将Blob对象转换为Json,处理异常错误? 使用FileReader,核心代码: var reader = new FileReader() reader.onload ...

  9. Qt NetWork即时通讯网络聊天室(基于TCP)

    本文使用QT的网络模块来创建一个网络聊天室程序,主要包括以下功能: 1.基于TCP的可靠连接(QTcpServer.QTcpSocket) 2.一个服务器,多个客户端 3.服务器接收到某个客户端的请求 ...

  10. consul:kv

    consul除了提供了服务发现的功能,还是提供了kv store的功能,kv store可用于动态配置服务.协调服务.leader选举等场景. consul的kv提供了cli和http的两种接口: h ...