前言:
MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其它类似框架的经验,你就能体会到根据
不同条件拼接 SQL 语句的痛苦。例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。
利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
Mybatis Generator可以帮我们根据数据库表自动生成pojo类和SQL映射文件,SQL映射文件提供了增删改查功能。
动态SQL
到网上找一个经典的mysql数据库表
CREATE TABLE `emp` (
#编号
`empno` int(11) NOT NULL,
#姓名
`ename` varchar(255) NOT NULL,
#职位
`job` varchar(255) DEFAULT NULL,
`mgr` int(11) DEFAULT NULL,
#入职时间
`hiredate` date NOT NULL,
#薪水
`sal` decimal(7,2) DEFAULT NULL,
#奖金级别
`comm` decimal(7,2) DEFAULT NULL,
#部门编号
`deptno` int(11) DEFAULT NULL,
PRIMARY KEY (`empno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
#部门表
CREATE TABLE `dept` (
#部门编号
`deptno` int(11) NOT NULL,
#部门名称
`dname` varchar(255) NOT NULL,
#部门地址
`loc` varchar(255) DEFAULT NULL,
PRIMARY KEY (`deptno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO emp VALUES(7369,'SMITH','CLERK',7902,'1980-12-17',800,NULL,20);
INSERT INTO emp VALUES(7499,'ALLEN','SALESMAN',7698,'1981-02-20',1600,300,30);
INSERT INTO emp VALUES(7521,'WARD','SALESMAN',7698,'1981-02-22',1250,500,30);
INSERT INTO emp VALUES(7566,'JONES','MANAGER',7839,'1981-04-02',2975,NULL,20);
INSERT INTO emp VALUES(7654,'MARTIN','SALESMAN',7698,'1981-09-28',1250,1400,30);
INSERT INTO emp VALUES(7698,'BLAKE','MANAGER',7839,'1981-05-01',2850,NULL,30);
INSERT INTO emp VALUES(7782,'CLARK','MANAGER',7839,'1981-06-09',2450,NULL,10);
INSERT INTO emp VALUES(7788,'SCOTT','ANALYST',7566,'1987-04-19',3000,NULL,20);
INSERT INTO emp VALUES(7839,'KING','PRESIDENT',NULL,'1981-11-17',5000,NULL,10);
INSERT INTO emp VALUES(7844,'TURNER','SALESMAN',7698,'1981-09-08',1500,0,30);
INSERT INTO emp VALUES(7876,'ADAMS','CLERK',7788,'1987-05-23',1100,NULL,20);
INSERT INTO emp VALUES(7900,'JAMES','CLERK',7698,'1981-12-03',950,NULL,30);
INSERT INTO emp VALUES(7902,'FORD','ANALYST',7566,'1981-12-03',3000,NULL,20);
INSERT INTO emp VALUES(7934,'MILLER','CLERK',7782,'1982-01-23',1300,NULL,10);
INSERT INTO dept VALUES(10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO dept VALUES(20, 'RESEARCH', 'DALLAS');
INSERT INTO dept VALUES(30, 'SALES', 'CHICAGO');
INSERT INTO dept VALUES(40, 'OPERATIONS', 'BOSTON');
表准备完成之后,开始编写动态SQL语句
If
动态 SQL 通常要做的事情是根据条件包含 where 子句的一部分。
<select id="selectEmployeeByCondition1" parameterType="Employee"
resultMap="EmployeeBaseMap">
select * from emp
where 1 = 1
<if test="empno != null">
and empno = #{empno}
</if>
<if test="ename != null">
and ename like #{ename}
</if>
<if test="job != null">
and job = #{job}
</if>
</select>
这条语句会根据empno,ename,job是否为空插入条件, " where 1 = 1 "是为了避免3个条件都为空出现"select * from emp where "这种SQL语句。
每个if条件之间必须显式用and或or进行连接。
choose,when,otherwise
有时我们不想应用到所有的条件语句,而只想从中择其一项。针对这种情况,MyBatis 提供了 choose 元素
choose类似if else if else,只会选择中一个条件执行。
<!-- choose -->
<select id="selectEmployeeByCondition2" parameterType="Employee"
resultMap="EmployeeBaseMap">
select * from emp
where
<choose>
<when test="empno != null">
empno = #{empno}
</when>
<when test="ename">
ename like #{ename}
</when>
<when test="job != null">
job = #{job}
</when>
<otherwise>
1 = 1
</otherwise>
</choose>
</select>
如果三个when条件都不满足,则会选中<otherwise>拼接到where条件后面!
where set trim
上面的几条动态SQL语句都会加上where条件,有时候我们希望如果条件都不满足的情况下不加where字句。
<select id="selectEmployeeByCondition3" parameterType="Employee"
resultMap="EmployeeBaseMap">
select * from emp
<where>
<if test="empno != null">
and empno = #{empno}
</if>
<if test="ename != null">
and ename like #{ename}
</if>
<if test="job != null">
and job = #{job}
</if>
</where>
</select>
where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。
而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除。
和where等价的trim
<select id="selectEmployeeByCondition4" parameterType="Employee" resultMap="EmployeeBaseMap"> select * from emp <trim prefix="where" prefixOverrides="and | or"> <if test="empno != null"> and empno = #{empno} </if> <if test="ename != null"> and ename like #{ename} </if> <if test="job != null"> and job = #{job} </if> </trim> </select>
<trim> 标签有两个属性,prefix是指定插入的内容,prefixOverrides属性会忽略通过管道分隔的文本序列。
它的作用是移除所有指定在 prefixOverrides 属性中的内容,并且插入 prefix 属性中指定的内容。
假设一和三条件满足,SQL语句就会变成 " select * from emp where empno = #{empno} and job = #{job} ";
<set>标签是专门为更新语句而准备的。
<update id="updateEmployee" parameterType="Employee"> update emp <set> <if test="ename != null"> ename = #{ename}, </if> <if test="job != null"> job = #{job}, </if> <if test="mgr != null"> mgr = #{mgr}, </if> <if test="salary != null"> sal = #{salary}, </if> <if test="comment != null"> comm = #{comment}, </if> <if test="dept != null and dept.deptNo != null"> deptno = #{dept.deptNo} </if> </set> where empno = #{empno} </update>
set 元素可以用于动态包含需要更新的列,而舍去其它的
这里,set 元素会动态前置 SET 关键字,同时也会删掉无关的逗号,
因为用了条件语句之后很可能就会在生成的 SQL 语句的后面留下这些逗号。
当然trim也可以完成set标签的功能
<update id="updateEmployee2" parameterType="Employee">
update emp
<trim prefix="set" prefixOverrides=",">
<if test="ename != null">
ename = #{ename},
</if>
<if test="job != null">
job = #{job},
</if>
<if test="mgr != null">
mgr = #{mgr},
</if>
<if test="salary != null">
sal = #{salary},
</if>
<if test="comment != null">
comm = #{comment},
</if>
<if test="dept != null and dept.deptNo != null">
deptno = #{dept.deptNo}
</if>
</trim>
where empno = #{empno}
</update>
forEach
动态 SQL 的另外一个常用的操作需求是对一个集合进行遍历,通常是在构建 IN 条件语句的时候。
<select id="selectEmployeeByEmpnos" parameterType="list" resultMap="EmployeeBaseMap">
select * from emp
where empno in
<foreach collection="list" open="(" close=")" item="item" index="index" separator=",">
#{item}
</foreach>
</select>
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和
索引(index)变量。它也允许你指定开头与结尾的字符串以及在迭代结果之间放置分隔符。
这个元素是很智能的,因此它不会偶然地附加多余的分隔符。
注意: 你可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象传递给 foreach 作为集合参数。
当使用可迭代对象或者数组时,index 是当前迭代的次数,item 的值是本次迭代获取的元素。
当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
动态SQL介绍完毕,这里提供一下完整的映射文件和映射接口。
映射文件:
<?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.briup.mapper.DynamicMapper"> <!-- 告诉数据库如何加载数据到类的那个属性上 --> <resultMap type="Employee" id="EmployeeBaseMap"> <id column="empno" property="empno"/> <result column="ename" property="ename"/> <result column="job" property="job"/> <result column="mgr" property="mgr"/> <result column="hiredate" property="hiredate"/> <result column="sal" property="salary"/> <result column="comm" property="comment"/> </resultMap> <resultMap type="Deptarment" id="DeptarmentBaseMap"> <id column="deptno" property="deptNo"/> <result column="dname" property="dName"/> <result column="loc" property="loc"/> </resultMap> <!-- if --> <select id="selectEmployeeByCondition1" parameterType="Employee" resultMap="EmployeeBaseMap"> select * from emp where 1 = 1 <if test="empno != null"> and empno = #{empno} </if> <if test="ename != null"> and ename like #{ename} </if> <if test="job != null"> and job = #{job} </if> </select> <!-- choose --> <select id="selectEmployeeByCondition2" parameterType="Employee" resultMap="EmployeeBaseMap"> select * from emp where <choose> <when test="empno != null"> empno = #{empno} </when> <when test="ename"> ename like #{ename} </when> <when test="job != null"> job = #{job} </when> <otherwise> 1 = 1 </otherwise> </choose> </select> <!-- where trim set --> <select id="selectEmployeeByCondition3" parameterType="Employee" resultMap="EmployeeBaseMap"> select * from emp <where> <if test="empno != null"> and empno = #{empno} </if> <if test="ename != null"> and ename like #{ename} </if> <if test="job != null"> and job = #{job} </if> </where> </select> <select id="selectEmployeeByCondition4" parameterType="Employee" resultMap="EmployeeBaseMap"> select * from emp <trim prefix="where" prefixOverrides="and | or"> <if test="empno != null"> and empno = #{empno} </if> <if test="ename != null"> and ename like #{ename} </if> <if test="job != null"> and job = #{job} </if> </trim> </select> <update id="updateEmployee" parameterType="Employee"> update emp <set> <if test="ename != null"> ename = #{ename}, </if> <if test="job != null"> job = #{job}, </if> <if test="mgr != null"> mgr = #{mgr}, </if> <if test="salary != null"> sal = #{salary}, </if> <if test="comment != null"> comm = #{comment}, </if> <if test="dept != null and dept.deptNo != null"> deptno = #{dept.deptNo} </if> </set> where empno = #{empno} </update> <update id="updateEmployee2" parameterType="Employee"> update emp <trim prefix="set" prefixOverrides=","> <if test="ename != null"> ename = #{ename}, </if> <if test="job != null"> job = #{job}, </if> <if test="mgr != null"> mgr = #{mgr}, </if> <if test="salary != null"> sal = #{salary}, </if> <if test="comment != null"> comm = #{comment}, </if> <if test="dept != null and dept.deptNo != null"> deptno = #{dept.deptNo} </if> </trim> where empno = #{empno} </update> <select id="selectEmployeeByEmpnos" parameterType="list" resultMap="EmployeeBaseMap"> select * from emp where empno in <foreach collection="list" open="(" close=")" item="item" index="index" separator=","> #{item} </foreach> </select> </mapper>