Mybatis的强大特性之一是它的动态SQL,在进行项目开发的时候,我们对数据库的操作不可能全部是定式的,当对数据库的操作根据不同情况发生变化时,就可以用到Mybatis的动态SQL。而Mybatis3的动态SQL在XML中支持的标签主要是以下几种:
- if
- choose(when、otherwise)
- trim(where、set)
- foreach
- bind
接下来我们将分别介绍这几种标签及其例子。
一. if 用法
在动态SQL中,我们很多时候都会用到 if 标签,通过对参数值的判断来决定下一步的操作。在这里我们设计一个高级的用户查询功能,能根据输入的条件自动检索相应的信息,首先在UserMapper接口中创建selectByUser方法,再往UserMapper.xml中添加动态SQL语句
/**
* 根据动态条件查询用户信息
*
* @param sysUser
* @return
*/
List<SysUser> selectByUser(SysUser sysUser);
<select id="selectByUser" resultType="SysUser">
select id,
user_name userName,
user_password userPassword,
user_email userEmail,
user_info userInfo,
head_img headImg,
create_time createTime
from sys_user
where 1 = 1
<if test="userName != null and userName != ''">
and user_name like concat('%', #{userName}, '%')
</if>
<if test="userEmail != null and userEmail != ''">
and user_Email = #{userEmail}
</if>
</select>
在这个方法中,当输入用户名或用户邮箱或两者同时输入都会查询相应的结果。在if标签中有一个必填的属性test,test的属性值是一个判断表达式,结果为true或false;当有多个判断条件时,使用and或or进行连接,也可采用括号分组。
接下来我们再添加一个updateByIdSelective的方法,它能实现只更新出现变化的字段的需求。
/**
* 根据主键更新
*
* @param sysUser
* @return
*
* */
int updateByIdSelective(SysUser sysUser);
<update id="updateByIdSelective">
update sys_user
set
<if test="userName != null and userName != ''">
user_name = #{userName},
</if>
<if test="userPassword != null and userPassword != ''">
user_password = #{userPassword},
</if>
<if test="userEmail != null and userEmail != ''">
user_email = #{userEmail},
</if>
<if test="userInfo != null and userInfo != ''">
user_info = #{userInfo},
</if>
<if test="headImg != null and headImg != ''">
head_img = #{headImg},
</if>
<if test="createTime != null and createTime != ''">
create_time = #{createTime},
</if>
id = #{id}
where id = #{id}
</update>
在这个动态SQL语句中,需要注意几个地方,因为动态SQL同样需要遵循SQL的语法规则,所以需要注意每个if元素中sql语句后的逗号,同时还需要注意最后的id=#{id}的条件,如果没有这个条件,则会出现update sys_user set where id = #{id}或者update sys_user set user_name = #{userName}, where id = #{id}这样的情况(具体为什么大家感兴趣可以去思考一下),而这些sql语句很明显是错误的。
最后我们再看一个insert中使用if的例子。在很多时候,我们在往表中插入数据时只用插入部分列,其他列则使用数据库的默认值,这也需要用到动态SQL。我们添加一个方法insertSelective以及它的动态SQL语句。
/**
* 动态插入新用户
*
* @param sysUser
* @return
*
* */
int insertSelective(SysUser sysUser);
<insert id="insertSelective" useGeneratedKeys="true" keyProperty="id">
insert into sys_user(
user_name, user_password,
<if test="userEmail != null and userEmail != ''">
user_email,
</if>
user_info, head_img, create_time)
values(
#{userName}, #{userPassword},
<if test="userEmail != null and userEmail != ''">
#{userEmail},
</if>
#{userInfo}, #{headImg, jdbcType=BLOB},
#{createTime, jdbcType=TIMESTAMP})
</insert>
通过在insert中使用if,能够实现动态添加数据的功能。需要注意的地方是如果在列的部分增加的if条件,那么在values中也需要增加相同的if条件保证上下文对应。
二.choose(when、otherwise)用法
if标签可以为我们提供基本的条件判断,但是无法实现if...else的功能,而想要实现if...else的功能,则需要用到choose标签,choose标签中包含了when和otherwise两个标签,一个choose中至少有一个when,有0或1个otherwise。我们设计这样一个查询:根据用户id和用户名进行查询,当用户id有值时优先用用户id查询,当用户id没有值时判断用户名是否有值,有值则用用户名查询,没有则查询无结果。添加一个selectByIdOrUserName方法和其动态SQL。
/**
* 根据用户ID或用户名查询
*
* @param sysUser
* @return
*
* */
SysUser selectByIdOrUserName(SysUser sysUser);
<select id="selectByIdOrUserName" resultType="sysUser">
select id,
user_name userName,
user_password userPassword,
user_email userEmail,
user_info userInfo,
head_img headImg,
create_time createTime
from sys_user
where 1 = 1
<choose>
<when test="id != null and id != 0">
and id = #{id}
</when>
<when test="userName != null and userName != ''">
and user_name = #{userName}
</when>
<otherwise>
and 1 = 2
</otherwise>
</choose>
</select>
在使用choose when otherwise时逻辑要严密,不然有可能会导致SQL出错。
三.where、set、trim用法
这三个标签解决了类似的问题,并且where和set都可以通过trim来实现。下面来分别看看这三个标签。
where标签的作用是如果标签中包含的元素有值,就插入一个where,并且会自动剔除直接跟在where后面的and或or,我们将之前的selectByUser改成如下方式。
<select id="selectByUser" resultType="SysUser">
select id,
user_name userName,
user_password userPassword,
user_email userEmail,
user_info userInfo,
head_img headImg,
create_time createTime
from sys_user
<where>
<if test="userName != null and userName != ''">
and user_name like concat('%', #{userName}, '%')
</if>
<if test="userEmail != null and userEmail != ''">
and user_Email = #{userEmail}
</if>
</where>
</select>
和之前的方法相比,这种动态SQL更美观,同时不会产生如where 1=1这样的条件。
set标签的作用与where类型,如果set标签中最后是以逗号结尾的,则会自动剔除,我们修改一个之前的updateByIdSelective方法。
<update id="updateByIdSelective">
update sys_user
<set>
<if test="userName != null and userName != ''">
user_name = #{userName},
</if>
<if test="userPassword != null and userPassword != ''">
user_password = #{userPassword},
</if>
<if test="userEmail != null and userEmail != ''">
user_email = #{userEmail},
</if>
<if test="userInfo != null and userInfo != ''">
user_info = #{userInfo},
</if>
<if test="headImg != null and headImg != ''">
head_img = #{headImg},
</if>
<if test="createTime != null and createTime != ''">
create_time = #{createTime},
</if>
id = #{id},
</set>
where id = #{id}
</update>
这里要注意的是,如果set元素中没有内容,还是会出现SQL错误,所以在这里保留id = #{id}的内容。
而trim则可以相当于where和set标签的父类,where标签和set标签通过trim的实现如下:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
<trim prefix="SET" suffixOverrides=",">
...
</trim>
trim标签有4个属性:
- prefix:当trim元素内包含内容时,会给内容增加prefix指定的前缀。
- prefixOverrides:当trim元素内包含内容时,会把内容中匹配的前缀字符串去掉。
- suffix:当trim元素内包含内容时,会给内容增加suffix指定的后缀。
- suffixOverrides:当trim元素内包含内容时,会把内容中匹配的后缀字符串去掉。
四.测试结果
我们将根据从上之下的顺序将本篇文章中介绍的所有方法的测试方法及其结果展示出来。
- selectByUser
@Test
public void testSelectByUser(){
SqlSession sqlSession = getSqlSession();
try{
UserMapper usermapper = sqlSession.getMapper(UserMapper.class);
//只查询用户名
SysUser query = new SysUser();
query.setUserName("ad");
List<SysUser> userList = usermapper.selectByUser(query);
//只查询用户邮箱
query = new SysUser();
query.setUserEmail("[email protected]");
userList = usermapper.selectByUser(query);
//同时查询用户名和邮箱
query = new SysUser();
query.setUserName("ad");
query.setUserEmail("[email protected]");
userList = usermapper.selectByUser(query);
} finally{
sqlSession.close();
}
}
- updateByIdSelective
@Test
public void testUpdateByIdSelective(){
SqlSession sqlSession = getSqlSession();
try{
UserMapper usermapper = sqlSession.getMapper(UserMapper.class);
SysUser user = new SysUser();
user.setId(1L);
user.setUserEmail("[email protected]");
//只更新id为1内的邮箱信息
int result = usermapper.updateByIdSelective(user);
user = usermapper.selectById(1L);
} finally{
sqlSession.rollback();
sqlSession.close();
}
}
- insertSelective
@Test
public void testInsertSelective(){
SqlSession sqlSession = getSqlSession();
try{
UserMapper usermapper = sqlSession.getMapper(UserMapper.class);
SysUser user = new SysUser();
user.setUserName("test-selective");
user.setUserPassword("123456");
user.setUserInfo("test info");
user.setCreateTime(new Date());
usermapper.insertSelective(user);
user = usermapper.selectById(user.getId());
} finally{
sqlSession.rollback();
sqlSession.close();
}
}
- selectByIdOrUserName
@Test
public void testSelectByIdOrUserName(){
SqlSession sqlSession = getSqlSession();
try{
UserMapper usermapper = sqlSession.getMapper(UserMapper.class);
//ID和用户名都不为Null
SysUser user = new SysUser();
user.setId(1L);
user.setUserName("admin");
SysUser su = usermapper.selectByIdOrUserName(user);
//当id为Null时
SysUser user1 = new SysUser();
user1.setUserName("admin");
su = usermapper.selectByIdOrUserName(user1);
//当id和name都为Null时
SysUser user2 = new SysUser();
su = usermapper.selectByIdOrUserName(user2);
} finally{
sqlSession.close();
}
}