MyBatis-Plus条件构造器实战指南:从基础查询到安全防护
记得第一次用MyBatis-Plus的EntityWrapper时,我对着文档里的"苍老师"示例笑了半天,但很快发现这个看似简单的工具背后藏着不少门道。今天咱们不聊段子,只谈干货——如何用条件构造器写出既高效又安全的动态查询。
1. 条件构造器基础:从入门到精通
刚接触MyBatis-Plus时,EntityWrapper就像是一把瑞士军刀,能解决各种查询场景。但要用好它,得先理解几个核心概念:
字段引用原则:与JPA不同,EntityWrapper使用的是数据库字段名而非Java属性名。这个细节坑过不少新手:
// 错误示范 - 使用Java属性名 wrapper.eq("lastName", "Tom"); // 正确写法 - 使用数据库字段名 wrapper.eq("last_name", "Tom");常用方法速查表:
| 方法名 | 等效SQL | 适用场景 |
|---|---|---|
| eq() | = | 精确匹配 |
| ne() | != | 不等于 |
| gt()/lt() | >/< | 数值比较 |
| between() | BETWEEN x AND y | 范围查询 |
| like() | LIKE '%x%' | 模糊查询 |
| in() | IN (x,y,z) | 多值匹配 |
提示:所有条件方法都支持链式调用,但建议每行一个条件保持代码可读性
2. 复杂查询构建:OR条件的艺术
当查询条件需要OR逻辑时,or()和orNew()的区别就像咖啡和茶——看似相似实则大不同。来看个实际案例:
// 查询名字包含"张"或者年龄大于30的员工 List<Employee> employees = employeeMapper.selectList( new EntityWrapper<Employee>() .like("last_name", "张") .or() // 注意这个or的位置 .gt("age", 30) ); // 生成的SQL: // SELECT * FROM employee WHERE (last_name LIKE '%张%' OR age > 30)而orNew()会创建新的条件分组:
List<Employee> employees = employeeMapper.selectList( new EntityWrapper<Employee>() .eq("gender", 1) .orNew() // 新的条件组 .like("email", "@company.com") ); // 生成的SQL: // SELECT * FROM employee WHERE (gender = 1) OR (email LIKE '%@company.com%')何时用哪个:
- 需要简单OR逻辑时用
or() - 需要明确分组条件时用
orNew() - 复杂嵌套条件建议直接使用XML方式
3. 排序与分页的陷阱
分页查询是高频操作,但有些用法暗藏风险:
// 危险示例 - 使用last()直接拼接SQL List<Employee> list = employeeMapper.selectList( new EntityWrapper<Employee>() .orderBy("create_time") .last("limit 10, 5") // SQL注入风险点 ); // 安全写法 - 使用Page对象 Page<Employee> page = new Page<>(2, 5); // 第2页,每页5条 List<Employee> list = employeeMapper.selectPage( page, new EntityWrapper<Employee>().orderBy("create_time") );分页最佳实践:
- 始终优先使用Page对象
- 避免直接拼接limit子句
- 大数据量分页考虑使用
PageHelper物理分页
4. 安全防护:远离SQL注入
EntityWrapper虽然方便,但某些方法可能成为安全漏洞。最典型的就是last()方法:
// 危险!用户输入直接拼接 String userInput = "1; DROP TABLE employee; --"; wrapper.last("limit " + userInput); // 安全替代方案: wrapper.last("limit #{limit}", 10); // 使用参数化安全编码清单:
- [ ] 禁用直接字符串拼接
- [ ] 对用户输入进行校验
- [ ] 使用最新版MyBatis-Plus(3.x以上)
- [ ] 考虑迁移到LambdaQueryWrapper
5. 现代替代方案:LambdaQueryWrapper
新版MyBatis-Plus推荐使用LambdaQueryWrapper,它有几个明显优势:
- 类型安全:编译时检查字段名
- 代码可读性更强
- 避免拼写错误
// 传统EntityWrapper写法 new EntityWrapper<Employee>().eq("last_name", "Tom"); // LambdaQueryWrapper写法 new LambdaQueryWrapper<Employee>().eq(Employee::getLastName, "Tom");迁移建议:
- 新项目直接使用Lambda方式
- 老项目逐步替换
- 复杂查询可混合使用
6. ActiveRecord模式:简洁背后的代价
ActiveRecord模式确实能让代码更简洁:
Employee employee = new Employee(); employee.setLastName("张"); List<Employee> list = employee.selectList( new EntityWrapper<Employee>().like("last_name", "张") );但要注意:
- 违反了单一职责原则
- 可能造成实体类臃肿
- 不利于复杂业务逻辑处理
适用场景: ✓ 简单CRUD操作 ✓ 快速原型开发 ✓ 小型项目
7. 性能优化小贴士
在大数据量场景下,这些技巧能提升查询效率:
索引命中:确保条件字段有适当索引
// 好的查询 - 能命中索引 wrapper.eq("department_id", 10).gt("salary", 5000); // 差的查询 - 无法使用索引 wrapper.like("%name", "张");**避免SELECT ***:明确指定查询字段
wrapper.setSqlSelect("id", "name", "email");批量操作:使用专用批量方法
// 低效 for(Employee e : list) { e.insert(); } // 高效 employeeMapper.insertBatch(list);
在最近的一个用户管理系统项目中,我们通过合理使用条件构造器,将动态查询代码量减少了40%,同时通过规范使用方式完全消除了SQL注入风险。特别是在处理多条件筛选面板时,EntityWrapper的链式调用让代码既简洁又易维护。