从 01 开始 从 01 开始
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机相关技巧
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • 最全面的 RSS 教程
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金
  • 职场规划
  • 关于离职
  • 杂谈
  • 自媒体
  • 📖 读书

    • 读书工具
    • 走进科学
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
    • 读书笔记
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档

晓林

程序猿,自由职业者,博主,英语爱好者,健身达人
首页
  • 📚 计算机基础

    • 计算机简史
    • 数字电路
    • 计算机组成原理
    • 操作系统
    • Linux
    • 计算机网络
    • 数据库
    • 编程工具
    • 装机
  • 🎨 前端

    • Node
  • JavaSE
  • Java 高级
  • JavaEE

    • 构建、依赖管理
    • Ant
    • Maven
    • 日志框架
    • Junit
    • JDBC
    • XML-JSON
  • JavaWeb

    • 服务器软件
    • 环境管理和配置管理-科普篇
    • Servlet
  • Spring

    • Spring基础
  • 主流框架

    • Redis
    • Mybatis
    • Lucene
    • Elasticsearch
    • RabbitMQ
    • MyCat
    • Lombok
  • SpringMVC

    • SpringMVC 基础
  • SpringBoot

    • SpringBoot 基础
  • Windows 使用技巧
  • 手机相关技巧
  • 最全面的输入法教程
  • 最全面的浏览器教程
  • Office
  • 图片类工具
  • 效率类工具
  • 最全面的 RSS 教程
  • 码字工具
  • 各大平台
  • 校招
  • 五险一金
  • 职场规划
  • 关于离职
  • 杂谈
  • 自媒体
  • 📖 读书

    • 读书工具
    • 走进科学
  • 🌍 英语

    • 从零开始学英语
    • 英语兔的相关视频
    • Larry 想做技术大佬的相关视频
  • 🏛️ 政治

    • 反腐
    • GFW
    • 404 内容
    • 审查与自我审查
    • 互联网
    • 战争
    • 读书笔记
  • 💰 经济

    • 关于税
    • 理财
  • 💪 健身

    • 睡眠
    • 皮肤
    • 口腔健康
    • 学会呼吸
    • 健身日志
  • 🏠 其他

    • 驾驶技能
    • 租房与买房
    • 厨艺
  • 电影

    • 电影推荐
  • 电视剧
  • 漫画

    • 漫画软件
    • 漫画推荐
  • 游戏

    • Steam
    • 三国杀
    • 求生之路
  • 小说
  • 关于本站
  • 关于博主
  • 打赏
  • 网站动态
  • 友人帐
  • 从零开始搭建博客
  • 搭建邮件服务器
  • 本站分享
  • 🌈 生活

    • 2022
    • 2023
    • 2024
    • 2025
  • 📇 文章索引

    • 文章分类
    • 文章归档
  • JavaSE

  • JavaSenior

  • JavaEE

  • JavaWeb

  • Spring

  • 主流框架

    • Redis

    • Mybatis

      • Mybatis 介绍
      • Mybatis 入门案例
      • Mybatis 入门案例-注解
      • Mybatis 入门案例-实现类
      • Mybatis 内部执行原理概述
      • 实现一个微型的 Mybatis-配置文件版
      • 实现一个微型的 Mybatis-注解版
      • Mybatis 实现 CRUD
      • Mybatis 中传递对象参数
      • Mybatis 中的列名和属性名的映射
      • Mybatis 实现 DAO 层的开发
      • Mybatis 实现类的执行过程-查询方法
      • properties 标签的使用及细节
      • typeAliases 标签和 package 标签
      • Mybatis 连接池和事务
      • Mybatis 与 JNDI
      • Mybatis 中的动态 SQL
        • 什么是动态 SQL
        • 实践 if
        • 多个 if
        • where 标签
        • foreach 标签
        • sql 标签
        • 源码
      • Mybatis 多表查询
      • Mybatis 中的多对多查询
      • Mybatis 的延迟加载
      • Mybatis 的缓存
      • Mybatis 的注解开发-CRUD
      • Mybatis 的注解开发-多表查询
    • Lucene

    • Elasticsearch

    • MQ

    • MyCat

    • Lombok

  • SpringMVC

  • SpringBoot

  • Java
  • 主流框架
  • Mybatis
