一、MyBatis框架动态SQL处理简单的多参数查询
MyBatis框架主要通过标签的配合使用实现SQL语句的动态拼接、前后缀格式化处理、复杂参数处理等功能。MyBatis框架动态SQL的常用标签:标签:if,说明:条件判断,与Java中的if语句类似;标签:where,说明:为SQL语句动态添加where关键字;标签:choose,说明:条件判断,这是一个组合标签,需要与when、otherwise标签搭配使用。可实现与Java中的switch语句类似的功能。标签:foreach,说明:以遍历方式处理集合类型参数。标签:set,说明:为SQL语句动态添加set关键字,实现动态实现数据更新功能。标签:trim,说明:对SQL语句进行格式化处理,添加或移除前后缀。
二、if标签
if标签是MyBatis框架动态SQL技术中重要且常用的标签之一,它所实现的功能与Java中的if语句基本相同,用法也很相似。
三、where标签
where标签的主要作用是对SQL语句中的where关键字进行简化处理,并可以智能地处理其内部and、or等关键字,避免多余字符带来的语法错误。下面通过代码来演示使用where标签的优势。仍然以用户列表查询为例,从用户表中查找realName中含有“赵”字的用户,给realName赋值为“赵”。测试方法testGetUserList()中的关键代码如下所示:
String realName = "赵";
Integer roleId = null;
userList = sqlSession.getMapper(SysUserMapper.class).selectList(realName,roleId);
此时,不需关注roleName的值。因此,查询中不再关联角色表,并根据MySQL语法规则去掉第一个过滤语句前的and关键字。改版后的SQL语句如下所示:
<select id="selectList" resultType="SysUser">
select * from t_sys_user
<if test="roleId != null">
roleId = #{roleId}
</if>
<if test="realName != null and realName != ''">
and realName like CONCAT ('%',#{realName},'%')
</if>
</select>
四、choose(when、otherwise)标签
choose标签是一个组合标签,通常与when、otherwise标签配合使用,实现了类似于Java中switch语句的功能,语法如下:
<choose>
<when test="条件判断,返回true或false ">
SQL语句······
</when>
<when test="条件判断,返回true或false ">
SQL语句······
</when>
<otherwise>
SQL语句······
</otherwise>
</choose>
执行测试方法,系统报错。观察控制台输出的SQL语句可发现:where关键字后直接拼接了一个“and”。
五、MyBatis框架动态SQL处理集合参数
在3.1节中学习了MyBatis框架的if、where、choose标签。如果要查询角色是“系统管理员”和“店长”的用户,应该怎样实现呢?
根据上述需求,分析可以得知,角色ID有多个,所以这个参数的数据类型应该是一个数组或集合。MySQL通过in语句来处理有多个值的参数。下面是查询roleId值分别为1和2用户的SQL语句。
select * from t_sys_user where roleId in (1,2);
MyBatis框架通过foreach标签对这类参数进行循环处理,最终拼接出一个符合MySQL语法的in语句来处理这类参数。foreach标签处理数组、List集合、Map对象类型参数的语法如下所示:
<foreach collection="参数名称" item="元素别名" open="(" separator="," close=")"
index="当前元素位置下标">
#{元素别名}
</foreach>
语法中的属性介绍如下。
→ itm:遍历数组时,为数组或List集合中的元素起的别名。
→ open:起始位置的拼接字符,表示in语句以“(”左括号开始。
→ close:结束位置的拼接字符,表示in语句以“)”右括号结束。
→ separator:元素之间的连接符,表示in语句中的元素之间以“,”逗号连接。
→ collection:参数名称。当参数为数组类型时,默认参数名为array。当参数类型为List集合时,默认参数名为list。当参数类型为Map时,参数名为Map中集合元素所在键值对的key。下面分别以数组、List集合、Map对象为参数进行测试。
六、foreach标签处理数组类型参数
当参数为数组类型时,代码如下所示:
在SysUserMapper中添加getUserByRoleIdArray()方法:
/**
* 根据角色ID数组查询用户列表信息
* @param roleIds
* @return
*/
public List<SysUser> getUserByRoleIdArray(Integer[] roleIds);
在SysUserMapper.xml中添加的查询代码:
<select id="getUserByRoleIdArray" resultType="SysUser">
select * from t_sys_user where roleId in
<foreach collection="array" item="item" open="(" separator="," close=",">
#{item}
</foreach>
</select>
在测试类中编写测试方法testGetUserByRoleIdArray():
@Test
public void testGetUserByRoleIdArray() {
SqlSession sqlSession = null;
List<SysUser> userList = new ArrayList<SysUser>();
Integer[] roleIds = {1,2};
try {
sqlSession = MyBatisUtil.createSqlSession();
userList = sqlSession.getMapper(SysUserMapper.class)
.getUserByRoleIdArray(roleIds);
} catch (Exception e) {
e.printStackTrace();
} finally {
MyBatisUtil.closeSqlSession(sqlSession);
}
logger.info("查询到用户数量:" + userList.size());
for(SysUser user : userList) {
logger.info("查询到用户信息:" + user);
}
}
执行测试方法testGetUserByRoleIdArray(),从控制台输出的信息中可以看到,item、open、close、separator属性配置的值都正确地被拼接到SQL语句中。数组中的元素则作为SQL语句的参数以占位符的方式被拼接到in语句中。
七、foreach标签处理List类型参数
当参数为List集合时,实现代码如下所示:
在SupplierMapper中添加getUserByRoleIdList()方法:
/**
* 根据角色ID集合 查询用户列表信息
* @param roleList
* @return
*/
public List<SysUser> getUserByRoleIdList(List<Integer> roleList);
在SupplierMapper.xml中添加的查询代码:
<select id="getUserByRoleIdList" resultType="SysUser">
select * from t_sys_user where roleId in
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
在测试类中创建测试方法testGetUserByRoleIdList(),关键代码如下所示。
List roleList =new ArrayList<Integer>();
roleList.add(1);
roleList.add(2);
userList =sqlSession.getMapper(SysUserMapper.class)
.getUserByRoleIdList(roleList);
执行测试方法,所得数据与使用数组为参数时相同,输出的SQL语句也相同。
八、foreach标签处理Map类型参数
当参数为Map类型时,实现代码如下所示:
在SysUserMapper中添加getUserByRoleIdMap()方法:
/**
* 根据角色ID集合(集合存在Map中)查询用户列表信息
* @param roleMap
* @return
*/
List<SysUser> getUserByRoleIdMap(Map<String,Object> roleMap);
在SysUserMapper.xml中添加的查询代码:
<select id="getUserByRoleIdMap" resultType="SysUser">
select * from t_sys_user where roleId in
<foreach collection="roleIdList" item="item" open="(" separator=","
close=")">
#{item}
</foreach>
</select>
在测试类中创建测试方法testGetUserByRoleMap(),关键代码如下所示。
List<Integer> roleIdList = new ArrayList<Integer>();
roleIdList.add(1);
roleIdList.add(2);
Map<String, Object> roleMap =new HashMap<String, Object>();
roleMap.put("roleIdList", roleIdList);
userList = sqlSession.getMapper(SysUserMapper.class).getUserByRoleIdMap(roleMap);
参数roleIdList集合中的值为1和2。执行测试。获得结果与前两次相同。这里省略控制台输出的信息。
对比此数组、List集合、Map对象为参数进行的三次测试可以发现,XML文件中的查询代码大同小异。其中有区别的只有用来标识参数名称的collection属性。如果使用@Param注解为参数配置了参数名称,则此处对应的就是参数名称。
实际上,MyBatis框架在进行参数传递时都会把它封装成一个Map对象,而Map对象的key就是参数名称,参数的值就是key对应的value。
小结:
(1)当参数为数据基本类型或数组、List集合类型时,MyBatis框架会将参数封装在一个Map对象中。
(2)当参数为数组类型时,collection对应值默认为array。
(3)当参数为List集合类型时,collection对应值默认为list。
(4)如果使用@Param注解为参数设置了名称,collection对应值为参数名。
(5)当参数为Map对象时,collection对应值为该Map对象中数组或集合元素对应的key。
九、MyBatis框架动态SQL处理更新功能
MyBatis框架动态更新数据的功能主要通过set+if标签实现。
1.set标签
724系统中提供了编辑用户信息的功能。假设要更新用户ID为16的用户真实姓名
SysUserMapper.xml中的更新代码如下所示:
<update id="update" parameterType="SysUser">
update t_sys_user set
account=#{account},realName=#{realName},password=#{password},
sex=#{sex},birthday=#{birthday},phone=#{phone},address=#{address},
roleId=#{roleId}, updatedUserId=#{updatedUserId},
updatedTime=#{updatedTime}
where id = #{id}
</update>
测试类中的更新方法testUpdate()如下所示。
@Test
public void testUpdate() {
SqlSessionsqlSession =null;
Integer userId =16;
int count =0;
try {
SysUser user =new SysUser();
user.setId(userId);
user.setRealName("测试用户修改");
user.setUpdatedUserId(1);
user.setUpdatedTime(new Date());
sqlSession = MyBatisUtil.createSqlSession();
count =sqlSession.getMapper(SysUserMapper.class).update(user);
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
sqlSession.rollback();
} finally {
MyBatisUtil.closeSqlSession(sqlSession);
}
logger.info("修改Id=" + userId +"的用户修改" + (count ==0 ?"失败" :"成功"));
}
上述这种方式中只修改部分数据时会有一个很严重的问题,下面通过测试进行验证。
经验:
针对示例9中的问题,也可以通过更新数据之前先从数据库中把这条要更新的数据查询出来,然后对其进行修改,最后把修改后的数据更新到数据库的方式来避免。
操作步骤如下。
第一步:从库中查询出完整的数据信息。
第二步:为这条数据中需要修改的字段重新赋值。
第三步:把这条数据更新到数据库中。
还有一种情况,即用户在修改数据时,确实清除了某个属性的值。例如,在修改个人信息时删除除了地址信息。但是,如果使用if+set标签方式进行处理,则会忽略这个修改项,导致修改无效。所以,通常会在XML文件中提供全部修改和部分修改两种方式,供程序员在开发过程中灵活使用。
十、MyBatis框架动态SQL知识扩展
除前面节学习的标签外,MyBatis框架还提供了其他标签实现动态SQL技术。
1. trim标签
where、set标签能够动态地为SQL语句添加前后缀,并可以智能地忽略标签前后多余的and、or或逗号等字符。除where和set标签外,MyBatis框架还提供了更为灵活的trim标签来实现类似的功能。trim标签的语法如下所示:
<trim prefix="前缀" suffix="后缀" prefixOverrides="忽略前缀"
suffixOverrides="忽略后缀">
......
</trim>
其中,trim标签的属性介绍如下:
→ prefix:前缀,可以自动对trim标签内部首部指定的内容。
→ suffix:后缀,在trim标签包含的语句末尾拼接后缀。
→ prefixOverrides:忽略的前缀,忽略trim标签内部首部指定的内容。
→ suffixOverrides:忽略的后缀,忽略trim标签包含内容尾部指定的内容。
根据以上语法,使用trim标签替换示例4中的where标签,实现相同功能,代码如下所示:
SysUserMapper.xml中查询数据的代码:
<select id="selectList" resultType="SysUser">
select * from t_sys_user
<trim prefix="where" prefixOverrides="and|or" >
<if test="roleId != null">
and roleId = #{roleId}
</if>
<if test="realName != null and realName != ''">
and realName like CONCAT ('%',#{realName},'%')
</if>
</trim>
</select>
测试方法testUpdate()中的关键代码:
String realName = "赵";
Integger roleId = null;
userList = sqlSession.getMapper(SysUserMapper.class).selectList(realName,
roleId);
执行测试方法,在控制台输出SQL语句,SQL语句中where关键字被正确拼接,且where后也没有出现多余的and。
十一、MyBatis框架的分页功能
MyBatis框架并没有对分页功能做过多的处理,而是使用数据库自带的分页功能。下面以MySQL数据库为例进行本节内容的学习。
1.基于MySQL的分页查询
MySQL数据库的分页关键字为limit,使用MyBatis框架对其进行操作时,用法与在客户端的操作基本相同。下面对其进行简单的介绍。
SysUserMapper中分页查询方法selectPageList()的代码:
/**
* 分页查询用户列表
* @param realName
* @param roleId
* @param pageBegin
* @param pageSize
* @return
*/
public List<SysUser> selectPageList(@Param("realName") String realName
,@Param("roleId") Integer roleId
, @Param("pageBegin") Integer pageBegin
, @Param("pageSize") Integer pageSize);
SysUserMapper.xml中分页查询的代码:
<select id="selectPageList" resultType="SysUser">
select * from t_sys_user
<trim prefix="where" prefixOverrides="and |or" >
<if test="realName != null and realName != ''">
and realName like CONCAT ('%',#{realName},'%')
</if>
<if test="roleId != null">
and roleId = #{roleId}
</if>
</trim>
order by createdTime desc
limit #{pageBegin}, #{pageSize}
</select>
测试类中测试方法testGetUserPage()的代码:
@Test
public void testGetUserPage() {
SqlSession sqlSession = null;
List<SysUser> userList = new ArrayList<SysUser>();
try {
sqlSession = MyBatisUtil.createSqlSession();
String realName = "";
Integer roleId = 2;
Integer pageIndex = 2;
Integer pageSize = 2;
Integer pageBegin = (pageIndex -1) * pageSize;//计算查询起始位置
userList = sqlSession.getMapper(SysUserMapper.class)
.selectPageList(realName, roleId, pageBegin, pageSize);
//查询分页数据
} catch (Exception e) {
e.printStackTrace();
} finally {
MyBatisUtil.closeSqlSession(sqlSession);
}
logger.info("查询到用户数量:" + userList.size());
for (SysUser user: userList) {
logger.info("查询到用户信息:" + user);
}
}
本章总结
• MyBatis框架的动态SQL技术是通过一个或多个标签的使用来实现的。
• where+if标签可以实现动态查询功能。
• choose(when、otherwise)组合实现多条件查询时,只匹配其中一个条件。
• foreach标签可以实现对数组、List集合等多值参数的处理。
• set+if标签可以实现动态更新功能。
• trim标签可以为SQL语句动态添加或移除指定的前后缀。