Mybatis中的@Param及动态SQL详解

 更新时间:2023年10月08日 08:54:40   作者:童话ing  
这篇文章主要介绍了Mybatis中的@Param及动态SQL详解,@Param是MyBatis所提供的作为Dao层的注解,作用是用于传递参数,从而可以与SQL中的的字段名相对应,需要的朋友可以参考下

一、@Param注解

@Param 是MyBatis所提供的作为Dao层的注解,作用是用于传递参数,从而可以与SQL中的的字段名相对应。

首先需要明确一下 @Param 和 @RequestParam 虽然看起来有那么一点相似,但其实是没有任何关系的,就像 鱼香茄子 和 鱼 一样。

注解来源
@Paramorg.apache.ibatis.annotations.Param
@RequestParamorg.springframework.web.bind.annotation.RequestParam

@RequestParam作用于Controller层,作用是为获取前端参数,解决的是前后端参数不一致的问题。@Param作用于Dao层,是为了传递多个参数,解决的是可读性和直观性;所以它们没有关系!

我们简单看一下使用:

List<PreEndBlogListVO> selectBlogListByTypeId(@Param("typeId") Integer id);

上述 @Param 作用其实就是将 id 重命名为 typeId 。因此,我们可以在xml中这样使用:

  <select id="selectBlogListByTypeId" resultMap="preEndBlogListMap">
       select id,title,description from blog where type_id=#{typeId,jdbcType=INTEGER}
  </select>

并且当参数来自于多个不同POJO时候,使用@Param后,Mybatis会自动检索参数类型,也就是不用再在xml中配置 parameterType 属性了。

public List<Role> findRoleByMix(@Param("roleP") RoleParam role, @Param("permissionP") PermissionParam permission);

xml配置:

<select id="findRoleByMix" resultType="role">
    SELECT id,name FROM t_role
    WHERE roleName=#{roleP.roleName}
    AND note=#{rolep.note}
    AND level=#{permissionP.level}
<select>

二、Mybatis动态SQL

以前在使用JDBC操作数据时,如果查询条件特别多,将条件串联成SQL字符串是一件痛苦的事情。通常的解决方法是写很多的if-else条件语句对字符串进行拼接,并确保不能忘了空格或在字段的最后省略逗号。MyBatis使用动态SQL来改善这种情形,动态SQL是基于OGNL的表达式,可方便我们在SQL语句中实现某些逻辑,简单说主要是为了解决参数不固定情况。用于实现动态SQL的元素如下。

  • if:利用if实现简单的条件选择
  • choose(when,otherwise):相当于Java中的switch语句,通常与when和otherwise搭配使用
  • set:解决动态更新语句
  • trim:可以灵活的去除多余的关键字
  • foreach:迭代一个集合,通常用于in条件

set 标签

使用set标签可以将动态的配置 SET 关键字,并剔除追加到条件末尾的任何不相关的逗号。使用 if+set 标签修改后,在进行表单更新的操作中,哪个字段中有值才去更新,如果某项为 null 则不进行更新,而是保持数据库原值。

<update id="updateSet" parameterType="com.dl.POJO.User">
     update user
     <set>
         <if test="name != null and name != ''">
             name = #{name},
         </if>
         <if test="county != null and county != ''">
             county = #{county},
         </if>
     </set>
     where id = #{id}
 </update>

大多数标签我们都是结合在一起使用的,常见主要是为了解决参数不固定情况。

2.1 if 和 where 标签

<sql id="params">
  id, name,country,sex,birth,tel
</sql>
<select id="selectByConditions"  resultType="com.dl.POJO.User">
  select <include refid="params"/> from user
  <where>
      <if test="id != null and id != 0">
          AND id = #{id}
      </if>
      <if test="name != null and name != ''">
          AND name = #{name}
      </if>
      <if test="county != null and county != ''">
          AND county = #{county}
      </if>
  </where>
</select>

<include refid="params"/>其实就是你需要查询的sql字段,用一个sql标签封装起来,以后直接引用即可。

一般开发列表业务的查询条件时,参数通常不固定,如果有多个查询条件,通常会使用<where> <if>标签来进行控制。

where标签会自动标识其标签内是否有返回值,若有,就插入一个where。 <where>标签可以

自动的将第一个满足的条件前面的逻辑运算符 (or ,and) 去掉,什么意思呢,我们简单看一下:

假设此时我们传入的参数为:id=0 name="童话" country="中国"

观察传入的参数,id传入0,此条件不会执行。name传入一个合法值,此时name条件成立,但是前面多了一个and关键字,此时<where>标签会自动去掉这个and关键字。

当然,有的人可能会说,后面直接给个where 1=1不就可以不用<where>标签了么。如下所示:

<select id="findActiveBlogLike" resultType="Blog">
    select * from blog
    where 1=1
   <if test="state != null">
     and state = #{state}
   </if> 
</select>

