首页 > 数据库 >Mybatis之动态SQL使用讲解

Mybatis之动态SQL使用讲解

时间:2024-05-13 09:19:16浏览次数:20  
标签:trim name 标签 SQL student 讲解 Mybatis where

目录

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>

标签:trim,name,标签,SQL,student,讲解,Mybatis,where
From: https://www.cnblogs.com/jingzh/p/18188601

相关文章

  • MySQL 死锁案例
    记一次MySQL死锁分析处理过程,聊聊我的思路。前车之鉴,后事之师。以一个例子为切入点一、问题背景某业务模块反馈数据库最近出现过几次死锁告警的情况,本文总结了这次死锁排查的全过程,并分析了导致死锁的原因及解决方案。希望给大家提供一个死锁的排查及解决思路。基础环境:......
  • SQLServer统计监控SQL执行计划突变的方法
    使用动态管理视图(DMVs)来检测SQL执行计划的突变,你需要关注那些能够提供查询执行统计和计划信息的视图。以下是一些可以用于此目的的DMVs以及相应的查询示例:sys.dm_exec_query_stats:这个视图提供了关于SQLServer中查询执行的统计信息,包括CPU时间、总工作时间、执行次数等。SEL......
  • SQLServer如何查询近3分钟最消耗CPU的SQL
    在SQLServer中,要查询近3分钟最消耗CPU的SQL语句,可以使用sys.dm_exec_query_stats动态管理视图结合sys.dm_exec_sql_text函数来获取SQL语句的文本。不过,直接查询近3分钟的数据可能需要一些额外的逻辑来筛选时间范围,因为sys.dm_exec_query_stats并不直接提供时间筛选的功能。一种......
  • MyBatis的一级、二级缓存
    一级缓存基于PerpetualCache的HashMap本地缓存,其存储作用域为Session,当Session进行flush或close之后,该Session中的所有Cache就将清空,默认打开一级缓存。二级缓存基于namespace和mapper的作用域起作用的,不是依赖于SQLsession,默认也是采用PerpetualCache,HashMap存储。需要单独......
  • php centos选择sqlserver的驱动和扩展选择版本的说明
    2023年2月23日13:41:48首先是phpphp扩展驱动数据库的关系官方文档说明:https://learn.microsoft.com/zh-cn/sql/connect/php/step-1-configure-development-environment-for-php-development?view=sql-server-2017https://learn.microsoft.com/zh-cn/sql/connect/php/syste......
  • MyBatis延迟加载
    MyBatis是否支持延迟加载延迟加载的意思是在需要用到数据时才进行加载,不需要用到数据时就不加载数据。MyBatis支持一对一关联对象和一对多关联集合对象的延迟加载。在Mybatis配置文件中,可以配置是否启用延迟加载lazyLoadingEnabled=truel/false,默认是关闭的。或者fetchType=laz......
  • Mysql中的索引下推优化
    当索引下推开启后,如果where子句中的部分条件可以通过索引中的列来进行过滤,MySQL会把这部分条件也下推给存储引擎。这里的关键是下推的这部分条件用于过滤而非定位数据。直接看官方的例子:SELECT*FROMpeopleWHEREzipcode='95054'ANDlastnameLIKE'%etrunia%'ANDad......
  • 初识PostgreSql
    前言PostgreSql常被称为Postgres,简称PG,后文中以PG称呼。是当今非常流行的一种数据库。为什么使用PG在分析这个问题之前,我认为有必要说一说Oracle和MySql这两家数据库。OracleOracle作为占有率最高的数据库,由Oracle公司开发,提供商业支持,其流行的原因在于其强大的性能......
  • MyBatis批处理
    减少了jdbc驱动与数据库服务器之间的网络传输开销使用batch前:执行一条sql就要进行一次网络IO开销,还要等待服务器响应结束后才能提交下一条sql。预编译n次,设置参数n次,执行n次使用batch之后:客户端的多条sql是一起提交给服务器的,一次batch只涉及到一次网络传输开销。预编译一次,设......
  • Pgsql报错修复
     1   这里更改安装目录bin下面例如E:\WorkingSoftware\PostgreSql\16\bin 更改权限,下面都改下  2如果安装时提示databse出错,就初始化下 E:\WorkingSoftware\PostgreSql\16\bin\pg_ctl.exe-DE:\WorkingSoftware\PostgreSql\16\datainitdb ......