MyBatis动态SQL与MyBatis-Plus高效开发指南
2026/8/22 21:25:51 网站建设 项目流程

文章目录

  • 1. 动态SQL
    • 1.1 <if>标签
    • 1.2 <trim>标签
    • 1.3 <where>标签
    • 1.4 <set>标签
    • 1.5 <foreach>标签
    • 1.6 <include>标签
  • 2. MyBatis-Plus
  • 3. MyBatis-Plus复杂操作
    • 3.1 常见注解
      • 3.1.1 @TableName
      • 3.1.2 @TableField
      • 3.1.3 @TableId
    • 3.2 打印日志
    • 3.3 条件构造器
      • 3.3.1 QueryWrapper
    • 3.3.2 UpdateWrapper
      • 3.3.3 LambdaQueryWrapper
      • 3.3.4 LambdaUpdateWrapper

1. 动态SQL

动态 SQL 是Mybatis的强大特性之一,能够完成不同条件下不同的 sql 拼接

1.1 <if>标签

可以用if标签来实现非必填字段
比如性别 gender 为非必填字段

接口定义

IntegerinsertUserByCondition(Integergender);

Xml实现

<insertid="insertUserByCondition">INSERT into userinfo ( username, password, age,<iftest="gender != null">gender,</if>phone) VALUES ( #{username}, #{password}, #{age},<iftest="gender != null">#{gender},</if>#{phone})</insert>

或者使用注解方式, 但并不推荐, 感兴趣可自行了解

test标签中的gender字段, 是传入对象的属性, 要跟Java对象的属性对应, 而不是数据库的字段
不可以不判断直接把字段设置为null, 这种情况下, 如果gender字段有默认值, 就会设置为默认值

1.2 <trim>标签

之前的插入用户功能,只是有一个 gender 字段可能是选填项,如果有多个字段,一般考虑使用标签结合标签,对多个字段都采取动态生成的方式.

  1. prefix:表示整个语句块,以prefix的值作为前缀
  2. suffix:表示整个语句块,以suffix的值作为后缀
  3. prefixOverrides:表示整个语句块要去除掉的前缀
  4. suffixOverrides:表示整个语句块要去除掉的后缀
<insertid="insertUserByCondition">INSERT INTO userinfo<trimprefix="("suffix=")"suffixOverrides=","><iftest="username !=null">username,</if><iftest="password !=null">`password`,</if><iftest="age != null">age,</if><iftest="gender != null">gender,</if><iftest="phone != null">phone,</if></trim>VALUES<trimprefix="("suffix=")"suffixOverrides=","><iftest="username !=null">#{username},</if><iftest="password !=null">#{password},</if><iftest="age != null">#{age},</if><iftest="gender != null">#{gender},</if><iftest="phone != null">#{phone}</if></trim></insert>

以上的sql,
基于 prefix 配置,开始部分加上 (
基于 suffix 配置,结束部分加上 )
多个 组织的语句都以 , 结尾,在最后拼接好的字符串还会以 , 结尾,会基于suffixOverrides 配置去掉最后一个, 注意suffixOverrides 只能去掉末尾的逗号, 如果末尾不是, suffixOverrides不会做处理

1.3 <where>标签

需求: 传入的用户对象,根据属性做where条件查询,用户对象中属性不为 null 的,都为查询条件.
如:
username 为 “a”,则查询条件为 where username=“a”
每个条件前面都写 AND,where标签会自动去掉第一个 AND,其他的保留

List<UserInfo>queryByCondition(UserInfouserInfo);
<selectid="queryByCondition"resultType="com.example.test.demos.dao.UserInfo">select id, username, age, gender, phone, delete_flag, create_time, update_time from userinfo<where><iftest="age != null">and age = #{age}</if><iftest="gender != null">and gender = #{gender}</if><iftest="deleteFlag != null">and delete_flag = #{deleteFlag}</if></where></select>

注意语句中数据库字段和Java对象字段的映射关系

1.4 <set>标签

需求: 根据传入的用户对象属性来更新用户数据,可以使用标签来指定动态内容
接口定义: 根据传入的用户 id 属性,修改其他不为 null 的属性

IntegerupdateUserByCondition(UserInfouserInfo);
<updateid="updateUserByCondition"><set><iftest="username != null">username = #{username}</if><iftest="age != null">age = #{age},</if><iftest="deleteFlag != null">delete_flag = #{deleteFlag},</if></set>where id = #{id}</update>

<set> :动态的在SQL语句中插入set关键字,并会删掉额外的逗号. (用于update语句中) 以上标签也可以使用 <trim prefix=“set” suffixOverrides=“,”> 替换
用if标签也能实现, 但是需要处理边界情况和多出来逗号, 很麻烦

1.5 <foreach>标签

对集合进行遍历时可以使用该标签。标签有如下属性:

  1. collection:绑定方法参数中的集合,如 List,Set,Map或数组对象
  2. item:遍历时的每一个对象
  3. open:语句块开头的字符串
  4. close:语句块结束的字符串
  5. separator:每次遍历之间间隔的字符串

接口定义: 根据多个userid, 删除用户数据

voiddeleteByIds(@Param("ids")List<Integer>ids);

XML实现

<deleteid="deleteByIds">delete from userinfo where id in<foreachcollection="ids"item="id"separator=","open="("close=")">#{id}</foreach></delete>

形参如果是多个集合, 需要用@Param, 否则会报错, 用了@Param后, collection的值要和@param中写的一致

1.6 <include>标签

在xml映射文件中配置的SQL,有时可能会存在很多重复的片段,此时就会存在很多冗余的代码
我们可以对重复的代码片段进行抽取,将其通过 <sql> 标签封装到一个SQL片段,然后再通过<include> 标签进行引用
<sql>:定义可重用的SQL片段
<include>:通过属性refid,指定包含的SQL片段

通过<sql>定义重复SQL片段

<sqlid="allColumn">id, username, age, gender, phone, delete_flag, create_time, update_time</sql>

通过 <include> 标签在原来抽取的地方进行引用

<selectid="queryAllUser"resultMap="BaseMap">select<includerefid="allColumn"></include>from userinfo</select>

2. MyBatis-Plus

MyBatis-Plus(简称 MP) 是一个 MyBatis 的增强工具, 在 MyBatis 的基础上只做增强不做改变, 为简化开发. 提高效率而生。

MybatisPlus提供了一个基础的 BaseMapper 接口,已经实现了单表的CRUD, 我们自定义的Mapper只需要继承这个BaseMapper, 就无需自己实现单表CRUD

@MapperpublicinterfaceUserInfoMapperextendsBaseMapper<UserInfo>{}

UserInfoMapper 在继承 BaseMapper 时, 指定了一个泛型, 这个UserInfo就是与数据库表相对应的实体类
默认情况下:

  1. 表名: 实体类的驼峰表示法转换成蛇形表示法(下划线分割), 作为表名. 比如UserInfo -> user_info
  2. 字段: 根据实体类的属性名, 转换为蛇形表示法作为字段名. 比如deleteFlag -> delete_flag
  3. 主键: 默认为id

3. MyBatis-Plus复杂操作

3.1 常见注解

3.1.1 @TableName

我们可以通过 @TableName 来标识实体类对应的表

@Data@TableName("user_info")publicclassUserInfo{privateIntegerid;privateStringusername;privateStringpassword;privateIntegerage;privateIntegergender;privateStringphone;privateIntegerdeleteFlag;privateDatecreateTime;privateDateupdateTime;}

3.1.2 @TableField

我们可以通过 @TableField 来标识 对应的字段名

@Data@TableName("user_info")publicclassUserInfo{privateIntegerid;privateStringusername;privateStringpassword;privateIntegerage;privateIntegergender;privateStringphone;@TableField("delete_flag")privateIntegerdeleteFlag;privateDatecreateTime;privateDateupdateTime;}

3.1.3 @TableId

我们可以通过 @TableId 来 指定对应的主键

@Data@TableName("user_info")publicclassUserInfo{@TableId("id")privateIntegerid;privateStringusername;privateStringpassword;privateIntegerage;privateIntegergender;privateStringphone;@TableField("delete_flag")privateIntegerdeleteFlag;privateDatecreateTime;privateDateupdateTime;}

如果属性名和字段名不一致, 需要在 @TableId 指明对应的字段名;
属性名和字段一致的情况下, 直接加 @TableId 注解就可以

3.2 打印日志

Mybatis-Plus配置日志如下:

mybatis-plus:configuration:# 配置打印 MyBatis日志log-impl:org.apache.ibatis.logging.stdout.StdOutImpl

3.3 条件构造器

入门程序里的使用, 都是简单的CRUD, 在实际的应用场景中, 我们还需要使用更复杂的操作, MyBatisPlus 也给我们提供了相应的支持, MyBatis-Plus 提供了一套强大的条件构造器(Wrapper), 用于构建复杂的数据库查询条件. Wrapper 类 允许开发者以链式调用的方式构造查询条件, 无需编写繁琐的 SQL 语句, 从而提高开发效率并减少 SQL注入 的风险

以下是主要的 Wrapper 类及其功能:

  1. AbstractWrapper:这是一个抽象基类, 提供了所有 Wrapper 类共有的方法和属性.
  2. QueryWrapper:用于构造查询条件, 在AbstractWrapper的基础上拓展了一个select方法, 允许指定查询字段
  3. UpdateWrapper: 用于构造更新条件, 可以在更新数据时指定条件
  4. LambdaQueryWrapper:基于 Lambda 表达式的查询条件构造器, 它通过 Lambda 表达式来引用实体类的属性,从而避免了硬编码字段名
  5. LambdaUpdateWrapper: 基于 Lambda 表达式的更新条件构造器, 它允许你使用 Lambda 表达式来指定更新字段和条件,同样避免了硬编码字段名的问题

LambdaQueryWrapper和LambdaUpdateWrapper是能完全替代QueryWrapper和UpdateWrapper的, 而且也更推荐使 lambdaWrapper

3.3.1 QueryWrapper

QueryWrapper并不只用于查询语句, 无论是修改, 删除, 查询, 都可以使用QueryWrapper来构建查询条件这里只简单介绍查询和更新

SELECTid,username,password,ageFROMuser_infoWHEREage=18ANDusername"%min%"
voidtestQueryWrapper(){QueryWrapper<UserInfo>userInfoQueryWrapper=newQueryWrapper<UserInfo>().select("id","username","password","age").eq("age",18).like("username","min");List<UserInfo>userInfos=userInfoMapper.selectList(userInfoQueryWrapper);}

QueryWrapper并不只用于查询语句, 无论是修改, 删除, 查询, 都可以使用QueryWrapper来构建查询条件这里只简单介绍查询和更新

UPDATEuser_infoSETdelete_flag=?WHEREage<20
voidtestUpdateByQueryWrapper(){QueryWrapper<UserInfo>userInfoQueryWrapper=newQueryWrapper<UserInfo>().lt("age",20);UserInfouserInfo=newUserInfo();userInfo.setDeleteFlag(1);userInfoMapper.update(userInfo,userInfoQueryWrapper);}

• lt : “less than” 的缩写,表示小于.
• le : "less than or equal to"的缩写, 表示小于等于
• ge : “greater than or equal to” 的缩写, 表示大于等于.
• gt : “greater than” 的缩写, 表示大于.
• eq : “equals” 的缩写, 表示等于.
• ne : “not equals” 的缩写, 表示不等于.

3.3.2 UpdateWrapper

对于更新, 我们也可以直接使用 UpdateWrapper, 在不创建实体对象的情况下, 直接设置更新字段和条件

UPDATEuser_infoSETdelete_flag=0,age=5WHEREidIN(1,2,3)
voidtestUpdateByUpdateWrapper(){UpdateWrapper<UserInfo>updateWrapper=newUpdateWrapper<UserInfo>().set("delete_flag",0).set("age",5).in("id",List.of(1,2,3));userInfoMapper.update(updateWrapper);}

QueryWrapper 和 UpdateWrapper存在一个问题, 就是需要写死字段名, 如果字段名发生变更, 可能会因为测试不到位导致出错;
MyBatis-Plus 给我们提供了一种基于Lambda表达式的条件构造器, 它通过 Lambda 表达式来引用实体类的属性,从而避免了硬编码字段名, 也提高了代码的可读性和可维护性

3.3.3 LambdaQueryWrapper

voidtestLambdaQueryWrapper(){QueryWrapper<UserInfo>queryWrapper=newQueryWrapper<UserInfo>();queryWrapper.lambda().select(UserInfo::getUsername,UserInfo::getPassword,UserInfo::getAge).eq(UserInfo::getId,1);userInfoMapper.selectList(queryWrapper);}

3.3.4 LambdaUpdateWrapper

voidtestLambdaUpdateWrapper(){UpdateWrapper<UserInfo>updateWrapper=newUpdateWrapper<UserInfo>();updateWrapper.lambda().set(UserInfo::getDeleteFlag,0).set(UserInfo::getAge,5).in(UserInfo::getId,List.of(1,2,3));userInfoMapper.update(updateWrapper);}

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询