QueryWrapper 的apply 是什么意思

在 MyBatis Plus 中,QueryWrapper 的 apply 方法用于 直接注入原生 SQL 片段 到查询条件中(通常是 WHERE 子句)。

当 MyBatis Plus 提供的标准方法(如 eqgtlike 等)无法满足复杂查询需求时,可以使用 apply 来编写自定义的 SQL 逻辑。


一、核心作用

apply 允许你在 Wrapper 中拼接任意的 SQL 条件,主要用于以下场景:

  1. 使用数据库函数:如 DATE_FORMATUNIX_TIMESTAMPFIND_IN_SET 等。
  2. 字段与字段比较:标准方法通常是 字段 = 值,而 apply 可以实现 字段 A > 字段 B
  3. 特殊语法:如 JSON 查询、全文检索、空间几何查询等。

二、使用示例

1. 使用数据库函数

查询创建日期为 "2023-10-01" 的用户(忽略时间部分)。


QueryWrapper<User> wrapper = new QueryWrapper<>();
// SQL: WHERE DATE_FORMAT(create_time, '%Y-%m-%d') = '2023-10-01'
wrapper.apply("DATE_FORMAT(create_time, '%Y-%m-%d') = {0}", "2023-10-01");

List<User> users = userMapper.selectList(wrapper);
2. 字段与字段比较

查询 start_time 早于 end_time 的记录。


QueryWrapper<Order> wrapper = new QueryWrapper<>();
// SQL: WHERE start_time < end_time
wrapper.apply("start_time < end_time");

List<Order> orders = orderMapper.selectList(wrapper);
3. 复杂逻辑(FIND_IN_SET)

查询标签包含 "1" 的记录(MySQL 特有)。


QueryWrapper<Product> wrapper = new QueryWrapper<>();
// SQL: WHERE FIND_IN_SET('1', tags)
wrapper.apply("FIND_IN_SET({0}, tags)", "1");

List<Product> products = productMapper.selectList(wrapper);

三、⚠️ 重要安全警告(SQL 注入)

apply 是 SQL 注入的高发区! 必须严格遵守以下规则:

✅ 正确写法(使用占位符)

使用 {0}{1} 作为占位符,并将用户输入作为参数传递。MyBatis 会自动进行预编译处理(?),防止注入。


// 安全:username 会被当作参数处理
String username = userInput; 
wrapper.apply("name = {0}", username);
// 生成 SQL: WHERE name = ?
❌ 错误写法(直接拼接)

绝对禁止将用户输入直接拼接到 SQL 字符串中。


// 危险!如果 userInput 是 "admin' OR '1'='1",会导致全表泄露
String username = userInput;
wrapper.apply("name = '" + username + "'"); 
// 生成 SQL: WHERE name = 'admin' OR '1'='1' (注入成功)

四、apply 与其他方法的区别

方法作用示例适用场景
eq / gt标准条件.eq("name", "张三")常规字段等于/大于某值
apply注入 SQL 片段.apply("DATE_FORMAT(time, '%Y') = {0}", 2023)函数、字段对比、特殊语法
last追加到 SQL 末尾.last("LIMIT 1")强行添加 LIMIT、ORDER BY 等(优先级最高)
func条件复用.func(consumer)抽取公共条件逻辑

注意apply 生成的内容会位于 WHERE 子句中,而 last 是直接拼接到整个 SQL 语句的最后。


五、LambdaQueryWrapper 中的 apply

LambdaQueryWrapper 也可以使用 apply,但要注意 apply 内部无法使用方法引用,只能写字符串列名。


LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
// 注意:这里不能写 User::getCreateTime,只能写字符串 "create_time"
wrapper.apply("DATE_FORMAT(create_time, '%Y-%m-%d') = {0}", "2023-01-01");

缺点:在 LambdaWrapper 中使用 apply 会失去列名的类型安全检查(如果数据库列名重构,这里不会报错,运行时会报错)。


六、总结与建议

  1. 优先使用标准方法:能用 eqlikein 解决的,不要用 apply,以保证代码可读性和数据库无关性。
  2. 必须防注入:使用 apply 时,务必使用 {0}{1} 占位符传递参数,严禁字符串拼接。
  3. 注意数据库兼容apply 中的 SQL 是原生的,切换数据库(如 MySQL 转 Oracle)时可能需要修改代码。
  4. 替代方案:如果 apply 逻辑过于复杂,建议直接写 XML 自定义 SQL,更易于维护。

一句话总结apply 是 MyBatis Plus 留给你的“后门”,用于处理标准 API 搞不定的特殊 SQL 逻辑,但使用时务必小心 SQL 注入风险。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值