一、动态SQL介绍
MyBatis 的强大特性之一便是它的动态 SQL。如果有使用 JDBC 或其他类似框架的经验,就能体会到根据不同条件拼接 SQL 语句有多么痛苦。拼接的时候要确保不能忘了必要的空格,还要注意省掉列名列表最后的逗号。利用动态 SQL 这一特性可以彻底摆脱这种痛苦。
通常使用动态 SQL 不可能是独立的一部分,MyBatis 当然使用一种强大的动态 SQL 语言来改进这种情形,这种语言可以被用在任意的 SQL 映射语句中。
动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处理器相似。在 MyBatis 之前的版本中,有很多的元素需要来了解。MyBatis 3 大大提升了它们,现在用不到原先一半的元素就可以了。MyBatis 采用功能强大的基于 OGNL(Struts2语法) 的表达式来消除其他元素。
mybatis 的动态sql语句是基于OGNL表达式的。可以方便的在 sql 语句中实现某些逻辑. 总体说来mybatis 动态SQL 语句主要有以下几类:
- if 语句 (简单的条件判断)
- choose (when,otherwise) ,相当于java 语言中的 switch ,与 jstl 中的choose 很类似.
- trim (对包含的内容加上 prefix,或者 suffix 等,前缀,后缀)
- where (主要是用来简化sql语句中where条件判断的,能智能的处理 and or ,不必担心多余导致语法错误)
- set (主要用于更新时)
- foreach (在实现 mybatis in 语句查询时特别有用)
二、动态SQL的使用
首先建立数据库和建立users表
建立users实体类:
package com.little.entity;
public class Users {
private Integer id;
private String name;
private String sex;
private String address;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Users{" +
"id=" + id +
", name='" + name + '\'' +
", sex='" + sex + '\'' +
", address='" + address + '\'' +
'}';
}
}
①if+where元素
根据name和sex元素来查询数据,如何name为空,sex不为空,根据sex来查询,反之根据name来查询,都为空就全查,都不为空就根据两者的条件来查询。
<select id="queryByNameAndSex" resultType="Users" parameterType="Users">
select * from users
<where>
<if test="name != null">
and name = #{name}
</if>
<if test="sex != null">
and sex = #{sex}
</if>
</where>
</select>
这个“where”标签会知道如果它包含的标签中有返回值的话,它就插入一个‘where’。此外,如果标签返回的内容是以AND 或OR 开头的,则它会剔除掉。
②if+set语句
比如在进行更新的时候,使用到set语句,MyBatis也有对应的set元素
<update id="updateUsersById" parameterType="Users">
update users u
<set>
<if test="name != null and name != ''">
u.name = #{name},
</if>
<if test="sex != null and sex != ''">
u.sex = #{sex},
</if>
</set>
where id=#{id}
</update>
如果出现多余逗号 会自动去掉
③choose(when,otherwise)
有时候,我们不想用到所有的查询条件,只想选择其中的一个,查询条件有一个满足即可,使用 choose 标签可以解决此类问题,类似于 Java 的 switch 语句
<select id="selectUsersByChoose" resultType="Users" parameterType="Users">
select * from users
<where>
<choose>
<when test="id !='' and id != null">
id=#{id}
</when>
<when test="name !='' and name != null">
and name=#{name}
</when>
<otherwise>
and sex=#{sex}
</otherwise>
</choose>
</where>
</select>
这里就是只根据一个条件来进行查询,按照顺序进行
④trim
trim标记是一个格式化的标记,可以完成set或者是where标记的功能
用 trim 改写上面的 if+where 语句
<select id="selectUsersByNameAndSex" resultType="users" parameterType="Users">
select * from users
<trim prefix="where" prefixOverrides="and | or">
<if test="name != null">
and name=#{name}
</if>
<if test="sex != null">
and sex=#{sex}
</if>
</trim>
</select>
prefix:前缀
prefixoverride:去掉第一个and或者是or
用 trim 改写上面的 if+set 语句
<!-- 根据 id 更新 user 表的数据 -->
<update id="updateUsersById" parameterType="Users">
update users u
<trim prefix="set" suffixOverrides=",">
<if test="name != null and name != ''">
u.name = #{name},
</if>
<if test="sex != null and sex != ''">
u.sex = #{sex},
</if>
</trim>
where id=#{id}
</update>
suffix:后缀
suffixoverride:去掉最后一个逗号(也可以是其他的标记,就像是上面前缀中的and一样)
⑤SQL片段
有时候可能某个 sql 语句我们用的特别多,为了增加代码的重用性,简化代码,我们需要将这些代码抽取出来,然后使用时直接调用。
比如:假如我们需要经常根据用户名和性别来进行联合查询,那么我们就把这个代码抽取出来,如下:
<!-- 定义 sql 片段 -->
<sql id="selectUsersByNameAndSexSQL">
<if test="name != null and name != ''">
AND name = #{name}
</if>
<if test="sex != null and sex != ''">
AND sex = #{sex}
</if>
</sql>
引用SQL片段
<select id="selectUsersByNameAndSex" resultType="users"
parameterType="Users">
select * from users
<trim prefix="where" prefixOverrides="and | or">
<!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
<include refid="selectUsersByNameAndSexSQL"></include>
<!-- 在这里还可以引用其他的 sql 片段 -->
</trim>
</select>
注意:最好基于 单表来定义 sql 片段,提高片段的可重用性
在 sql 片段中不要包括 where
⑥foreach
需求:我们需要查询 users 表中 id 分别为1,2,3的用户
sql语句:select * from users where id=1 or id=2 or id=3 select * from users where id in (1,2,3)
建立一个 UserVo 类,里面封装一个 List<Integer> ids 的属性
public class UserVo {
//封装多个用户的id
private List<Integer> ids;
public List<Integer> getIds() {
return ids;
}
public void setIds(List<Integer> ids) {
this.ids = ids;
}
}
我们用 foreach 来改写 select * from users where id=1 or id=2 or id=3
<select id="selectUsersByListId" parameterType="UserVo"
resultType="Users">
select * from users
<where>
<!-- collection:指定输入对象中的集合属性 item:每次遍历生成的对象 open:开始遍历时的拼接字符串 close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串 select * from users where 1=1 and (id=1 or id=2 or
id=3) -->
<foreach collection="ids" item="id" open="and (" close=")"
separator="or">
id=#{id}
</foreach>
</where>
</select>
测试:
//根据id集合查询users表数据
@Test
public void testSelectUsersByListId(){
UserVo uv = new UserVo();
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(3);
uv.setIds(ids);
List<Users> listUsers = mapper.selectUsersByListId(uv);
for(Users u : listUsers){
System.out.println(u);
}
session.close();
}
我们用 foreach 来改写 select * from users where id in (1,2,3)
<select id="selectUsersByListId" parameterType="Users" resultType="Users">
select * from users
<where>
<!--
collection:指定输入对象中的集合属性
item:每次遍历生成的对象
open:开始遍历时的拼接字符串
close:结束时拼接的字符串
separator:遍历对象之间需要拼接的字符串
select * from user where 1=1 and id in (1,2,3)
-->
<foreach collection="ids" item="id" open="and id in (" close=") " separator=",">
#{id}
</foreach>
</where>
</select>
单参数List的类型
<select id="dynamicForeachTest" resultType="Users">
select * from users where id in
<foreach collection="list" index="index" item="item" open="("
separator="," close=")">
#{item}
</foreach>
</select>
测试:
public List<User> dynamicForeachTest(List<Integer> ids);
@Test
public void testSelectUserByListId1(){
List<Integer> ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
ids.add(3);
List<Users> listUsers = mapper.dynamicForeachTest(ids);
for(Users u : listUsers){
System.out.println(u);
}
}
单参数array数组的类型
<select id="dynamicForeach2Test" resultType="User">
select * from users where id in
<foreach collection="array" index="index" item="item" open="("
separator="," close=")">
#{item}
</foreach>
</select>
public List<Users> dynamicForeach2Test(Integer[] ids);
@Test
public void dynamicForeach2Test(){
Integer[] ids={1,2,3};
List<Users> listUsers = mapper.dynamicForeach2Test(ids);
for(Users u : listUsers){
System.out.println(u);
}
}
将参数封装成Map的类型
<select id="dynamicForeach3Test" resultType="User">
select * from users where name like "%"#{name}"%" and id in
<foreach collection="ids" index="index" item="item" open="("
separator="," close=")">
#{item}
</foreach>
</select>
public List<Users> dynamicForeach3Test(Map params);
测试:
@Test
public void dynamicForeach3Test() {
List ids = new ArrayList();
ids.add(28);
ids.add(29);
ids.add(30);
Map params = new HashMap();
params.put("ids", ids);
params.put("ame", "张");
List<Users> listUsers = mapper.dynamicForeach3Test(params);
for (Users u : listUsers) {
System.out.println(u);
}
}