2023-04-25
目录

Mybatis 中的动态 SQL

# 150.Mybatis 中的动态 SQL

Mybatis 的映射文件中,前面我们的 SQL 都是比较简单的,有些时候业务逻辑复杂时,我们的 SQL 是动态变化的,此时在前面的学习中我们的 SQL 就不能满足要求了。 ‍

# 什么是动态 SQL

‍

MyBatis 官网说明 | 动态 SQL (opens new window):动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

使用动态 SQL 并非一件易事,但借助可用于任何 SQL 映射语句中的强大的动态 SQL 语言,MyBatis 显著地提升了这一特性的易用性。

如果你之前用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。 ‍ 举个例子,我们查询的时候,可以允许用户选择多个条件进行查询,也可以不选。例如某购物网站上可以根据订单类型、成交时间等条件,查询订单的数据:

‍

那么在查询的时候,有些参数是有值的,有些又是没有的。此时我们就可以用到动态 SQL,它提供了很多标签,例如 <if>,当有参数则传入,没有则不传入。

# 实践 if

我们在 IUserDao.java,接口里加一个方法:

List<User> findUserByCondition(User user);
1

user 作为查询的条件,该对象里有可能有用户名,有可能有性别,有可能有地址,有可能都有,也可能都没有 ‍ 燃煤我们在 IUserDao.xml 里,配置我们的 SQL 了:

<!-- 根据可有可无的多个参数进行查询-->
<select id="findUserByCondition"  resultType="USER" resultMap="userMap">
    select * from user where 1=1
    <if test="userName != null">
       and username = #{userName}
    </if>
</select>
1
2
3
4
5
6
7

‍ 注意 if 标签里,不能写 && 符号,这是 Java 语言提供的运算符;而我们现在是写 SQL,SQL 里有 and 这个关键字,但是没有&&。

此外,test 属性里的内容,以及 #{} 里的内容,是 Java 代码里的内容,因此区分大小写,得和 User 类中的 userName 成员变量对应起来。

我们写个测试方法:

@Test
public void testFindUserByCondition(){
    User user = new User();
    user.setUserName("王五");

    List<User> users = userDao.findUserByCondition(user);
    for(User u : users){
        System.out.println(u);
    }
}
1
2
3
4
5
6
7
8
9
10

‍ 运行结果:

Preparing: select * from user where 1=1 and username = ?
 - ==> Parameters: 王五(String)
 - <==      Total: 1
User{userId=43, userName='王五', userBirthday=Sun Mar 04 11:34:34 CST 2018, userSex='女', userAddress='北京'}
1
2
3
4

‍

# 多个 if

也可以使用多个条件:

<!-- 根据可有可无的多个参数进行查询-->
<select id="findUserByCondition"  resultType="USER" resultMap="userMap">
    select * from user where 1=1
    <if test="userName != null">
       and username = #{userName}
    </if>
    <if test="userSex != null">
        and sex = #{userSex}
    </if>
</select>
1
2
3
4
5
6
7
8
9
10

‍ 此时我们先不修改测试类,试下运行:

Preparing: select * from user where 1=1 and username = ?
 - ==> Parameters: 王五(String)
 - <==      Total: 1
User{userId=43, userName='王五', userBirthday=Sun Mar 04 11:34:34 CST 2018, userSex='女', userAddress='北京'}
1
2
3
4

由于我们的 userSex 是 null,因此查询的 SQL 里也不会有该条件

而如果我们设置了性别:

@Test
public void testFindUserByCondition(){
    User user = new User();
    user.setUserName("王五");
    user.setUserSex("女");
    List<User> users = userDao.findUserByCondition(user);
    for(User u : users){
        System.out.println(u);
    }
}
1
2
3
4
5
6
7
8
9
10

‍ 那么运行结果里,就可以看到,SQL 里也加上了性别:

- ==>  Preparing: select * from user where 1=1 and username = ? and sex = ?
- ==> Parameters: 王五(String), 女(String)
- <==      Total: 1
User{userId=43, userName='王五', userBirthday=Sun Mar 04 11:34:34 CST 2018, userSex='女', userAddress='北京'}
1
2
3
4

