【MyBatis-Plus】深入解析 MP:条件构造器 wrapper 类介绍、QueryWrapper 实现动态 SQL、实体类名自动映射表名、SQL运算符、字段名与属性名映射不一致导致查询报错问题

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


条件构造器


入门程序里的使用,都是简单的 CRUD。

在实际的应用场景中,我们还需要使用更复杂的操作,MyBatis-Plus 也给我们提供了相应的支持。


MyBatis-Plus 提供了一套强大的条件构造器(Wrapper)Wrapper 是 MyBatis-Plus 对动态 SQL 条件面向对象抽象,其命名直观体现了它作为"条件构造(包装)器"的核心职责:用于构建复杂的数据库查询条件

条件构造器 | MyBatis-Plus


Wrapper 类及其功能


Wrapper 类允许开发者以链式调用的方式构造查询条件,无需编写繁琐的 SQL 语句,从而提高开发效率,并减少 SQL 注入的风险

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


  • AbstractWrapper
    • 这是一个抽象基类,提供了所有 Wrapper 类共有的方法属性
    • 它定义了条件构造基本逻辑,包括字段(column)值(value)操作符(condition)等。
    • 所有的 QueryWrapperUpdateWrapperLambdaQueryWrapperLambdaUpdateWrapper 都继承自 AbstractWrapper

  • QueryWrapper
    • 专门用于构造查询条件,支持基本的等于不等于大于小于等各种常见操作。
    • 它允许你以链式调用的方式添加多个查询条件,并且可以组合使用 andor 逻辑。

  • UpdateWrapper
    • 用于构造更新条件,可以在更新数据时指定条件
    • QueryWrapper 类似,它也支持链式调用逻辑组合
    • 使用 UpdateWrapper 可以在不创建实体对象的情况下,直接设置更新字段和条件

  • LambdaQueryWrapper
    • 这是一个基于 Lambda 表达式查询条件构造器
    • 它通过 Lambda 表达式来引用实体类的属性,从而避免了硬编码字段名
    • 这种方式提高了代码的可读性可维护性,尤其是在字段名可能发生变化的情况下。

  • LambdaUpdateWrapper

    • 类似于 LambdaQueryWrapper

    • LambdaUpdateWrapper 是基于 Lambda 表达式更新条件构造器

    • 它允许你使用 Lambda 表达式指定更新字段和条件,同样避免了硬编码字段名的问题。


在这里插入图片描述


QueryWrapper


QueryWrapper 并不只用于查询语句,无论是修改、删除、查询,都可以使用 QueryWrapper 来构建查询条件。


构造查询语句


通过条件构造器 QueryWrapper 完成下述 SQL 查询:

SELECT id, username, password, age 
FROM user_info 
WHERE age = 18 AND username 
LIKE "%min%"

接下来,我们需要使用 MyBatis-Plus提供的条件构造器 QueryWrapper;

并且指定泛型参数,限定为UserInfo实体类(通过UserInfo泛型,完成实体类表字段隐式映射):

image-20250413152507954


链式调用

条件构造操作(连着调用三个方法,属于链式调用)

  • .select("id,username,password,age") → 字段投影(指定查询列)
  • .eq("age",18) → 等值条件(WHERE age = 18)
  • .like("username", "min") → 模糊查询(WHERE username LIKE ‘%min%’)

image-20250413152920606


调用 MyBatis-plus 提供的 BaseMapper 接口的内置方法 selectList() ,来根据构造好的条件查找表 user_info 符合构造条件的记录:

image-20250413153046713


打印查询到的结果:

image-20250413153437960


MyBatis-Plus 自动映射表名的机制

在使用 MyBatis-Plus 的 selectByCondition() 方法时,我们发现:

  1. 不需要显式指定 FROM 表名
  2. 却能准确查询到 user_info 表的数据

image-20250413185852361


这与传统 MyBatis 的以下方式形成对比:

  • 注解方式需要写 @Select("SELECT * FROM user_info")
  • XML 方式需要写 <select id="..." resultMap="...">SELECT * FROM user_info</select>