当然,这样肯定是能实现你所想的功能的做法,但这个东西也会带来一个巨坑,但是这种做法有一个最大的弊端,如果表有索引的话,会导致数据表上的索引失效。这样查询得效率显然就会很低下,就像 ${} 和 #{} 多数情况下可以通用,但是${}就是不能防止SQL注入问题。因此我们还是采用 <where> 标签比较舒适。

如果 where 元素不能满足你的要求,你也可以通过自定义 trim 元素来定制 where 元素的功能。

2.2 trim和if标签

mybatis的trim标签一般用于去除sql语句中多余的and关键字,逗号,或者给sql语句前拼接 where 、 set 以及 values( 等前缀,或者添加“)“等后缀,可用于选择性插入、更新、删除或者条件查询等操作。

主要有4个属性:

属性描述
prefix给sql语句拼接的前缀
suffix给sql语句拼接的后缀
prefixOverrides去除sql语句前面的关键字或者字符,该关键字或者字符由prefixOverrides属性指定,假设该属性指定为"AND",当sql语句的开头为"AND",trim标签将会去除该"AND"
suffixOverrides去除sql语句后面的关键字或者字符,该关键字或者字符由suffixOverrides属性指定

举个栗子,和 where 元素等价的自定义 trim 元素为:

<select id="getUserList" resultType="com.dl.POJO.User">
  select <include refid="params"/> from user
  <trim prefix="where" prefixOverrides="and | or" suffix="order by id">
      <if test="id != null and id != 0">
          AND id = #{id}
      </if>
      <if test="name != null and name != ''">
          AND name = #{name}
      </if>
      <if test="county != null and county != ''">
          AND county = #{county}
      </if>
  </trim>
</select>

上面我们多添加了一个 suffix="order by id" 来实现按照某种规则排序,下面再看一个稍微复杂一点的栗子。

  <insert id="insertSelective" parameterType="com.dl.blog.pojo.Blog">
    insert into blog
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      </if>
      <if test="title != null">
        title,
      </if>
      <if test="views != null">
        views,
      </if>
      <if test="createTime != null">
        create_time,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=INTEGER},
      </if>
      <if test="title != null">
        #{title,jdbcType=VARCHAR},
      </if>
      <if test="views != null">
        #{views,jdbcType=INTEGER},
      </if>
      <if test="createTime != null">
        #{createTime,jdbcType=TIMESTAMP},
      </if>
    </trim>
  </insert>

其中最重要的就是 suffixOverrides="," ,表示去除sql语句结尾多余的逗号。 假设上述条件只匹配上前三个,可以看到我们的sql将变成

insert into blog(id,title,views,) values(1,"标题",22,)

此时插入将会失败。使用trim标签就可以轻松解决此问题。

2.3 foreach 标签

foreach标签主要有以下参数:

属性描述
item循环体中的具体对象。支持属性的点路径访问,如item.age,item.info.details,在list和数组中是其中的对象,在map中是value。
index在list和数组中,index是元素的序号,在map中,index是元素的key,该参数可选。
open表示该语句以什么开始
close表示该语句以什么结束
separator表示元素之间的分隔符,例如在in()的时候,separator=","会自动在元素中间用“,“隔开,避免手动输入逗号导致sql错误,如in(1,2,)这样。该参数可选。

栗子1:进行list批量插入