‍

# where 标签

刚刚我们使用了 where 1 = 1 的条件,看起来不够简洁明了;而如果使用 where 标签,可以更简化配置:

<select id="findUserByCondition"  resultType="USER" resultMap="userMap">
    select * from user
    <where>
        <if test="userName != null">
            and username = #{userName}
        </if>
        <if test="userSex != null">
            and sex = #{userSex}
        </if>
    </where>
</select>
1
2
3
4
5
6
7
8
9
10
11

‍ 我们重新运行测试方法,可以看到运行结果没有变化。

# foreach 标签

使用 if,我们可以解决传入一个参数的情况;但有时候,我们需要用到传入多个参数,例如:

select * from user where id in (41, 42, 43);
1

那这种情况该怎么传参呢?

我们这里使用 QueryVo 为例,添加一个 List:

public class QueryVo {
    private User user;
    private List<Integer> ids;
}
1
2
3
4

请读者自行添加 getter 和 setter

我们在接口 IUserDao.java 里添加一个方法:

/**
 * 根据QueryVo提供的id集合,查询用户信息
 * @param vo
 * @return
 */
List<User> findUserInIds(QueryVo vo );
1
2
3
4
5
6

‍ 然后,我们就可以使用 Mybatis 提供的 foreach 标签了:该标签能遍历一个集合

<!-- 根据QueryVo中的id集合,实现查询用户列表 -->
<select id="findUserInIds" resultType="USER" 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="uid" separator=",">
                #{uid}
            </foreach>
        </if>
    </where>
</select>
1
2
3
4
5
6
7
8
9
10
11

collection 属性表示这是一个集合,open 表示开始,close 表示结束,items 就是要填充的内容,分隔符是逗号

然后我们写一个测试方法:

@Test
public void testFindUserInIds(){
    List<Integer> list = new ArrayList<>();
    list.add(41);
    list.add(42);
    list.add(43);

    QueryVo vo = new QueryVo();
    vo.setIds(list);

    List<User> users = userDao.findUserInIds(vo);
    for(User u : users){
        System.out.println(u);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

运行结果:

 - ==>  Preparing: select * from user WHERE id in ( ? , ? , ? )
 - ==> Parameters: 41(Integer), 42(Integer), 43(Integer)
 - <==      Total: 3
1
2
3

可以看到能正常查询

# sql 标签

纵观我们的 IUserDao.xml 文件,几乎每个 select 标签都是以 select * from user 开头的,此时我们可以抽取重复的部分:在 IUserDao.xml 下新建一个 sql 标签,内容写我们希望抽取的内容

<sql id="defaultUser">
    select * from user
</sql>
1
2
3

注意:抽取了代码片段后,里面不能写分号,不然拼接会出错的

然后,我们就可以在 select 标签里引用了:

<!-- 配置查询所有用户,id要写方法名称-->
<select id="findAll" resultMap="userMap">
    <include refid="defaultUser"/>
</select>

<!-- 根据QueryVo中的id集合,实现查询用户列表 -->
<select id="findUserInIds" resultType="USER" resultMap="userMap" parameterType="QueryVo">
    <include refid="defaultUser"/>
    <where>
        <if test="ids != null and ids.size() > 0">
            <foreach collection="ids" open="and id in (" close=")" item="uid" separator=",">
                #{uid}
            </foreach>
        </if>
    </where>
</select>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

‍ 该标签了解即可,用的较少。

# 源码

本文所有代码已上传到了 GitHub (opens new window) 和 Gitee (opens new window) 上,并且创建了分支 demo13,读者可以通过切换分支来查看本文的示例代码。

上次更新: 2025/6/3 09:31:54
Mybatis 与 JNDI
Mybatis 多表查询

← Mybatis 与 JNDI Mybatis 多表查询→

最近更新
01
语雀文档一键下载至本地教程
07-04
02
要成功,就不要低估环境对你的影响
07-03
03
血泪教训:电子设备要定期开机
07-02
更多文章>
Theme by Vdoing | Copyright © 2022-2025 | 粤 ICP 备 2022067627 号 -1 | 粤公网安备 44011302003646 号 | 点击查看十年之约
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式