在实际的项目中,经常会遇到动态SQL的增、删、改、查问题,这里我们举一个简单的例子:
在一个web工程中,我们以学生信息为例(表为 stundet),经常会有一个搜索框,并且在搜索之前通常会进行一个关键词的过滤,比如可以过滤的条件有:姓名、性别等,如果当我们姓名和性别都不选,则等价于下面的SQL语句
select * from student
如果我们只填写姓名为"小元",则等价于下面的SQL语句
select * from student where sname="小元"
如果我们同时选中姓名"小元"与性别"男",则等价于下面的SQL语句
select * from student where sname="小元" and sex="男"
当我们有很多的条件时,此时就需要我们去组合这些条件,并动态的生成一个可执行的SQL语句,这样就不是一个简单的SQL语句能够解决问题,那么我们该怎么办呢?在MyBatis中同样是支持这种动态SQL的写法,具体见下面的内容。
一、什么是MyBatis的动态SQL语句:
动态SQL语句可以根据条件智能生成SQL语句,主要用于解决查询条件不确定的情况,在程序运行期间,根据提交的查询条件进行查询,即通过MyBatis提供的各种标签对条件作出判断以实现动态拼接SQL语句。
MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦。拼接的时候要确保不能忘了必要的空格,还要注意省掉列名列表最后的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
MyBatis的动态SQL是基于OGNL表达式的,它可以帮助我们方便的在SQL语句中实现某些逻辑。
(OGNL表达式:OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,他是一个开源项目。Struts框架使用OGNL作为默认的表达式语言。)
MyBatis中用于实现动态SQL的标签元素主要有:
- if
- where
- set
- choose(when,otherwise)
- trim
- foreach
二、MyBatis的动态SQL语句的使用
数据库表结构如下:
数据库表内容如下:
1.if标签元素的使用
例子:从student表中模糊查询 姓名包含"元" 并且 性别为"男"的学生
(1)mapper映射文件内容如下:
<?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 namespace="com.yuan.model.Student">
<select id="queryByIf" parameterType="com.yuan.model.Student" resultType="com.yuan.model.Student">
select * from student where 1=1
<if test="sname != null">
and sname like concat('%',#{sname},'%')
</if>
<if test="sex != null">
and sex = #{sex}
</if>
</select>
</mapper>
(2)测试程序以及结果如下:
运行成功,此时,我们仅赋值姓名或性别或都不赋值都可正确查询,但每次要在 where后面写 1=1或其他等价条件很烦呀,肿么办?
下面引入第二个标签元素 where
2.where标签元素的使用
(1)mapper映射文件内容如下:
<?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 namespace="com.yuan.model.Student">
<select id="queryByIf" parameterType="com.yuan.model.Student" resultType="com.yuan.model.Student">
select * from student
<where>
<if test="sname != null">
and sname like concat('%',#{sname},'%')
</if>
<if test="sex != null">
and sex = #{sex}
</if>
</where>
</select>
</mapper>
(2)测试程序以及结果与上图一致
where的作用:where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除。
仅仅会这种方式肿么足够呢?请看我们的万能trim标签元素来实现动态多条件查询!
3.trim标签元素的使用
如果我们不想用 where 1=1 ,更不想受到where标签元素不能为空的约束,我们可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:将多余的AND 或者OR 覆盖,并在其前面加WHERE
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
上述mapper可改为
<?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 namespace="com.yuan.model.Student">
<select id="queryByIf" parameterType="com.yuan.model.Student"
resultType="com.yuan.model.Student">
select * from student
<trim prefix="WHERE" prefixOverrides="AND |OR ">
<if test="sname != null">
and sname like concat('%',#{sname},'%')
</if>
<if test="sex != null">
and sex = #{sex}
</if>
</trim>
</select>
</mapper>
prefixOverrides 属性会忽略通过管道分隔的文本序列(注意此例中的空格也是必要的,因为怕表中字段有名字包含AND或OR,如Android)。它的作用是移除所有指定在 prefixOverrides 属性中的内容,并且插入 prefix 属性中指定的内容。
运行结果同样正确。
那么动态多条件查询结束,我们又如何进行动态多条件更新呢?那就需要用到我们的set标签元素了(当然配合if标签元素使用)
4.set标签元素的使用
例子:从student表中修改sno为1的学生,修改他的姓名由 大元 到 大大元,修改他的性别由 女 变为 男
更新前数据库表内容:
(1)不使用set标签元素进行更新操作,mapper映射文件内容如下:
在update标签里我们没写parameterType ,parameterType 将会传入这条语句的参数类的完全限定名或别名。
这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。
parameterType 将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。<?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 namespace="com.yuan.model.Student">
<update id="updateBySet">
<if test="sname != null">
sname = #{sname},
</if>
<if test="sex != null">
sex = #{sex}
</if>
where sno = #{sno}
</update>
</mapper>
测试程序以及结果:
缺点:在本例中,如果仅传入sname,而不传入sex,则会发生异常
通过观察sql发现,多了一个逗号,所以我们使用set标签元素进行替换
(2)使用set标签元素进行更新操作,mapper映射文件内容如下:
<?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 namespace="com.yuan.model.Student">
<update id="updateBySet">
update student
<set>
<if test="sname != null">
sname = #{sname},
</if>
<if test="sex != null">
sex = #{sex}
</if>
</set>
where sno = #{sno}
</update>
</mapper>
这次我们继续只传入sname,结果如下,无异常:
set 元素可以用于动态包含需要更新的列,而舍去其它的。
set 元素会动态前置 SET 关键字,同时也会删掉无关的逗号,因为用了条件语句之后很可能就会在生成的 SQL 语句的后面留下这些逗号。(和where标签元素一样,要至少有一个子元素的条件返回 SQL 子句,- 没有值我们更新啥-)
刚才我们说trim很全能,那他全能在哪呢?对!更新也可以用它!
(3)使用trim标签元素进行更新操作,mapper映射文件内容如下:将多余的逗号 , 覆盖,并在其前面加SET
<trim prefix="SET" suffixOverrides=",">
...
</trim>
在本例中:
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yuan.model.Student">
<update id="updateBySet">
update student
<trim prefix="SET" suffixOverrides=",">
<if test="sname != null">
sname = #{sname},
</if>
<if test="sex != null">
sex = #{sex}
</if>
</trim>
where sno = #{sno}
</update>
</mapper>
结果同样正确。
今天暂时整理动态SQL的其中4个标签元素 if、where、trim、set
下一篇将整理剩下的 choose、foreach