MyBatis 的强大特性之一便是它的动态 SQL。如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦。拼接的时候要确保不能忘了必要的空格,还要注意省掉列名列表最后的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
通常使用动态 SQL 不可能是独立的一部分,MyBatis 当然使用一种强大的动态 SQL 语言来改进这种情形,这种语言可以被用在任意的 SQL 映射语句中。
动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多的元素需要来了解。MyBatis 3 大大提升了它们,现在用不到原先一半的元素就可以了。MyBatis 采用功能强大的基于 OGNL 的表达式来消除其他元素。
mybatis 的动态sql语句是基于OGNL表达式的。可以方便的在 sql 语句中实现某些逻辑. 总体说来mybatis 动态SQL 语句主要有以下几类:
- if 语句 (简单的条件判断)
- where (主要是用来简化sql语句中where条件判断的,能智能的处理 and or ,不必担心多余导致语法错误)
- set (主要用于更新时)
- trim (对包含的内容加上 prefix,或者 suffix 等,前缀,后缀)
- choose (when,otherwize) ,相当于java 语言中的 switch ,与 jstl 中的choose 很类似.
- foreach (在实现 mybatis in 语句查询时特别有用)
if标签
目标:通过传入User对象查询符合条件的user
<select id="queryByUser" parameterType="cn.xpu.hcp.bean.User" resultType="cn.xpu.hcp.bean.User">
select * from user
where
id=#{id}
and username like "%"#{username}"%"
and sex=#{sex}
and address=#{address}
</select>
这是未使用if标签前。设想一下,如果传入的User对象并没有对应的值,那么这条SQL语句就什么也查不出。
这时就需要用if标签对各个属性值进行判断。
<select id="queryByUser" parameterType="cn.xpu.hcp.bean.User" resultType="cn.xpu.hcp.bean.User">
select * from user where 1=1
<if test="id>=1">
id=#{id}
</if>
<if test="username!=null and username!=''">
and username like "%"#{username}"%"
</if>
<if test="sex!=null and sex!=''">
and sex=#{sex}
</if>
<if test="address!=null and address!=''">
and address=#{address}
</if>
</select>
where后面的1=1一定不能丢失。
这是为了防止两种SQL语句:
①:select * from user where
②:select * from user where and username like '张'
一个是where后无条件,一个是紧跟一个and。
测试:
public void MapperTest(){
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User();
user.setSex("m");
user.setUsername("张");
List<User> list = mapper.queryByUser(user);
for (User u : list) {
System.out.println(u);
}
session.commit();
session.close();
}
where标签
在上述if标签使用后有不满意的地方,即where后一定要有一个条件(譬如:1=1)。不让会产生SQL语句的错误。
这时可以使用 where 动态语句来解决。“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以 AND 或OR 开头的,则它会剔除掉。
使用where标签:
<select id="queryByUser" parameterType="cn.xpu.hcp.bean.User" resultType="cn.xpu.hcp.bean.User">
select * from user
<where>
<if test="id>=1">
id=#{id}
</if>
<if test="username!=null and username!=''">
and username like "%"#{username}"%"
</if>
<if test="sex!=null and sex!=''">
and sex=#{sex}
</if>
<if test="address!=null and address!=''">
and address=#{address}
</if>
</where>
</select>
使用了where标签后在SQL语句中不需要写where了,会自动生成。
我们也不需要担心出现类似select * from user where and username like ‘张’ 这样的SQL语句了,MyBatis会自动将第一个and(或or)剔除。自动变为:select * from user where username like ‘张’
set标签
未使用set标签前:
<update id="updateByUser" parameterType="cn.xpu.hcp.bean.User">
update user
set
<if test="birthday!=null">
birthday = #{birthday},
</if>
<if test="username!=null and username!=''">
username = #{username},
</if>
<if test="sex!=null and sex!=''">
sex=#{sex},
</if>
<if test="address!=null and address!=''">
address=#{address},
</if>
where id=#{id}
</update>
一大问题,“,”的取舍问题,最后一个的“,”怎么办?
这也是使用set标签的好处了,set标签可以帮我们处理最后一个逗号:
<update id="updateByUser" parameterType="cn.xpu.hcp.bean.User">
update user
<set>
<if test="birthday!=null">
birthday = #{birthday},
</if>
<if test="username!=null and username!=''">
username = #{username},
</if>
<if test="sex!=null and sex!=''">
sex=#{sex},
</if>
<if test="address!=null and address!=''">
address=#{address},
</if>
</set>
where id=#{id}
</update>
同样,使用set标签后sql语句中不要写set了。
测试:
public void MapperTest(){
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User();
user.setId(2);
user.setSex("f");
user.setUsername("孙尚香");
mapper.updateByUser(user);
session.commit();
session.close();
}
trim标签
trim 标签的主要功能是可以在自己包含的内容前加上某些前缀,也可以在其后加上某些后缀,与之对应的属性是 prefix 和 suffix;可以把包含内容的首部某些内容覆盖,即忽略,也可以把尾部的某些内容覆盖,对应的属性是 prefixOverrides 和 suffixOverrides;正因为 trim 有这样的功能,所以我们也可以非常简单的利用 trim 来代替 where /set标签的功能。
代替where标签:
select * from user
<trim prefix="where" suffixOverrides="and"><!--如果是要去除or就写or-->
<if test="id>=1">
id=#{id}
</if>
<if test="username!=null and username!=''">
and username LIKE "%"#{username}"%"
</if>
<if test="sex!=null and sex!=''">
and sex=#{sex}
</if>
<if test="address!=null and address!=''">
and address=#{address}
</if>
</trim>
代替set标签:
update user
<trim prefix="set" suffixOverrides=",">
<if test="birthday!=null">
birthday = #{birthday},
</if>
<if test="username!=null and username!=''">
username = #{username},
</if>
<if test="sex!=null and sex!=''">
sex=#{sex},
</if>
<if test="address!=null and address!=''">
address=#{address},
</if>
where id=#{id}
</trim>
choose标签
choose标签是按顺序判断其内部when标签中的test条件出否成立,如果有一个成立,则 choose 结束。当 choose 中所有 when 的条件都不满则时,则执行 otherwise 中的sql。类似于Java 的 switch 语句,choose 为 switch,when 为 case,otherwise 则为 default。
<select id="testChoose" parameterType="cn.xpu.hcp.bean.User" resultType="cn.xpu.hcp.bean.User">
select * from user
<where>
<choose>
<when test="id>=1">
id=#{id}
</when>
<when test="username!=null and username!=''">
and username LIKE "%"#{username}"%"
</when>
<when test="sex!=null and sex!=''">
and sex=#{sex}
</when>
<when test="address!=null and address!=''">
and address=#{address}
</when>
<otherwise>
</otherwise>
</choose>
</where>
</select>
测试:
public void MapperTest(){
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User();
user.setSex("f");
user.setUsername("张");
List<User> list = mapper.testChoose(user);
for (User u : list) {
System.out.println(u);
}
session.commit();
session.close();
}
如果使用if标签则会查出有”张”字并且性别为“f”的人,使用了choose标签后,由于已经满足了有“张”字这一条件,就不会进行下面的性别判断了。
foreach标签
向SQL语句传递数组或集合,MyBatis使用foreach进行解析。
比如:
select * from user where id in (1,5,6)
解析包装类中的集合/数组
在包装类中声明集合ids,并且添加getter/setter方法:
public class UserWrapper {
private List<Integer> ids;
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
}
在UserMapper.xml中:
<select id="testForeach" parameterType="cn.xpu.hcp.bean.UserWrapper" resultType="cn.xpu.hcp.bean.User">
select * from user
<!--
collection:遍历的集合
item:遍历的项目,名字任意,只需要与下面#{}中的相同即可
open:在前面添加的sql片段
close:在结尾添加的sql片段
separator:以什么为分割符,这里选择","
如此组成sql语句:select * from user where id in (#{item},...)
-->
<where>
<foreach collection="ids" item="item" open="id in(" close=")" separator=",">
#{item}
</foreach>
</where>
</select>
测试:
public void MapperTest(){
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
UserWrapper wrapper = new UserWrapper();
List<Integer> ids = new ArrayList<>();
ids.add(2);
ids.add(3);
ids.add(10);
ids.add(13);
wrapper.setIds(ids);
List<User> list = mapper.testForeach(wrapper);
for (User u : list) {
System.out.println(u);
}
session.commit();
session.close();
}
解析直接传递的数组/集合
测试类:
public void MapperTest(){
SqlSession session = sqlSessionFactory.openSession();
UserMapper mapper = session.getMapper(UserMapper.class);
Integer[] ids = {1,3,10,15};
List<User> list = mapper.testForeach(ids);//直接传递数组
for (User u : list) {
System.out.println(u);
}
session.commit();
session.close();
}
此时的foreach中:
<foreach collection="array" item="item" open="id in(" close=")" separator=",">
......
</foreach>
注意collection的变化,此时的值不应该为ids,而是array,所以如果传递集合就为list。