1. 问题背景与现象还原最近在项目中使用MyBatis Plus的QueryWrapper构建动态SQL时遇到了一个典型的拼接问题。具体场景是在自定义XML映射文件中使用Param(Constants.WRAPPER)注解配合${ew.customSqlSegment}时生成的SQL语句出现了AND条件衔接错误。这个问题看似简单但排查过程却让我对MyBatis Plus的条件构造器有了更深的理解。先还原下问题现场我需要根据前端传入的不同查询参数动态生成WHERE条件于是写了如下Mapper接口方法ListUser selectUserList(Param(Constants.WRAPPER) QueryWrapperUser wrapper);对应的XML映射文件是这样写的select idselectUserList resultTypeUser SELECT * FROM user WHERE status 1 ${ew.customSqlSegment} /select当wrapper中添加了多个条件时比如wrapper.eq(dept_id, 10) .like(user_name, 张);预期生成的SQL应该是SELECT * FROM user WHERE status 1 AND dept_id 10 AND user_name LIKE %张%但实际执行时却变成了SELECT * FROM user WHERE status 1 dept_id 10 AND user_name LIKE %张%可以看到第一个条件dept_id 10前面缺少了AND连接词导致SQL语法错误。2. 问题根源分析2.1 MyBatis Plus的条件拼接机制要理解这个问题需要先了解MyBatis Plus的条件构造器工作原理。QueryWrapper在生成SQL片段时会根据是否是第一个条件自动决定是否添加AND前缀。具体逻辑在AbstractWrapper类的getNormal()方法中protected String getNormal() { return (StringUtils.isNotBlank(expression) ? (firstOfWhere ? : AND ) expression : ); }这里的firstOfWhere标记非常重要它表示当前条件是否是WHERE子句中的第一个条件。如果是则不添加AND前缀否则会自动添加。2.2 ${ew.customSqlSegment}的工作机制当我们使用${ew.customSqlSegment}时MyBatis Plus会将QueryWrapper中所有非空条件拼接成一个完整的SQL片段。关键点在于这个片段会保留QueryWrapper内部的条件顺序和AND前缀但不会考虑这个片段将如何被外部SQL使用2.3 问题产生的根本原因在我们的例子中XML里已经有一个固定条件status 1后面直接拼接${ew.customSqlSegment}。QueryWrapper不知道前面已经有WHERE条件所以它生成的第一个条件dept_id 10没有添加AND前缀导致语法错误。3. 解决方案对比3.1 方案一手动添加AND前缀不推荐最直观的修改方式是在XML中手动添加ANDselect idselectUserList resultTypeUser SELECT * FROM user WHERE status 1 AND ${ew.customSqlSegment} /select这样虽然能解决问题但存在明显缺陷如果wrapper没有添加任何条件会生成WHERE status 1 AND这样的非法SQL破坏了QueryWrapper的自包含性3.2 方案二使用条件构造器包含固定条件推荐更好的做法是将固定条件也放入QueryWrapper中wrapper.eq(status, 1) .eq(dept_id, 10) .like(user_name, 张);然后修改XML为select idselectUserList resultTypeUser SELECT * FROM user ${ew.customSqlSegment} /select这种方式的优点保持SQL生成的统一性避免手动拼接带来的风险所有条件都在Java代码中集中管理3.3 方案三使用Select注解动态SQL如果项目允许还可以考虑完全使用注解方式Select(script SELECT * FROM user WHERE status 1 if testew ! null${ew.customSqlSegment}/if /script) ListUser selectUserList(Param(Constants.WRAPPER) QueryWrapperUser wrapper);这种方式更灵活可以添加更多动态逻辑。4. 深入原理与最佳实践4.1 QueryWrapper的内部状态管理QueryWrapper内部维护了几个关键状态firstOfWhere标记是否是WHERE后的第一个条件paramNameValuePairs存储参数键值对expression当前条件表达式当调用getCustomSqlSegment()时它会遍历所有条件根据firstOfWhere状态决定是否添加AND前缀。4.2 条件拼接的边界情况处理在实际项目中还需要考虑以下边界情况wrapper为空时应该跳过${ew.customSqlSegment}拼接混合使用固定条件和动态条件时需要统一管理条件顺序多表关联查询时注意表别名的一致性4.3 性能考量虽然${ew.customSqlSegment}很方便但要注意大量使用字符串拼接可能影响SQL解析性能复杂的动态条件建议使用script标签对于固定模式查询考虑使用预编译SQL5. 常见问题排查指南5.1 问题现象SQL语法错误可能原因缺少AND连接词如本文案例条件值为空导致生成非法SQL表别名不一致排查步骤打印实际执行的SQL设置mybatis-plus.configuration.log-implorg.apache.ibatis.logging.stdout.StdOutImpl检查WHERE子句的连接词验证条件值是否为空5.2 问题现象条件不生效可能原因wrapper条件被意外清空参数名称不匹配未使用Param(Constants.WRAPPER)XML中未正确使用${ew.customSqlSegment}排查步骤调试检查wrapper对象内容确认Mapper方法参数注解检查XML片段是否正确5.3 问题现象SQL注入风险可能原因直接拼接用户输入到wrapper条件中使用${}而非#{}处理参数解决方案始终使用wrapper的eq、like等方法处理用户输入避免直接拼接字符串到条件中对复杂查询使用XML动态SQL6. 高级技巧与实战经验6.1 链式调用的优雅写法在实际项目中可以这样组织条件构造public ListUser queryUsers(UserQuery query) { return userMapper.selectList(new QueryWrapperUser() .eq(query.getDeptId() ! null, dept_id, query.getDeptId()) .like(StringUtils.isNotBlank(query.getUserName()), user_name, query.getUserName()) .between(query.getStartTime() ! null query.getEndTime() ! null, create_time, query.getStartTime(), query.getEndTime()) .orderByDesc(create_time)); }这种写法使用条件方法的重载版本避免空判断保持代码的整洁性和可读性易于扩展新的查询条件6.2 复杂查询的构建策略对于多表关联等复杂查询建议使用SelectProvider动态生成SQL或者创建专门的Query对象和Builder对于报表类查询考虑使用原生SQLResultMap6.3 性能优化建议为常用查询条件添加数据库索引避免在wrapper中使用SELECT *对于大数据量查询考虑分页或延迟加载7. 替代方案比较除了QueryWrapperMyBatis Plus还提供了其他条件构造方式7.1 LambdaQueryWrapper使用示例new LambdaQueryWrapperUser() .eq(User::getStatus, 1) .like(User::getUserName, 张)优点类型安全编译时检查更好的IDE支持缺点稍复杂的表达式支持有限7.2 QueryChainWrapper使用示例userService.query() .eq(status, 1) .list();优点更简洁的链式调用与Service层更好集成7.3 原生MyBatis动态SQL对于特别复杂的查询有时回退到原生MyBatis动态SQL可能是更好的选择select idselectUsers resultTypeUser SELECT * FROM user where if teststatus ! null AND status #{status} /if if testuserName ! null AND user_name LIKE CONCAT(%, #{userName}, %) /if /where /select8. 实际项目中的经验总结经过多个项目的实践我总结了以下经验保持一致性整个项目应该统一使用一种条件构造方式避免混用造成维护困难防御性编程对前端传入的查询参数进行校验避免生成危险的SQL日志记录在生产环境记录关键查询的SQL和参数便于问题排查性能监控对复杂查询进行性能监控及时发现潜在问题文档注释为复杂的查询条件添加详细注释说明业务含义和使用场景这个问题的解决过程让我深刻理解到即使是看似简单的工具深入理解其工作原理也能避免很多陷阱。MyBatis Plus的条件构造器虽然强大但也需要正确使用才能发挥最大价值。
企业数字化 ERP 产品动态
相关推荐
JMeter分布式压力测试实战指南与优化技巧 1. 压力测试基础概念解析压力测试(Stress Test)作为性能测试的重要组成部分,主要目的是评估系统在极端条件下的表现。在实际工作中,我们通常需要模拟超出正常操作条件的负载,以确定系统的瓶颈和失效点。这种测试方法在… · 2026/11/6 4:08:27
PL/0编译器教学骨架:语法分析+语义检查+四元式生成 简介:本资源是广东工业大学编译原理课程配套实验项目,面向计算机专业本科生及编译技术初学者,聚焦PL/0教学编译器的深度改造实践。实验要求在原PL/0基础上扩充保留字(ELSE、FOR、TO、DOWNTO、RETURN)、新增复合赋值与自… · 2026/11/6 4:08:27
SQLyog数据库管理工具使用与优化指南 1. 数据库管理工具的选择与使用心得作为长期从事数据库管理工作的技术人员,我深知一款优秀的数据库客户端工具对工作效率的影响。在众多MySQL管理工具中,SQLyog凭借其直观的界面和强大的功能,成为了许多开发者和DBA的首选。今天我想分享一些关… · 2026/11/6 4:08:27
初级电工证的职务与郑州报考避坑指南:想上岗必须先考证 初级电工证的职务与郑州报考避坑指南:想上岗必须先考证 你是不是也正为找工作发愁?一边是招工单位要求必须持有特种作业操作证,一边是你手头还没个证,这种时候,焦虑感简直要压垮你了。 初级电工证的职务… · 2026/11/7 1:20:09
安阳市政公用工程从业者怎么考【苟小强电工证】?官方回应来了 安阳市政公用工程从业者怎么考【苟小强电工证】?官方回应来了 证书过期了不知道怎么复审延期?这事儿别慌,官方回应已经明确,特种作业操作证复审和延期是有明确流程和时间规定的。作为一名在安阳市政工程领域从业多年的电工,我深知电工证在日常施工中的重… · 2026/11/7 1:20:09
高压电工证超期复审怎么搞?2026河南最新政策别踩坑 高压电工证超期复审怎么搞?2026河南最新政策别踩坑 你学历不高怕报不上名?高压电工证超期复审其实没你想象那么难,关键是要懂规则。2026年河南最新政策已经调整,如果你是濮阳的公路工程从业者,千万别忽视高压电工证的复审流程,不然轻则影响工作… · 2026/11/7 1:20:09
抚顺市参考电工证怕考不过白交培训费?良心建议带你避坑 抚顺市参考电工证怕考不过白交培训费?良心建议带你避坑 怕考不过白交培训费?这不是你一个人的焦虑,每年都有不少学员因为备考不得法,要么临时抱佛脚,要么死磕难点,结果在考试现场被几个基础题卡住,白白浪费了时间和钱。今天我以多年从事特种作业考试辅… · 2026/11/7 1:20:09
戚金柱电工证实操考试别踩坑,实操考试心里没底怕挂科怎么办 戚金柱电工证实操考试别踩坑,实操考试心里没底怕挂科怎么办 实操考试心里没底怕挂科?这是大多数准备考取【戚金柱电工证】学员的共同心声。尤其是首次参加考试的新手,对考试流程、评分标准和操作步骤都缺乏直观理解,一不小心就可能掉进坑里。… · 2026/11/7 1:20:09
塘沽杭州道电工证怎么报名?实操考试心里没底怕挂科?教你一次过 塘沽杭州道电工证怎么报名?实操考试心里没底怕挂科?教你一次过 实操考试心里没底怕挂科?你不是一个人。很多准备报考塘沽杭州道电工证的朋友,最担心的就是考试时手忙脚乱,操作不熟练,最后挂科。别担心,这篇文章就是为你量身打造的,从报名到考试,从复… · 2026/11/7 1:19:09
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 10:29:23
澳洲水电工证考试:证书过期了不知道怎么复审延期?电子证书怎么弄? 澳洲水电工证考试:证书过期了不知道怎么复审延期?电子证书怎么弄? 证书过期了不知道怎么复审延期?别慌,很多人在拿到【澳洲水电工证考试】的电子证书后,一不小心就忽略了复审时间,结果证书失效,工作受阻,甚至被单位追责。今天就从【澳洲水电工证考试… · 2026/11/7 0:00:41
电工作业提示版通过率揭秘:怎么考才能一次过? 电工作业提示版通过率揭秘:怎么考才能一次过? 怕考不过白交培训费?别急,今天我就从驻马店应届工程类毕业生的视角,带你全面了解电工作业提示版的考试流程、备考要点和真实通过率,帮你少走弯路、提高一次通过率。… · 2026/11/7 0:00:41
电工证年审报名千万别踩坑 外省证转过来要搞清这几点 电工证年审报名千万别踩坑 外省证转过来要搞清这几点 之前考的证在外省能不能转过来?这个问题是很多电工从业者最关心的,特别是像你这样在商丘从事公路工程的人员,经常需要跨省流动,证书的 有效性 和 可迁移性… · 2026/11/7 0:00:41
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:27:59
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/5 10:28:03
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/6 10:29:23