思考:

  1. MyBatis-Plus 是如何实现:自动确定查询的目标表名
  2. MyBatis-Plus 是通过什么机制将实体类数据库表进行关联?

在 MyBatis-Plus 中,查询的表名 user_info 是通过以下方式隐式指定的:image-20250413162750884


完整逻辑链:
QueryWrapper<UserInfo> → 通过泛型找到 UserInfo 实体类 → 通过实体类的 @TableName 或默认规则确定表名 → 生成 SELECT ... FROM user_info ...

image-20250413160550417


运行对应测试类方法,观察打印日志:

image-20250413164311557


MyBatis-Plus 字段名与属性名映射不一致导致查询报错

MyBatis-Plus 在字段映射时,还有一个注意事项,煮啵用一个具体的例子解释:

假设实体类和数据库表是这样定义的:

@Data
@TableName("user_info")
public class UserInfo {
    @TableField(value = "user_name")  // 数据库字段是user_name
    private String username;         // 实体属性是username
    
    private Integer age;
}

当你这样查询时:

QueryWrapper<UserInfo> wrapper = new QueryWrapper<>();
wrapper.select("username, age")  // 这里写的是实体属性名
      .eq("age", 18);

生成的SQL会是:

SELECT username, age FROM user_info WHERE age = 18

但数据库实际没有username字段(只有user_name字段),这时就会报错字段不存在。


为什么会这样?

  1. 默认行为
    不指定select时,MP会自动给字段加别名:

    SELECT user_name AS username, age FROM user_info
    
  2. 指定select后的变化
    当手动指定select("username,age")后:

    • MP会直接使用你写的字段名,不再自动转换
    • 但数据库实际需要的是user_name而非username

解决字段名映射不一致的方法

方案示例代码优点缺点
自定义SQL@Select("SELECT user_name, age FROM user_info")最灵活失去MP的便捷性
保持属性名与字段名一致实体类改成:private String user_name;最简单破坏Java命名规范
不指定select字段去掉wrapper.select()自动处理别名无法控制返回字段
使用LambdaQueryWrapperwrapper.select(UserInfo::getUsername, UserInfo::getAge)类型安全需要MP 3.0+

推荐使用方案4(Lambda写法)

LambdaQueryWrapper<UserInfo> wrapper = new LambdaQueryWrapper<>();
wrapper.select(UserInfo::getUsername, UserInfo::getAge)
       .eq(UserInfo::getAge, 18);

这样会生成正确的SQL:

SELECT user_name, age FROM user_info WHERE age = 18

因为Lambda表达式能自动识别:

  • 方法引用 UserInfo::getUsername → 对应@TableField("user_name")
  • 完全避免字段名硬编码问题

构造更新语句


更新:完成下述 SQL 查询。

UPDATE user_info SET delete_flag=? WHERE age < 20

测试代码:

image-20250413163258533


image-20250413163601026


image-20250413163841984


执行测试类方法,观察打印日志:

image-20250413163957130


刷新数据库表,确认数据是否更新:

image-20250413164033609


SQL 比较运算符

缩写全称含义SQL等价示例
ltless than小于<lt("age",18)age<18
leless than or equal to小于等于<=le("age",18)age<=18
gtgreater than大于>gt("score",90)score>90
gegreater than or equal to大于等于>=ge("price",100)price>=100
eqequals等于=eq("name","John")name='John'
nenot equals不等于<>或!=ne("status",0)status<>0

说明

  1. 所有方法均支持链式调用(如 .eq("status",1).gt("age",18))。
  2. Lambda 表达式写法(如 lt(User::getAge, 18))可避免字段名硬编码。

构造删除语句


删除:完成下述 SQL 查询。

DELETE FROM user_info WHERE age = 18

测试代码:

image-20250413180058988


运行对应测试类方法,观察打印日志:

image-20250413180211187

所以无论查找、更新、删除相关的 SQL 语句,只要有 where 条件,都可以使用 QureyWrapper 来构造 SQL 语句;


在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值