文章目录
Mybatis 映射文件的 SQL 深入
注:所有的代码的基础基于利用 dao 实现类来完成。
Mybatis 的映射文件中,前面我们的 SQL 都是比较简单的,有些时候业务逻辑复杂时,我们的 SQL 是动态变化的,此时在前面学习中我们的 SQL 就不能满足要求了。
所以,就需要 SQL 语句可以动态地进行变化。这里的动态的 SQL 语句都是和查询相关的。
1. if 标签
根据实体类的不同取值,使用不同的 SQL 语句来进行查询。比如在 id 如果不为空时可以根据 id 查询,如果 username 不同时空时,还要加入用户名为条件。这种情况在我们的多条件组合查询中经常会碰到。
1.1 if 标签的使用
-
首先配置持久层 Dao 接口:
/** * 根据传入的参数条件查询 * @param user 查询条件,有可能有用户名,有可能有性别,有可能有地址,还有可能是都有 * @return */ List<User> findUserByCondition(User user);
-
然后在 映射配置文件中进行如下的配置:
<!-- 根据条件查询--> <select id="findUserByCondition" resultMap="userMap" parameterType="user"> select * from user where 1=1 <if test="userName != null"> and userName = #{userName} </if> </select>
这里需要注意的是,除了和 SQL 语句相关的部分,其他的部分严格区分大小写。
这里的 SQL 语句中的
where 1=1
这个条件始终为True,在不定数量查询条件下, 1=1可以很方便地规范语句。where 1=1 的应用不是什么高级的应该,也不是所谓的智能化的构造,仅仅只是为了满足多条件查询页面中不确定的各种因素而采用的一种构造一条正确并且能运行的动态SQL语句的一种方式。当动态建立SQL语句时,如果动态构建的语句错误,那么该语句相当于select * from user
,查询所有的数据。
2. where 标签的使用
如果以上的条件很多,那么会有多组 where 1=1,配置文件会变得相当的麻烦。为了简化上面 where 1=1 的条件拼装,我们可以采用 < where > 标签来简化开发。
-
首先编写持久层 dao 接口:
/** * 根据传入的参数条件查询 * @param user 查询条件,有可能有用户名,有可能有性别,有可能有地址,还有可能是都有 * @return */ List<User> findUserByCondition(User user);
-
然后,编写持久层 dao 映射配置文件:
<select id="findUserByCondition" resultMap="userMap" parameterType="user"> select * from user <where> <if test="userName != null"> and userName = #{userName} </if> <if test="userSex != null"> and sex = #{userSex} </if> </where> </select>
这里使用 where 标签将 if 标签包裹住,同时删除 where 1=1 的SQL语句。使用 where 标签可以使我们不用写 where 1=1这个永远为真的条件,从而简化SQL语句,让SQL语句更加清晰,方便后续的阅读。
3. foreach 标签
3.1 需求
传入多个id查询用户信息,用下边的两个SQL实现:
select * from users where username like "%张%" and (id = 10 or id = 89 or id = 16);
select * from users where username like "%张%" and id in (10, 89, 16);
如果我要从一个集合中,获取 id 值,并将数据查询出来呢?
这里就要用到 foreach 标签。
3.2 foreach 标签的属性
看以下一个 foreach 标签
<foreach collection="ids" open="and id in(" close=")" item="id" separator=",">
#{id}
</foreach>
collection:表示后面的 ids 是一个集合
open:表示开始,说明需要符合的查询数据的条件是从 in 后面的括号开始的
close:表示关闭
item:需要填充的单元名
separator:通过什么符号分隔的数据
然后以上的需求,反映到映射配置文件中,应该是这样的:
<select id="findUserInIds" resultMap="userMap" parameterType="queryvo">
select * from user
<where>
<if test="ids != null and ids.size()>0">
<foreach collection="ids" open="and id in (" close=")" item="id" separator=",">
#{id}
</foreach>
</if>
</where>
</select>
注意:在 foreach 标签中,如果 item 属性值为 uid,那么 #{id} 就要变为 #{uid} 需要和 item 的属性值保持一致。
3.3 foreach 标签的例子
-
首先依然在 dao 文件中添加一下方法:
/** * 根据 QueryVo 中提供的 id 集合,查询用户信息 * @param vo * @return */ List<User> findUserInIds(QueryVo vo);
-
然后再映射配置文件中:
<!-- 根据 QueryVo 中的 id 集合, 实现查询用户列表--> <select id="findUserInIds" resultMap="userMap" parameterType="queryvo"> select * from user <where> <if test="ids != null and ids.size()>0"> <foreach collection="ids" open="and id in (" close=")" item="id" separator=","> #{id} </foreach> </if> </where> </select>
-
然后再实体类中添加包装类
package com.itheima.domain; import java.util.List; public class QueryVo { private List<Integer> ids; private User user; public User getUser() { return user; } public void setUser(User user) { this.user = user; } public List<Integer> getIds() { return ids; } public void setIds(List<Integer> ids) { this.ids = ids; } }
-
最后在测试类中调用该方法:
@Test public void testFindInIds(){ QueryVo vo = new QueryVo(); List<Integer> list = new ArrayList<Integer>(); list.add(41); list.add(42); list.add(46); vo.setIds(list); List<User> users = userDao.findUserInIds(vo); for(User user : users) { System.out.println(user); } }
-
这里是以传入的类型是包装类为例子的,如果想传入的类型直接是 List 类型而不是包装类类型,可以尝试以下方式:
- 其他的不变,只是修改 IUserDao 接口和 IUserDao.xml 映射配置文件
-
public interface IUserDao { List<User> findByIds(List<User> list>; }
-
<!-- 根据 QueryVo 中的 id 集合, 实现查询用户列表--> <select id="findUserInIds" resultMap="userMap"> select * from user <where> <if test="ids != null and ids.size()>0"> <foreach collection="list" open="and id in (" close=")" item="id" separator=","> #{id} </foreach> </if> </where> </select>
4. sql标签
4.1 需求
在以上的操作,查看映射文件,会发现有很多的 select 标签,有很多的重复的内容。我们就可以抽取重复的 SQL 语句。
这里就有一个 sql 标签来抽取重复的SQL语句
4.2 实例
-
在映射文件中,可以加入以下的标签:
<sql id="defaultUser"> select * from user </sql>
-
然后对于上面的查询语句,就可以通过id导入 sql 标签下的内容来达到减少代码的目的:
<!-- 根据 QueryVo 中的 id 集合, 实现查询用户列表--> <select id="findUserInIds" resultMap="userMap" parameterType="queryvo"> <include refid="defaultUser"></include> <where> <if test="ids != null and ids.size()>0"> <foreach collection="ids" open="and id in (" close=")" item="id" separator=","> #{id} </foreach> </if> </where> </select>