MyBatis系列五 动态SQL
如果使用JDBC或者其他框架,很多时候你得根据需要去拼装SQL,这是一个麻烦的 事情。而MyBatis提供对SQL语句动态的组装能力,而且它只有几个基本的元素,十分简
单明了,大量的判断都可以在MyBatis的映射XML文件里面配置,以达到许多我们需要 大量代码才能实现的功能,大大减少了我们编写代码的工作量,这体现了 MyBatis的灵活
性、高度可配置性和可维护性。MyBatis也可以在注解中配置SQL,但是由于注解中配置 功能受限,对于复杂的SQL而言可读性很差,所以使用较少,因此在将不对它们进行 介绍。
一、概述
MyBatis的动态SQL包括以下几种元素,如表5-1所示。
表5・1动态SQL的元素
元 素 |
作 用 |
备 注 |
if |
判断语句 |
单条件分支判断 |
choose (when、otherwise) |
相当于Java中的case when语句 |
多条件分支判断 |
trim (where set) |
辅助元素 |
用于处理一些SQL拼装问题 |
fbreach |
循环语句 |
在in语句等列举条件常用 |
下面我们就讨论这些动态元素的用法。
二、if元素
if元素是我们最常用的判断语句,相当于Java中的if语句。它常常与test属性联合使用。
在大部分情况下,if元素使用方法简单,让我们先了解它的基本用法。现在我们要根 据角色名称(roleName)去查找角色,但是角色名称是一个可填可不填的条件,不填写的 时候就不要用它作为查询条件。这是查询中常见的场景之一。if元素提供了简易的实现方 法,如代码清单5.1所示。
这句话的含义就是当我们将参数roleName传递进入到映射器中,采取构造对roleName 的模糊查询。如果这个参数为空,就不要去构造这个条件,显然这样的场景在我们实际的 工作中是十分常见的。通过MyBatis的条件语句我们可以节省许多拼接SQL的工作,把精 力集中在XML的维护上。
三、choose、when, otherwise 元素
5.2节的例子相对于Java语言中的if语句就是一种非此即彼的关系,但是很多时候我 们所面对的不是一种非此即彼的选择。在有些时候我们还需要第三种选择甚至更多的选择, 也就是说,我们也需要switch...case...default语句,而在映射器的动态语句中choose、 when、 otherwise元素承担了这个功能,让我们看看下面的场景。
- 当角色编号不为空,则只用角色编号作为条件查询。
- 当角色编号为空,而角色名称不为空,则用色名称作为条件进行模糊查询。
- 当角色编号和角色名称都为空,则要求角色备注不为空。
让我们看看如何使用choose、when、otherwise元素去实现,如代码清单5.2所示。
这样MyBatis就会根据参数的设置进行判断来动态组装SQL,以满足不同业务的要求。 远比Hibernate和JDBC大量判断Java代码要清晰和明确得多。
四、trim、where、set 兀素
细心的读者会发现5.3节的SQL语句笔者加入了一个条件“ 1=1 ”,如果没有加入这个 条件,那么可能就变为了下面这样一条错误的语句。
显然就会报错关于SQL的语法异常。而加入了 “1=1”这样的条件又显得相当奇怪。 不过不必担心,我们可以用where元素去处理SQL以达到预期的效果。例如我们去掉了条 件“1=1”,只要用where元素就可以了,如代码清单5.3所示。
这样当where元素里面的条件成立的时候,才会加入where这个SQL关键字到组装的 SQL里面,否则就不加入。
有时候我们要去掉一些特殊的SQL语法,比如常见的and、or。而使用trim元素可以 达到我们预期的效果,如代码清单5.4所示。
稍微解释一下,trim元素就意味着我们需要去掉一些特殊的字符串,prefix代表的是语 句的前缀,而prefixOverrides代表的是你需要去掉的那种字符串。上面的写法基本与where 是等效的。
在Hibernate中我们常常需要更新某一对象,发送所有的字段给持久对象。现实中的场 景常常是,我只想更新一个字段,如果发送所有的属性去更新一遍,对网络带宽消耗较大, 性能最佳的办法是把主键和更新字段的值传递给SQL更新即可。例如,角色表有一个主键 和两个字段,如果一个个去更新需要写2条SQL,如果有1000个字段呢?显然这种做法是 不方便的,而在Hibernate中我们做更新都是用全部字段发送给SQL的方法来避免这一情 况发生。
在MyBatis中,我们常常可以使用set元素来完成这些功能,如代码清单5.5所示。
set元素遇到了逗号,它会把对应的逗号去掉,如果我们自己编写那将是多少次的判断 呢?当我们只想更新备注,我们只需要传递备注信息和角色编号即可,而不需要再传递角 色名称。MyBatis就会根据参数的规则进行动态SQL组装,这样便能满足要求,同时避免 了全部字段更新的麻烦。
同样的你也可以把它转变为对应的trim元素,代码如下。
五、foreach 元素
显然foreach元素是一个循环语句,它的作用是遍历集合。它能够很好的支持数组和 List、Set接口的集合,对此提供遍历的功能。
在数据库中,数据字典是经常使用的内容,比如在用户表中,性别可以分为男、女或 者未知,我们把性别作为一个字典,定义如下。
1 .男,2.女,0.未知
实际工作中,用户可能查找非未知性别的用户,也可能查找女性和未知性别的用户, 或者男性和未知性别的用户等,具体的参数需要使用foreach元素去确定,如代码清单5.6 所示。
这里需要稍微解释一下。
- collection配置的sexList是传递进来的参数名称,它可以是一个数组或者List、Set 等集合。
- item配置的是循环中当前的元素。
- index配置的是当前元素在集合的位置下标。
- open和close配置的是以什么符号将这些集合元素包装起来。
- separator是各个元素的间隔符。
在SQL中对于in语句我们常常使用,对于大量数据的in语句需要我们特别注意,因 为它会消耗大量的性能,还有一些数据库的SQL对执行的SQL长度也有限制。所以我们 使用它的时候需要预估一下这个collection对象的长度。
六、test的属性
test的属性用于条件判断的语句中,它在MyBatis中广泛使用。它的作用相当于判断真 假。在大部分的场景中我们都是用它判断空和非空。有时候我们需要判断字符串、数字和 枚举等。所以十分有必要讨论一下它的用法。通过5.2节对if元素的介绍,我们知道了如 何判断非空。但是如何用if语句判断字符串呢?我们对代码进行下面的测试,如代码清 单5.7所示。
把type= "Y”传递给这条SQL,我们发现程序自动加入了 where 1=1。换句话说,这条语句判定成功了。在旧的版本里面我们往往需要加入toString(),新的版本已经解决了这个问题。同样的它可以给我们判断数值型的参数。对于枚举而言,取决 于你使用何种typeHandler,这些需要参考第3章关于枚举typeHandler的介绍。
七、bind 兀素
bind元素的作用是通过OGNL表达式去自定义一个上下文变量,这样更方便我们使 用。在我们进行模糊查询的时候,如果是MySQL数据库,我们常常用到的是一个concat 用“%”和参数相连接。然而在Oracle数据库则是用连接符号“||”,这样SQL就需要提供 两种形式去实现。但是有了 bind元素,我们就完全不必使用数据库的语言,只要使用MyBatis 的语言即可与所需参数相连。
比如我们要按角色名称进行模糊查询,我们可以把映射文件写成这样,如代码清单5.8 所示。
这里的".parameter”代表的就是传递进来的参数,它和通配符连接后,赋给了 pattern, 我们就可以在select语句中使用这个变量进行模糊查询,不管是MySQL数据库还是Oracle 数据库都可以使用这样的语句,提高了其可移植性。
我们传递的参数往往不止这么一个,我们可能传递多个参数。让我们来学习多个参数bind元素的用法。首先,定义接口方法,如代码清单5.9所示。
代码5-9:使用bind元素传递多个参数
*查询角色 *@param roleName 角色名称 *@param note 备注 * @return符合条件的角色 */ public List<RoleBean> findRole(@Param("roleName")String roleName, @Param (”note”)String note);
然后,定义映射文件,定义两个新的变量去执行模糊查询,如代码清单5.10所示。