别再写硬编码了!MyBatis-Plus的apply方法,这样用才安全又灵活(附日期查询实战)
别再写硬编码了MyBatis-Plus的apply方法这样用才安全又灵活附日期查询实战在Java开发中动态SQL拼接是个绕不开的话题。记得刚入行时我为了赶进度直接在代码里拼接SQL字符串结果上线后遭遇了SQL注入攻击差点酿成大祸。那次教训让我深刻认识到字符串拼接不是编程而是埋雷。今天我们就来聊聊MyBatis-Plus中那个被低估的apply方法——它可能是你代码安全最后的防线。1. 为什么我们需要告别硬编码硬编码SQL就像把数据库密码写在代码注释里——看似方便实则危险。我曾见过一个经典案例开发者为了查询某天的用户数据直接写出了这样的代码String sql date_format(birthday,%Y-%m-%d) date ; wrapper.apply(sql);当date参数来自用户输入时攻击者只需传入 OR 11这样的值就能轻松获取全表数据。而使用参数化替换的apply方法后wrapper.apply(date_format(birthday,%Y-%m-%d) {0}, date);MyBatis-Plus会自动将参数预处理为预编译语句从根本上杜绝注入风险。这两种方式的底层SQL对比如下方式生成SQL示例安全性硬编码date_format(birthday,%Y-%m-%d) 2023-01-01高危参数化date_format(birthday,%Y-%m-%d) ?安全2. apply方法的实战应用技巧2.1 日期处理的正确姿势日期查询是最容易踩坑的场景。假设我们需要查询2023年国庆节当天的用户// 错误示范直接拼接 wrapper.apply(DATE(birthday) 2023-10-01); // 正确做法参数化 wrapper.apply(DATE(birthday) {0}, LocalDate.of(2023, 10, 1));更复杂的场景比如查询最近30天活跃用户wrapper.apply(last_login_time BETWEEN {0} AND {1}, LocalDateTime.now().minusDays(30), LocalDateTime.now());2.2 动态条件组合的艺术apply的condition参数让动态SQL变得优雅。比如根据权限过滤数据boolean isAdmin getCurrentUser().isAdmin(); wrapper.apply(!isAdmin, department_id {0}, userDeptId);当isAdmin为false时才会添加部门过滤条件。这种写法比在service层写if判断更清晰。3. 高级应用数据库函数安全调用3.1 字符串函数的安全使用字符串拼接同样需要防范注入。比如模糊查询用户名// 危险方式 wrapper.apply(CONCAT(%, name , %)); // 安全方式 wrapper.apply(CONCAT(%, {0}, %), name);3.2 JSON字段查询方案现在很多项目使用JSON字段存储扩展属性。假设我们要查询profile字段中address.city为北京的用户wrapper.apply(JSON_EXTRACT(profile, $.address.city) {0}, 北京);不同数据库的JSON函数差异较大但参数化原则不变数据库JSON查询函数示例MySQLJSON_EXTRACTJSON_EXTRACT(column, $.path)PostgreSQL-column-pathOracleJSON_VALUEJSON_VALUE(column, $.path)4. 性能优化与最佳实践4.1 索引使用注意事项虽然apply很强大但滥用会影响索引效率。比如这样的写法wrapper.apply(YEAR(create_time) {0}, 2023);会导致MySQL无法使用create_time上的索引。更好的方式是wrapper.between(create_time, LocalDateTime.of(2023, 1, 1, 0, 0), LocalDateTime.of(2023, 12, 31, 23, 59));4.2 复杂查询的拆解策略遇到特别复杂的SQL时不要试图用一个apply解决所有问题。比如多表关联查询// 不推荐 wrapper.apply(EXISTS (SELECT 1 FROM orders o WHERE o.user_id u.id AND o.status {0}), 1); // 推荐使用join或分开查询 wrapper.inSql(id, SELECT user_id FROM orders WHERE status 1);实际项目中我发现将复杂逻辑拆分为多个简单查询往往性能更好代码也更易维护。