<insert id="dynamicSqlInsertList" useGeneratedKeys="true" keyProperty="id" parameterType="java.util.List">
    insert into users (name, age, county, date)
    values
    <foreach collection="list" item="user" separator="," >
        (#{user.name}, #{user.age}, #{user.county}, #{user.date})
    </foreach>
</insert>

特别注意:Mysql默认接受sql的大小是 1048576(1M), 即第三种方式若数据量超过1M会报如下异常:(可通过调整MySQL安装目录下的 my.ini 文件中 [mysqld] 段的 max_allowed_packet = 1M )

useGeneratedKeys="true" keyProperty="id" 是用于插入之后返回自增主键的id的

在这里插入图片描述

从结果可以看出,我们一下插入了两条数据,每条数据之间使用 “,” 逗号进行分割, separator="," 的作用就是如此。其中 < foreach > 标签内部的属性务必加上 item。

与上面批量插入对应的mapper代码如下:

int insert(List<User> userList);

当然,其实说到批量插入,最粗暴方法就是for循环直接插入,但是数据量越多,耗费的时间将是巨大的。 

记录条数普通for循环Mybatis batchforeach标签
500条77427388622
1000条1529015078746
5000条780111773501172
10000条3974722011801205

栗子2:list集合参数

<select id="dynamicSqlSelectList" resultType="com.dl.POJO.User">
     SELECT * from user WHERE id in
     <foreach collection="list" item="id" open="(" close=")" separator="," >
         #{id}
     </foreach>
 </select>

在这里插入图片描述

可以看出我们的 SQL 语句新增了:( ? , ? ) ,前后的括号由 open="(" close=")" 进行控制,用 “?” 占位符占位,并通过separator以:“,”隔开,内部两个循环遍历出的元素。

栗子3:map参数 < map> 标签需要结合MyBatis的参数注解 @Param() 来使用,需要告诉Mybatis配置文件中的 collection="map" 里的map是一个参数:

<select id="dynamicSqlSelectMap" resultType="com.lks.bean.User">
     select * from users WHERE
     <foreach collection="map" index="key" item="value"  separator="=">
         ${key} = #{value}
     </foreach>
 </select>

2.4 choose、when、otherwise 标签

这三个标签需要组合在一起使用,类似于 Java 中的 switch、case、default。只有一个条件生效,也就是只执行满足的条件 when,没有满足的条件就执行 otherwise,表示默认条件。

 <select id="dynamicSql2" resultType="com.lks.domain.User">
     select * from users
     <where>
         <choose>
             <when test="name != null and name != ''">
                 AND name = #{name}
             </when>
             <when test="county != null and county != ''">
                 AND county = #{county}
             </when>
             <otherwise>
                 AND id = #{id}
             </otherwise>
         </choose>
     </where>
 </select>

即使同时添加name和county的值,最终的sql也只会添加第一个属性值。

三、总结

使用批量插入执行的SQL语句应该等价于:

insert into users(name, age, county, date) values (?,?,?,? ),(?,?,?,? ),(?,?,?,? ),(?,?,?,?)

在使用foreach的时候最关键的也是最容易出错的就是collection属性,该属性是必须指定的,但是在不同情况 下,该属性的值是不一样的,主要有一下3种情况:

1.如果传入的是单参数且参数类型是一个List的时候,collection属性值为list

2.如果传入的是单参数且参数类型是一个array数组的时候,collection的属性值为array

3.如果传入的参数是多个的时候,我们就需要把它们封装成一个Map了,当然单参数也可以封装成map

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

相关文章

  • elasticsearch bucket 之rare terms聚合使用详解

    elasticsearch bucket 之rare terms聚合使用详解

    这篇文章主要为大家介绍了elasticsearch bucket 之rare terms聚合使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • 使用SpringBoot实现自动发送注册验证码邮件功能

    使用SpringBoot实现自动发送注册验证码邮件功能

    一直以来,我都对程序如何自动发送邮件感到好奇,想象一下,当你在某个网站注册时,输入邮箱后不久就收到一封带有验证码的邮件,这种体验既方便又高效,所以本文给大家介绍了如何用 Spring Boot 实现自动发送注册验证码邮件,需要的朋友可以参考下
    2025-04-04
  • mybatisPlus实现倒序拼接字符串

    mybatisPlus实现倒序拼接字符串

    这篇文章主要介绍了mybatisPlus实现倒序拼接字符串方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • idea中创建maven的Javaweb工程并进行配置(图文教程)

    idea中创建maven的Javaweb工程并进行配置(图文教程)

    这篇文章主要介绍了idea中创建maven的Javaweb工程并进行配置,本文通过图文并茂的形式给大家介绍的非常详细,文中给大家提到了tomcat的运行方法,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-02-02
  • Java Apache Shiro安全框架快速开发详解流程

    Java Apache Shiro安全框架快速开发详解流程

    Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序
    2021-10-10
  • 在eclipse中修改tomcat的部署路径操作

    在eclipse中修改tomcat的部署路径操作

    这篇文章主要介绍了在eclipse中修改tomcat的部署路径操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2021-01-01
  • SpringSecurity6.4中一次性令牌登录(One-Time Token Login)实现

    SpringSecurity6.4中一次性令牌登录(One-Time Token Login)实现

    Spring Security为一次性令牌认证提供了支持,本文就来介绍一下SpringSecurity6.4中一次性令牌登录(One-Time Token Login)实现,具有一定的参考价值,感兴趣的可以了解一下
    2025-03-03
  • 详解Java中的Reflection反射和暴力反射

    详解Java中的Reflection反射和暴力反射

    本文主要介绍了详解Java中的Reflection反射和暴力反射,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-06-06
  • JDK1.7的ConcurrentHashMap源码解析

    JDK1.7的ConcurrentHashMap源码解析

    这篇文章主要介绍了JDK1.7的ConcurrentHashMap源码解析,HashMap是非线程安全的,而HashTable是线程安全的,但是HashTable实现同步的方法比较暴力,即在所有的方法体上添加synchronized关键字,需要的朋友可以参考下
    2023-12-12
  • maven的settings.xml、pom.xml配置文件使用详解

    maven的settings.xml、pom.xml配置文件使用详解

    本文详解了Maven中的配置文件settings.xml和pom.xml,阐述了它们的作用、配置项以及优先级顺序,settings.xml存在于Maven安装目录和用户目录下,分别作用于全局和当前用户,pom.xml位于项目根路径下
    2024-09-09

最新评论