MyBatis 动态SQL
MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦。拼接的时候要确保不能忘了必要的空格,还要注意省掉列名列表最后的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
通常使用动态 SQL 不可能是独立的一部分,MyBatis 当然使用一种强大的动态 SQL 语言来改进这种情形,这种语言可以被用在任意的 SQL 映射语句中。
动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多的元素需要来了解。MyBatis 3 大大提升了它们,现在用不到原先一半的元素就可以了。MyBatis 采用功能强大的基于 OGNL 的表达式来消除其他元素。
if
choose (when, otherwise)
trim (where, set)
foreach
if
动态 SQL 通常要做的事情是有条件地包含 where 子句的一部分。
<select id="findStudentNameById" parameterType="int" resultType="StudentInfo">
select * from student
<where>
<trim suffix="" suffixOverrides="and">
<if test="classId!=null">
classId=#{classId}
</if>
<if test="studentName!=null and studentName!=''">
and studentName like concat(concat('%',#{studentName}),'%')
</if>
</trim>
</where>
</select>
suffix:在trim标签内sql语句加上后缀
prefix:在trim标签内sql语句加上前缀
suffixOverrides:指定去除多余的后缀内容
prefixOverrides:指定去除多余的前缀内容
比如:suffixOverrides=”and”,去除trim标签内sql语句多余的后缀”and”
choose, when, otherwise
有些时候,我们不想用到所有的条件语句,而只想从中择其一二。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
<select id="findStudentByChoose" resultType="studentInfo">
select * from student where 1=1
<choose>
<when test="studentName!=null and studentName!=''">
and studentName like concat(concat('%',#{studentName}),'%')
</when>
<when test="studentAge!=0">
and studentAge>#{studentName}
</when>
<otherwise>
and classId=#{classId}
</otherwise>
</choose>
</select>
choose标签是按顺序判断其内部when标签中的test条件出否成立,如果有一个成立,则 choose 结束。当 choose 中所有 when 的条件都不满则时,则执行 otherwise 中的sql。类似于Java 的 switch 语句,choose 为 switch,when 为 case,otherwise 则为 default。
trim, where, set
where 元素知道只有在一个以上的if条件有值的情况下才去插入"WHERE"子句。而且,若最后的内容是"AND"或"OR"开头的,where 元素也知道如何将他们去除。
类似的用于动态更新语句的解决方案叫做 set。set 元素可以被用于动态包含需要更新的列,而舍去其他的。
<update id="update" parameterType="StudentInfo">
update student
<trim suffix="where studentId=#{studentId}" suffixOverrides=",">
<set>
<if test="studentName!=null">studentName=#{studentName},</if>
<if test="studentSex!=null">studentSex=#{studentSex},</if>
<if test="studentAddress!=null">studentAddress=#{studentAddress},</if>
<if test="studentAge!=null">studentAge=#{studentAge},</if>
<if test="classId!=null">classId=#{classId},</if>
</set>
</trim>
</update>
trim标签一般用于去除sql语句中多余的and关键字,逗号,或者给sql语句前拼接 “where“、“set“以及“values(“ 等前缀,或者添加“)“等后缀,可用于选择性插入、更新、删除或者条件查询等操作。
foreach
动态 SQL 的另外一个常用的必要操作是需要对一个集合进行遍历,通常是在构建 IN 条件语句的时候。
<select id="findStudentByMap" resultType="StudentInfo">
select * from student where studentName like concat(concat('%',#{studentName}),'%') and classId in
<foreach collection="list" item="map" open="(" separator="," close=")">
#{map}
</foreach>
</select>
在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况下,该属性的值是不一样的,主要有一下3种情况:
如果传入的是单参数且参数类型是一个List的时候,collection属性值为list 。
如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array 。
如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map,实际上如果你在传入参数的时候,在MyBatis里面也是会把它封装成一个Map的,map的key就是参数名,所以这个时候collection属性值就是传入的List或array对象在自己封装的map里面的key。
举个例子:
mapper 方法:
public List<StudentInfo> findStudentByMap(Map<String,Object> map);
xml 映射文件:
<?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="cn.zbw.mapper.StudentInfoMapper">
<select id="findStudentByMap" resultType="StudentInfo">
select * from student where studentName like concat(concat('%',#{studentName}),'%') and classId in
<foreach collection="list" item="map" open="(" separator="," close=")">
#{map}
</foreach>
</select>
test 类:
package cn.zbw.test;
import cn.zbw.entity.StudentInfo;
import cn.zbw.mapper.StudentInfoMapper;
import cn.zbw.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class Test07 {
public static void main(String[] args) {
test07();
}
private static void test07() {
SqlSession session = MybatisUtils.getSession();
HashMap<String, Object> map = new HashMap<>();
List list = new ArrayList();
list.add(2);
map.put("studentName","冰");
map.put("list",list);
List<StudentInfo> stuList = session.getMapper(StudentInfoMapper.class).findStudentByMap(map);
for (StudentInfo stu : stuList) {
System.out.println(stu.getStudentName()+"\t"+stu.getStudentAddress());
}
}
}
查询结果: