MyBatis 动态 SQL 详解:灵活构建强大查询

MyBatis 的动态 SQL 功能是其最强大的特性之一,它允许开发者根据不同条件动态生成 SQL 语句,极大地提高了 SQL 的灵活性和复用性。本文将深入探讨 MyBatis 的动态 SQL 功能,包括 OGNL 表达式的使用以及各种动态 SQL 元素(如 if、choose、when、foreach 等)的应用场景和示例。

1.动态 SQL 概述

动态 SQL 是 MyBatis 的核心特性之一,它允许在 XML 映射文件或注解中定义灵活的 SQL 语句,根据运行时条件动态生成最终执行的 SQL。常见的应用场景包括:

  • 根据不同条件构建 WHERE 子句
  • 动态插入或更新字段
  • 处理集合参数,实现批量操作
  • 构建复杂的查询条件组合

动态 SQL 的核心是通过 OGNL(对象图导航语言)表达式来评估条件,并结合各种动态元素来生成 SQL。

2.OGNL 表达式基础

OGNL(Object Graph Navigation Language)是一种强大的表达式语言,MyBatis 使用它来解析动态 SQL 中的条件表达式。在 MyBatis 中,OGNL 表达式主要用于:

  • 访问对象属性:user.username
  • 调用方法:list.size()
  • 执行逻辑运算:age > 18 && gender == 'M'
  • 判断集合是否包含元素:list.contains('value')

示例

<if test="username != null and username != ''">
    AND username = #{username}
</if>

这里的 test 属性值就是一个 OGNL 表达式,用于判断 username 是否不为空。

3.动态 SQL 元素详解

3.1 <if> 元素

<if> 元素是最基本的动态 SQL 元素,用于条件判断。

示例

<select id="findUser" parameterType="map" resultType="User">
    SELECT * FROM users
    WHERE 1=1<if test="username != null and username != ''">
        AND username = #{username}</if><if test="age != null and age > 0">
        AND age > #{age}</if>
</select>

这个查询会根据传入的参数动态添加条件。如果 username 不为空,则添加 username 条件;如果 age 不为空且大于 0,则添加 age 条件。

3.2 <choose>、<when>、<otherwise> 元素

<choose> 元素类似于 Java 中的 switch 语句,用于多条件选择。

示例

<select id="findUser" parameterType="map" resultType="User">
    SELECT * FROM users
    WHERE 1=1<choose><when test="username != null and username != ''">
            AND username = #{username}</when><when test="email != null and email != ''">
            AND email = #{email}</when><otherwise>
            AND age > 18</otherwise></choose>
</select>

这个查询会依次检查条件,一旦某个 <when> 条件满足,就会使用对应的 SQL 片段,其他条件将被忽略。如果所有 <when> 条件都不满足,则使用 <otherwise> 中的 SQL 片段。

3.3 <where> 元素

<where> 元素用于简化 SQL 语句中的 WHERE 子句,它会自动处理 AND 和 OR 前缀。

示例

<select id="findUser" parameterType="map" resultType="User">
    SELECT * FROM users<where><if test="username != null and username != ''">
            username = #{username}</if><if test="age != null and age > 0">
            AND age > #{age}</if></where>
</select>

如果第一个条件成立,<where> 元素会自动添加 WHERE 关键字;如果后面的条件以 AND 或 OR 开头,<where> 元素会自动去除这些前缀,避免 SQL 语法错误。

3.4 <set> 元素

<set> 元素用于动态更新语句,它会自动处理逗号。

示例

<update id="updateUser" parameterType="User">
    UPDATE users<set><if test="username != null and username != ''">
            username = #{username},</if><if test="email != null and email != ''">
            email = #{email},</if><if test="age != null">
            age = #{age}</if></set>
    WHERE id = #{id}
</update>

<set> 元素会自动添加 SET 关键字,并去除最后一个条件后的逗号,确保 SQL 语法正确。

3.5 <foreach> 元素

<foreach> 元素用于遍历集合,常用于 IN 条件或批量操作。

属性说明

  • collection:要遍历的集合,如 List、Array 或 Map。
  • item:集合中的元素。
  • index:索引,对于 List 和 Array 是位置索引,对于 Map 是键。
  • open:开始符号,如 (。
  • close:结束符号,如 )。
  • separator:分隔符,如 ,。

示例 1:IN 条件

<select id="findUsersByIds" parameterType="list" resultType="User">
    SELECT * FROM users
    WHERE id IN<foreach item="id" collection="list" open="(" separator="," close=")">
        #{id}</foreach>
</select>

示例 2:批量插入

<insert id="insertUsers" parameterType="list">
    INSERT INTO users (username, email, age)
    VALUES<foreach item="user" collection="list" separator=",">
        (#{user.username}, #{user.email}, #{user.age})</foreach>
</insert>

3.6 <trim> 元素

<trim> 元素是一个通用的格式化元素,可以用来定制 <where> 和 <set> 元素的功能。

属性说明

  • prefix:添加前缀。
  • prefixOverrides:去除前缀。
  • suffix:添加后缀。
  • suffixOverrides:去除后缀。

替代 <where> 元素

<trim prefix="WHERE" prefixOverrides="AND |OR ">
    ...
</trim>

替代 <set> 元素

<trim prefix="SET" suffixOverrides=",">
    ...
</trim>

3.7 <sql> 和 <include> 元素

<sql> 元素用于定义可重用的 SQL 片段,<include> 元素用于引用这些片段。

示例

<sql id="userColumns">
    id, username, email, age
</sql><select id="findUser" parameterType="int" resultType="User">
    SELECT <include refid="userColumns"/>
    FROM users
    WHERE id = #{id}
</select>

4.动态 SQL 工作流程

下面是一个动态 SQL 执行的流程图,展示了 MyBatis 如何处理动态 SQL:

SQL 执行请求
    |
    v
获取映射文件中的 SQL 模板
    |
    v
解析动态 SQL 元素和 OGNL 表达式
    |
    v
根据条件生成最终 SQL 语句
    |
    v
参数处理和类型转换
    |
    v
执行最终生成的 SQL 语句
    |
    v
返回结果

5.综合示例

下面是一个综合示例,展示如何使用多种动态 SQL 元素构建复杂查询:

<mapper namespace="com.example.mapper.UserMapper"><!-- 定义可重用的列 --><sql id="userColumns">
        id, username, email, age, gender</sql><!-- 复杂查询示例 --><select id="searchUsers" parameterType="map" resultType="User">
        SELECT <include refid="userColumns"/>
        FROM users<where><choose><when test="keyword != null and keyword != ''">
                    (username LIKE CONCAT('%', #{keyword}, '%')
                    OR email LIKE CONCAT('%', #{keyword}, '%'))</when><otherwise>
                    1=1</otherwise></choose><if test="ageRange != null and ageRange.size() == 2">
                AND age BETWEEN #{ageRange[0]} AND #{ageRange[1]}</if><if test="genders != null and genders.size() > 0">
                AND gender IN<foreach item="gender" collection="genders" open="(" separator="," close=")">
                    #{gender}</foreach></if></where><choose><when test="sortField != null and sortField != ''">
                ORDER BY ${sortField}<if test="sortOrder != null and sortOrder != ''">
                    ${sortOrder}</if></when><otherwise>
                ORDER BY id DESC</otherwise></choose><if test="offset != null and limit != null">
            LIMIT #{offset}, #{limit}</if></select><!-- 动态更新示例 --><update id="updateUser" parameterType="User">
        UPDATE users<set><if test="username != null and username != ''">
                username = #{username},</if><if test="email != null and email != ''">
                email = #{email},</if><if test="age != null">
                age = #{age},</if><if test="gender != null and gender != ''">
                gender = #{gender}</if></set>
        WHERE id = #{id}</update><!-- 批量插入示例 --><insert id="batchInsert" parameterType="list">
        INSERT INTO users (username, email, age, gender)
        VALUES<foreach item="user" collection="list" separator=",">
            (#{user.username}, #{user.email}, #{user.age}, #{user.gender})</foreach></insert>
</mapper>

6.动态 SQL 最佳实践

1. 保持表达式简洁:避免在 OGNL 表达式中编写复杂的逻辑,保持表达式简单易懂。

2. 合理使用 <where> 和 <set>:它们可以自动处理 SQL 语法问题,减少错误。

3. 使用 <sql> 和 <include> 提高复用性:将常用的 SQL 片段提取出来,便于维护。

4. 谨慎使用 ${}:${} 会直接替换参数,存在 SQL 注入风险,应尽量使用 #{}。

5. 避免过度复杂的动态 SQL:如果动态 SQL 过于复杂,考虑拆分成多个简单的 SQL 语句。

6. 测试动态 SQL:由于动态 SQL 的灵活性,建议编写单元测试确保各种条件下生成的 SQL 正确。

7.总结

MyBatis 的动态 SQL 功能通过 OGNL 表达式和各种动态元素,为开发者提供了强大而灵活的 SQL 构建能力。无论是简单的条件查询,还是复杂的批量操作,动态 SQL 都能轻松应对。通过合理使用动态 SQL,可以提高 SQL 的复用性和可维护性,减少重复代码,使数据库操作更加高效。

在实际开发中,需要根据业务需求选择合适的动态 SQL 元素,遵循最佳实践,避免陷入过度复杂的动态 SQL 陷阱。掌握动态 SQL 的使用,是成为一名高效的 MyBatis 开发者的关键一步。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/news/907289.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式自学第三十天(5.28)

&#xff08;1&#xff09;多线程资源竞争问题&#xff1a; 互斥&#xff1a;在多线程中对临界资源的排他性访问。 解决方案&#xff1a;互斥锁 mutex互斥锁在进程pcb块&#xff0c;ret 为0说明别人在用&#xff0c;1说明空闲。 阻塞锁 man pthread_mutex_init man pthread_…

【HW系列】—web常规漏洞(SQL注入与XSS)

SQL注入与XSS攻防解析&#xff08;安全防御指南&#xff09; 一、SQL注入基础&#xff08;防御视角&#xff09; ​​1. 简介​​ SQL注入是一种通过构造非预期SQL语句操纵数据库的攻击技术。作为开发者&#xff0c;需重点关注输入验证与查询安全&#xff0c;建立全流量监测…

Accelerate 2025北亚巡展正式启航!AI智御全球·引领安全新时代

近日&#xff0c;网络安全行业年度盛会Accelerate 2025北亚巡展正式在深圳启航&#xff01;智库专家、产业领袖及Fortinet高管、产品技术团队和300余位行业客户齐聚一堂&#xff0c;围绕“AI智御全球引领安全新时代”主题&#xff0c;共同探讨AI时代网络安全新范式。大会聚焦三…

RAG系统构建之嵌入模型性能优化完整指南

导读&#xff1a;在企业级RAG系统的实际部署中&#xff0c;您是否遇到过这样的困扰&#xff1a;嵌入计算成本不断攀升&#xff0c;API调用频繁触及限制&#xff0c;而系统响应速度却始终达不到用户期望&#xff1f;这些看似分散的问题&#xff0c;实际上都指向同一个技术核心&a…

python 自动生成不同行高的word

python 自动生成不同行高的word # -*- coding: utf-8 -*- from docx import Document from docx.shared import Cm, Pt, Inches from docx.oxml import OxmlElement from docx.oxml.ns import qn from docx.enum.text import WD_ALIGN_PARAGRAPHclass DynamicTableGenerator:d…

如何训练意志力

设定清晰的目标 目标需要是具体的&#xff0c;可实现的&#xff0c;有时间限制的。比如不要说“我要锻炼”&#xff0c;而是改成“每周跑步3次&#xff0c;每次30分钟”。 从小事开始 起步通常都是困难的&#xff0c;一开始定一个很大很复杂的任务也超出了自己的能力&#x…

FastAPI 依赖注入

依赖注入常用于以下场景&#xff1a; 共享业务逻辑&#xff08;复用相同的代码逻辑&#xff09; 共享数据库连接 实现安全、验证、角色权限 等…… 上述场景均可以使用依赖注入&#xff0c;将代码重复最小化。 创建依赖项 依赖项就是一个函数&#xff0c;且可以使用与路…

接口幂等性原理与方案总结

文章目录 接口幂等概念典型场景核心解决方案一锁二判三更新 方案选型对比 接口幂等概念 定义&#xff1a;无论调用接口多少次&#xff0c;对系统的影响与单次调用一样 范畴&#xff1a;在后端开发中&#xff0c;通常更关注写接口的幂等&#xff0c;因为写接口才会对系统数据造…

【已解决】windows gitbash 出现CondaError: Run ‘conda init‘ before ‘conda activate‘

在 Git Bash 中执行&#xff1a; source /c/Users/你的用户名/miniconda3/etc/profile.d/conda.sh # 注意填入你自己的路径 conda init bash关闭并重新打开 Git Bash 终端。测试激活环境&#xff1a; conda activate your_env_name注意事项 要把上述命令中的 你的用户名 替…

软件包管理系统的架构与生态机制

文章目录 前言一、总结二、如何上传自己的软件包 前言 在日常软件开发中&#xff0c;我们经常使用诸如apt install, pip install, npm install之类的命令&#xff0c;但有一个问题是&#xff0c;这些下载命令是从哪里下载的这些软件包&#xff0c;以及我们是否能上传自己的代码…

Java线程池管理最佳实践(设计模式)

引言 在多线程编程中&#xff0c;线程池是一种非常重要的资源管理工具。合理使用线程池可以显著提高系统性能&#xff0c;避免频繁创建和销毁线程带来的开销。今天&#xff0c;我将为大家深入分析一个实用的ThreadPoolManager实现&#xff0c;它来自com.kingdee.eas.util包&am…

4.8.2 利用Spark SQL计算总分与平均分

在本次实战中&#xff0c;我们的目标是利用Spark SQL计算学生的总分与平均分。首先&#xff0c;我们准备了包含学生成绩的数据文件&#xff0c;并将其上传至HDFS。接着&#xff0c;通过Spark的交互式编程环境&#xff0c;我们读取了成绩文件并将其转换为结构化的DataFrame。然后…

HTML 文件路径完全指南:相对路径、绝对路径解析与引用技巧

一、为什么必须学会文件路径&#xff1f;—— 网页引用资源的 “地址规则” 在 HTML 中&#xff0c;引用图片、CSS、JS 等外部文件时&#xff0c;必须通过文件路径告诉浏览器资源的位置。路径错误会导致资源无法加载&#xff08;页面出现 broken image 图标或样式丢失&#xf…

keepalived两台设备同时出现VIP问题

目录 问题背景&#xff1a; 日志分析如下&#xff1a; 原因和解决方案总结&#xff1a; 问题背景&#xff1a; keepalived-master和keepalived-slave同时出现了VIP&#xff0c;出现了非对称路由和双主现象 日志分析如下&#xff1a; master能够接受到来自slave的通告消息…

【开源解析】基于PyQt5+Folium的谷歌地图应用开发:从入门到实战

&#x1f310;【开源解析】基于PyQt5Folium的谷歌地图应用开发&#xff1a;从入门到实战 &#x1f308; 个人主页&#xff1a;创客白泽 - CSDN博客 &#x1f525; 系列专栏&#xff1a;&#x1f40d;《Python开源项目实战》 &#x1f4a1; 热爱不止于代码&#xff0c;热情源自每…

篇章五 数据结构——链表(一)

目录 1.ArrayList的缺陷 2. 链表 2.1 链表的概念及结构 2.2 链表结构 1. 单向或者双向 2.带头或者不带头 3.循环或者非循环 2.3 链表的实现 1.完整代码 2.图解 3.显示方法 4.链表大小 5. 链表是否存在 key 值 6.头插法 7.尾插法 8.中间插入 9.删除key值节点 10.…

数据库相关面试

数据库相关面试 Mysql MySQL中的事务隔离级别及其特点 参考&#xff1a;事务的隔离级别&#xff1a;可重复读 未提交读(Read Uncommitted)&#xff1a;允许脏读&#xff0c;也就是可能读取到其他会话中未提交事务修改的数据 已提交读(Read Committed)&#xff1a;只能读取到…

基于Scrapy的天猫商品数据爬取与分析实战(含API签名破解与可视化)

基于Scrapy的天猫商品数据爬取与分析实战&#xff08;含API签名破解与可视化&#xff09; 本文以华为Mate 60 Pro为例&#xff0c;详细介绍如何使用Scrapy框架爬取天猫商品数据&#xff0c;涵盖API签名破解、反爬应对、数据存储及可视化全流程&#xff0c;适合爬虫进阶学习者实…

【C++进阶篇】哈希表的模拟实现(赋源码)

这里写目录标题 前言一. 开放地址法实现哈希表1.1 闭散列结构定义1.2 构造函数1.3 插入&#xff08;线性探测&#xff09;1.3.1 传统写法1.3.2 现代写法 1.4 查找1.5 删除 二. 链地址法实现哈希表&#xff08;哈希桶&#xff09;2.1 开散列结构定义2.2 构造函数2.3 插入2.4 查找…

07-后端Web实战(部门管理)

5. 修改部门 对于任何业务的修改功能来说&#xff0c;一般都会分为两步进行&#xff1a;查询回显、修改数据。 5.1 查询回显 5.1.1 需求 当我们点击 "编辑" 的时候&#xff0c;需要根据ID查询部门数据&#xff0c;然后用于页面回显展示。 5.1.2 接口描述 参照参照…