Mybatis之动态SQL使用小结(全网最新)

 更新时间:2024年05月13日 10:00:07   作者:上善若泪  
MyBatis令人喜欢的一大特性就是动态SQL, 在使用JDBC的过程中, 根据条件进行SQL的拼接是很麻烦且很容易出错的,MyBatis通过OGNL来进行动态SQL的使用解决了这个麻烦,对Mybatis动态SQL相关知识感兴趣的朋友跟随小编一起看看吧

1 动态SQL

1.1 引言

MyBatis令人喜欢的一大特性就是动态SQL。 在使用JDBC的过程中, 根据条件进行SQL的拼接是很麻烦且很容易出错的。MyBatis动态SQL的出现, 解决了这个麻烦。
MyBatis通过OGNL来进行动态SQL的使用的。
目前, 动态 SQL 支持以下几种标签

元素作用备注
if判断语句单条件分支
choose(when、otherwise)相当于 Java 中的 if else多条件分支
trim(where、set)辅助元素用于处理 SQL 拼接问题
foreach循环语句批量插入, 更新, 查询时经常用到
bind创建一个变量, 并绑定到上下文中用于兼容不同的数据库, 防止 SQL 注入等

1.2 数据准备

为了后面的演示, 创建了一个Maven项目 mybatis-dynamic, 创建了对应的数据库和表

DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
  `student_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '编号',
  `name` varchar(20) DEFAULT NULL COMMENT '姓名',
  `phone` varchar(20) DEFAULT NULL COMMENT '电话',
  `email` varchar(50) DEFAULT NULL COMMENT '邮箱',
  `sex` tinyint(4) DEFAULT NULL COMMENT '性别',
  `locked` tinyint(4) DEFAULT NULL COMMENT '状态(0:正常,1:锁定)',
  `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '存入数据库的时间',
  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改的时间',
  `delete` int(11) DEFAULT NULL,
  PRIMARY KEY (`student_id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='学生表';

对应的项目结构

1.3 if 标签

if 标签是我们最常使用的。在查询、删除、更新的时候很可能会使用到。必须结合test属性联合使用

1.3.1 在 WHERE 条件中使用 if 标签

这是常见的一种现象, 我们在进行按条件查询的时候, 可能会有多种情况。
查询条件
根据输入的学生信息进行条件检索

  • 当只输入用户名时, 使用用户名进行模糊检索;
  • 当只输入性别时, 使用性别进行完全匹配
  • 当用户名和性别都存在时, 用这两个条件进行查询匹配查询

动态SQL

<select id="selectByStudentSelective" resultMap="BaseResultMap" parameterType="com.homejim.mybatis.entity.Student">
    select
    <include refid="Base_Column_List" />
    from student
    where 1=1
    <if test="name != null and name !=''">
      and name like concat('%', #{name}, '%')
    </if>
    <if test="sex != null">
      and sex=#{sex}
    </if>
  </select>

在此 SQL 语句中, where 1=1 是多条件拼接时的小技巧, 后面的条件查询就可以都用 and 了。
同时, 我们添加了 if 标签来处理动态 SQL

<if test="name != null and name !=''">
      and name like concat('%', #{name}, '%')
    </if>
    <if test="sex != null">
      and sex=#{sex}
    </if>

此 if 标签的test属性值是一个符合OGNL的表达式, 表达式可以是truefalse。如果表达式返回的是数值, 则0false,非 0true

1.3.2 在 UPDATE 更新列中使用 if 标签

有时候我们不希望更新所有的字段, 只更新有变化的字段。

示例SQL

<update id="updateByPrimaryKeySelective" parameterType="com.homejim.mybatis.entity.Student">
    update student
    <set>
      <if test="name != null">
        `name` = #{name,jdbcType=VARCHAR},
      </if>
      <if test="phone != null">
        phone = #{phone,jdbcType=VARCHAR},
      </if>
      <if test="email != null">
        email = #{email,jdbcType=VARCHAR},
      </if>
      <if test="sex != null">
        sex = #{sex,jdbcType=TINYINT},
      </if>
      <if test="locked != null">
        locked = #{locked,jdbcType=TINYINT},
      </if>
      <if test="createTime != null">
        create_time= #{createTime ,jdbcType=TIMESTAMP},
      </if>
      <if test="updateTime != null">
        update_time = #{updateTime ,jdbcType=TIMESTAMP},
      </if>
    </set>
    where student_id = #{studentId,jdbcType=INTEGER}

1.3.3 在 INSERT 动态插入中使用 if 标签

我们插入数据库中的一条记录, 不是每一个字段都有值的, 而是动态变化的。在这时候使用 if 标签, 可帮我们解决这个问题。

如果只有非空属性才插入则对应的SQL

<insert id="insertSelective" parameterType="com.homejim.mybatis.entity.Student">
    insert into student
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="studentId != null">
        student_id,
      </if>
      <if test="name != null">
        `name`,
      </if>
      <if test="phone != null">
        phone,
      </if>
      <if test="email != null">
        email,
      </if>
      <if test="sex != null">
        sex,
      </if>
      <if test="locked != null">
        locked,
      </if>
      <if test="createTime!= null">
        create_time,
      </if>
      <if test="updateTime != null">
        update_time,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="studentId != null">
        #{studentId,jdbcType=INTEGER},
      </if>
      <if test="name != null">
        #{name,jdbcType=VARCHAR},
      </if>
      <if test="phone != null">
        #{phone,jdbcType=VARCHAR},
      </if>
      <if test="email != null">
        #{email,jdbcType=VARCHAR},
      </if>
      <if test="sex != null">
        #{sex,jdbcType=TINYINT},
      </if>
      <if test="locked != null">
        #{locked,jdbcType=TINYINT},
      </if>
      <if test="createTime != null">
        #{createTime,jdbcType=TIMESTAMP},
      </if>
      <if test="updateTime != null">
        #{updateTime,jdbcType=TIMESTAMP},
      </if>
    </trim>
  </insert>

1.4 choose 标签

choose when otherwise标签可以帮我们实现if else的逻辑。
一个choose标签至少有一个when, 最多一个otherwise

假如查询条件:

  • 假设 name 具有唯一性, 查询一个学生
  • 当 studen_id 有值时, 使用 studen_id 进行查询;
  • 当 studen_id 没有值时, 使用 name 进行查询;
  • 否则返回空

动态SQL

<select id="selectByIdOrName" resultMap="BaseResultMap" parameterType="com.homejim.mybatis.entity.Student">
    select
    <include refid="Base_Column_List" />
    from student
    where 1=1
    <choose>
      <when test="studentId != null">
        and student_id=#{studentId}
      </when>
      <when test="name != null and name != ''">
        and name=#{name}
      </when>
      <otherwise>
        and 1=2
      </otherwise>
    </choose>
  </select>

1.5 trim(set、where)

这三个其实解决的是类似的问题。如我们在写前面的[在WHERE条件中使用if 标签 SQL的时候, where 1=1 这个条件我们是不希望存在的。

1.5.1 where

查询条件:

  • 根据输入的学生信息进行条件检索
  • 当只输入用户名时, 使用用户名进行模糊检索;
  • 当只输入性别时, 使用性别进行完全匹配
  • 当用户名和性别都存在时, 用这两个条件进行查询匹配查询
    不使用 where 1=1

动态 SQL

 <select id="selectByStudentSelectiveWhereTag" resultMap="BaseResultMap" parameterType="com.homejim.mybatis.entity.Student">
    select
    <include refid="Base_Column_List" />
    from student
   <where>
    <if test="name != null and name !=''">
      and name like concat('%', #{name}, '%')
    </if>
    <if test="sex != null">
      and sex=#{sex}
    </if>
   </where>
  </select>

1.5.2 set

set标签也类似, 在UPDATE更新列中使用if 标签中, 如果我们的方法 updateByPrimaryKeySelective 没有使用 标签, 那么我们就要想办法处理字段全为空的条件, 字段不为空的条件等。有了这个, 我们只需要写 if 标签即可, 不需要处理类似的问题。

1.5.3 trim

setwhere其实都是trim标签的一种类型, 该两种功能都可以使用trim标签进行实现。

1.5.3.1 trim 来表示 where

where标签, 我们也可以写成

<trim prefix="where" prefixOverrides="AND |OR">
</trim>

表示当trim中含有内容时, 添加where, 且第一个为andor时, 会将其去掉。而如果没有内容, 则不添加where

1.5.3.2 trim 来表示 set

相应的,set标签可以如下表示

<trim prefix="set" suffixOverrides=",">
</trim>

表示当trim中含有内容时, 添加set, 且最后的内容为,时, 会将其去掉。而没有内容, 不添加 set

1.5.3.3 trim 的几个属性

prefix:当trim元素包含有内容时, 增加prefix所指定的前缀
prefixOverrides:当trim元素包含有内容时, 去除prefixOverrides指定的 前缀

  • suffix:当trim元素包含有内容时, 增加suffix所指定的后缀
  • suffixOverrides:当trim元素包含有内容时, 去除suffixOverrides指定的后缀

1.6 foreach 标签

foreach标签可以对数组,Map或实现Iterable接口。
foreach中有以下几个属性:

  • collection:必填,集合/数组/Map的名称
  • item:变量名。即从迭代的对象中取出的每一个值
  • index:索引的属性名。当迭代的对象为 Map 时, 该值为 Map 中的 Key
  • open:循环开头的字符串
  • close:循环结束的字符串
  • separator:每次循环的分隔符

其他的比较好理解, collection 中的值应该怎么设定呢?
跟接口方法中的参数相关。只有一个数组参数或集合参数默认情况:集合collection=list, 数组是collection=array

使用@Param来指定参数的名称, 如我们在参数前@Param("ids"), 则就填写collection=ids如果参数是Map,指定为Map中的对应的Key即可。其实上面的 @Param 最后也是转化为 Map 的。
如果参数是对象使用属性.属性即可。

1.6.1 在 where 中使用 foreach

在 where条件中使用, 如按id集合查询, 按id集合删除等。

查询条件,我们希望查询用户 id 集合中的所有用户信息。
对应 SQL

<select id="selectByStudentIdList" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from student
    where student_id in
    <foreach collection="list" item="id" open="(" close=")" separator="," index="i">
      #{id}
    </foreach>
  </select>

1.6.2 foreach 实现批量插入

可以通过foreach来实现批量插入
动态SQL

 <insert id="insertList">
    insert into student(name, phone, email, sex, locked)
    values
    <foreach collection="list" item="student" separator=",">
      (
      #{student.name}, #{student.phone},#{student.email},
      #{student.sex},#{student.locked}
      )
    </foreach>
  </insert>

1.7 bind 标签

bind标签是通过OGNL表达式去定义一个上下文的变量, 这样方便我们使用。
如在selectByStudentSelective方法中, 有如下

<if test="name != null and name !=''">
      and name like concat('%', #{name}, '%')
</if>

在 MySQL 中, 该函数支持多参数, 但在 Oracle 中只支持两个参数。那么我们可以使用 bind 来让该 SQL 达到支持两个数据库的作用

<if test="name != null and name !=''">
     <bind name="nameLike" value="'%'+name+'%'"/>
     and name like #{nameLike}
</if>

到此这篇关于Mybatis之动态SQL使用讲解的文章就介绍到这了,更多相关Mybatis动态SQL内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot中YAML配置文件异常:ArrayIndexOutOfBoundsException: -1的解决方法

    SpringBoot中YAML配置文件异常:ArrayIndexOutOfBoundsException: -

    本文详细探讨了在SpringBoot应用中使用YAML配置文件时遇到的ArrayIndexOutOfBoundsException异常,揭示了问题的根本原因在于SnakeYAML解析器在处理隐式和显式多文档结构时的边界情况bug,通过分析问题现象、背景知识、解决方案及最佳实践,需要的朋友可以参考下
    2025-12-12
  • Spring Security实现身份认证和授权的示例代码

    Spring Security实现身份认证和授权的示例代码

    在 Spring Boot 应用中使用 Spring Security 可以非常方便地实现用户身份认证和授权,本文主要介绍了Spring Security实现身份认证和授权的示例代码,感兴趣的可以了解一下
    2023-06-06
  • Java SerialVersionUID作用详解

    Java SerialVersionUID作用详解

    这篇文章主要介绍了Java SerialVersionUID作用详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • 从java面试题了解你所模糊的数组

    从java面试题了解你所模糊的数组

    这篇文章主要介绍了从java面试题了解你所模糊的数组,数组用来存储一系列的数据项,其中的每一项具有相同的基本数据类型、类或相同的父类。通过使用数组,可以在很大程度上缩短和简化程序代码,从而提高应用程序的效率。,需要的朋友可以参考下
    2019-06-06
  • IntelliJ IDEA中进行背景设置过程

    IntelliJ IDEA中进行背景设置过程

    本文介绍了IntelliJ IDEA中主题切换、代码编辑区护眼色设置、背景图片设置的方法及注意事项,推荐使用深色主题+豆沙绿编辑区组合,兼顾美观与护眼,设置适用于Windows/macOS/Linux平台
    2026-05-05
  • Spring事件监听器ApplicationListener的使用与原理分析

    Spring事件监听器ApplicationListener的使用与原理分析

    文章介绍了Spring中的事件监听器和事件发布机制,包括内置事件的使用和自定义事件的创建与发布,以及如何使用@EventListener注解进行事件监听,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧
    2025-11-11
  • idea推送项目到gitee中的创建方法

    idea推送项目到gitee中的创建方法

    这篇文章主要介绍了idea推送项目到gitee中的创建方法,本文通过图文实例相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-08-08
  • spring boot基于注解的声明式事务配置详解

    spring boot基于注解的声明式事务配置详解

    这篇文章主要介绍了spring boot基于注解的声明式事务配置详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • Java WebService 简单实例(附实例代码)

    Java WebService 简单实例(附实例代码)

    本篇文章主要介绍了Java WebService 简单实例(附实例代码), Web Service 是一种新的web应用程序分支,他们是自包含、自描述、模块化的应用,可以发布、定位、通过web调用。有兴趣的可以了解一下
    2017-01-01
  • Mybatis如何实现@Select等注解动态组合SQL语句

    Mybatis如何实现@Select等注解动态组合SQL语句

    这篇文章主要介绍了Mybatis如何实现@Select等注解动态组合SQL语句,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07

最新评论