MyBatis 的强大特性之一便是它的动态 SQL。
动态 SQL 元素和 JSTL 或基于类似 XML 的文本处理器相似。
MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
1. if
动态 SQL 通常要做的事情是根据条件包含 where 子句的一部分。
【示例】
数据库表:
java bean:
public class Stu {
private int id;
private int clazzId;
private String name;
private String sex;
private int age;
/*getter setter toString*/
}
mapper 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="com.zth.mapper.StudentMapper">
<select id="getAllStudents" resultType="com.zth.jopo.Stu" parameterType="com.zth.jopo.Stu">
select * from stu where 1=1
<if test="null != sex">
and sex = #{sex}
</if>
<if test="null != age">
and age = #{age}
</if>
</select>
</mapper>
【注】1=1 防止后面条件成立时语法错误。
接口定义:
public List<Stu> getAllStudents(Stu stu);
测试:
private static Logger log = Logger.getLogger(StudentMapperTest.class);
@Test
public void testgetAllClazz(){
InputStream inputStream = StudentMapperTest.class.getResourceAsStream("/mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Stu stu = new Stu();
stu.setSex("F");
stu.setAge(23);
List<Stu> stus = studentMapper.getAllStudents(stu);
System.out.println(stus);
}
执行结果:
2. choose (when, otherwise)
有时不想应用到所有的条件语句,而只想从中择其一项。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。when和other类似if和else。
XML code:
<select id="getAllStudents1" resultType="com.zth.jopo.Stu" parameterType="com.zth.jopo.Stu">
select * from stu where 1=1
<choose>
<when test="null != sex">
and sex = #{sex}
</when>
<when test="null != age">
and age = #{age}
</when>
<otherwise>
and clazz_id = 2
</otherwise>
</choose>
</select>
接口定义:
public List<Stu> getAllStudents1(Stu stu);
测试:
@Test
public void testgetAllstu1(){
InputStream inputStream = StudentMapperTest.class.getResourceAsStream("/mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Stu stu = new Stu();
stu.setSex("M");
stu.setAge(23);
List<Stu> stus = studentMapper.getAllStudents1(stu);
System.out.println(stus);
}
执行结果:
3. trim (where, set)
上面如果 1=1 也设成动态条件语句,当其不成立的时候就会语法错误。
3.1 where
where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除。
XML code:
<select id="getAllStudents2" resultType="com.zth.jopo.Stu" parameterType="com.zth.jopo.Stu">
select * from stu
<where>
<if test="null != sex">
sex = #{sex}
</if>
<if test="null != age">
and age = #{age}
</if>
</where>
</select>
接口定义:
public List<Stu> getAllStudents2(Stu stu);
测试:
@Test
public void testgetAllstu2(){
InputStream inputStream = StudentMapperTest.class.getResourceAsStream("/mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Stu stu = new Stu();
stu.setAge(23);
List<Stu> stus = studentMapper.getAllStudents2(stu);
System.out.println(stus);
}
执行结果:
如果 where 元素没有按正常套路出牌,我们可以通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
prefixOverrides 属性会忽略通过管道分隔的文本序列(注意此例中的空格也是必要的)。它的作用是移除所有指定在 prefixOverrides 属性中的内容,并且插入 prefix 属性中指定的内容。
3.2 set
set 元素可以用于动态包含需要更新的列,而舍去其它的。
XML code:
<update id="updateStu" parameterType="com.zth.jopo.Stu">
update stu
<set>
<if test="clazzId != null">clazz_id = #{clazzId},</if>
<if test="name != null">name = #{name},</if>
<if test="sex != null">sex = #{sex},</if>
<if test="age != null">age = #{age}</if>
</set>
where id = #{id}
</update>
接口定义:
public void updateStu(Stu stu);
测试:
@Test
public void testUpdateStu(){
InputStream inputStream = StudentMapperTest.class.getResourceAsStream("/mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
Stu stu = new Stu();
stu.setId(4);
stu.setAge(22);
studentMapper.updateStu(stu);
sqlSession.commit();
sqlSession.close();
}
执行结果:
4. foreach
foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(item)和索引(index)变量。它也允许你指定开头与结尾的字符串以及在迭代结果之间放置分隔符。
将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象传递给 foreach 作为集合参数。当使用可迭代对象或者数组时,index 是当前迭代的次数,item 的值是本次迭代获取的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。
MXL code:
<select id="getStudentsIn" resultType="com.zth.jopo.Stu">
select * from stu where id in
<foreach item="item" index="index" collection="list"
open="(" separator="," close=")">
#{item}
</foreach>
</select>
接口定义:
public List<Stu> getStudentsIn(List ids);
测试:
@Test
public void testGetStuIN(){
InputStream inputStream = StudentMapperTest.class.getResourceAsStream("/mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
List list = new ArrayList();
list.add(1);
list.add(3);
List<Stu> stus = studentMapper.getStudentsIn(list);
System.out.println(stus);
sqlSession.close();
}
执行结果: