JSQLParser 4.x 中 IN 表达式缺少括号的问题排查与解决
JSQLParser 4.x 中 IN 表达式缺少括号的问题排查与解决问题背景在使用 MyBatis Plus 拦截器动态拼接数据权限 SQL 时使用 JSQLParser 构造IN表达式运行后发现生成的 SQL 语法错误-- 期望SELECT*FROMsdk_projectWHEREdeleted0ANDsales_user_idIN(aaa,bbb,ccc)-- 实际SELECT*FROMsdk_projectWHEREdeleted0ANDsales_user_idINaaa,bbb,cccDruid 随即抛出异常com.alibaba.druid.sql.parser.ParserException: not supported. pos 116, line 1, column 116, token ,问题代码InExpressioninExpressionnewInExpression();inExpression.setLeftExpression(newColumn(sales_user_id));inExpression.setRightExpression(newExpressionList(ids.stream().map(StringValue::new).collect(Collectors.toList())));根因分析环境依赖版本JSQLParser4.9通过 MyBatis-Plus 3.5.7 传递引入关键结论InExpression在调用toString()时括号完全由rightExpression.toString()自身决定InExpression本身不负责添加括号。ExpressionList.toString() 不带括号通过反编译jsqlparser-4.9.jar验证// ExpressionList.toString() 等价源码OverridepublicStringtoString(){StringBuildersbnewStringBuilder();appendTo(sb);// 自行拼接不加括号returnsb.toString();// 输出aaa, bbb, ccc}ParenthesedExpressionList.toString() 带括号// ParenthesedExpressionList.toString() 等价源码OverridepublicStringtoString(){returnPlainSelect.getStringList(this,true,true);// ^^^^ ^^^^// useBracketstrue ─┘ │// useCommatrue ─────────┘// 输出(aaa, bbb, ccc)}两个true是源码内硬编码的不是外部调用者传入的。通过字节码可以直接验证ParenthesedExpressionList.toString(): 0: aload_0 ← this 1: iconst_1 ← 硬编码 trueuseBrackets 2: iconst_1 ← 硬编码 trueuseComma 3: invokestatic PlainSelect.getStringList:(List, Z, Z)三种写法实测对比ListStringidsArrays.asList(aaa,bbb,ccc);// A: Parenthesis 包裹 ExpressionListInExpressioninAnewInExpression();inA.setLeftExpression(newColumn(user_id));inA.setRightExpression(newParenthesis(newExpressionList(ids.stream().map(StringValue::new).collect(Collectors.toList()))));// 输出user_id IN (aaa, bbb, ccc) ✓// B: ParenthesedExpressionListInExpressioninBnewInExpression();inB.setLeftExpression(newColumn(user_id));inB.setRightExpression(newParenthesedExpressionList(ids.stream().map(StringValue::new).collect(Collectors.toList())));// 输出user_id IN (aaa, bbb, ccc) ✓// C: 裸 ExpressionList原始问题写法InExpressioninCnewInExpression();inC.setLeftExpression(newColumn(user_id));inC.setRightExpression(newExpressionList(ids.stream().map(StringValue::new).collect(Collectors.toList())));// 输出user_id IN aaa, bbb, ccc ✗ SQL 语法错误A 和 B 的 SQL 输出完全相同两种写法均可解决问题。Parenthesis 的原理Parenthesis是一个装饰器toString()直接硬拼括号// Parenthesis.toString() 等价源码OverridepublicStringtoString(){return(expression);}字节码验证Parenthesis.toString(): 7: ldc ( ← 硬拼左括号 13: append(expression) 31: ldc ) ← 硬拼右括号 39: areturntoString() 的触发时机setRightExpression()只存引用不触发toString()。整个序列化发生在最后调用statement.toString()时// 构造阶段只存引用无任何输出newParenthesedExpressionList(list)// 构造对象inExpression.setRightExpression(...)// 存引用// 序列化阶段statement.toString() 触发整棵 AST 递归序列化returnstatement.toString();// └─ InExpression.toString()// └─ StringBuilder.append(IN )// └─ StringBuilder.append(rightExpression) ← 此处才调用 rightExpression.toString()// └─ ParenthesedExpressionList.toString()// └─ PlainSelect.getStringList(this, true, true)// └─ 输出 (aaa, bbb, ccc)解决方案将ExpressionList替换为ParenthesedExpressionList// 修复前inExpression.setRightExpression(newExpressionList(ids.stream().map(StringValue::new).collect(Collectors.toList())));// 修复后inExpression.setRightExpression(newParenthesedExpressionList(ids.stream().map(StringValue::new).collect(Collectors.toList())));或者用Parenthesis包裹也可以两者 SQL 输出等价inExpression.setRightExpression(newParenthesis(newExpressionList(ids.stream().map(StringValue::new).collect(Collectors.toList()))));总结写法SQL 输出说明ExpressionList(...)IN a, b✗不带括号SQL 语法错误ParenthesedExpressionList(...)IN (a, b)✓内部硬编码useBracketstrueParenthesis(ExpressionList(...))IN (a, b)✓装饰器强制加括号注意此问题仅出现在 JSQLParser 4.x。若项目升级至 5.x建议重新验证 API 兼容性。

相关新闻

HarmonyOS应用开发实战:猫猫大作战-排序在下落算法中的关键作用

HarmonyOS应用开发实战:猫猫大作战-排序在下落算法中的关键作用

前言 在重力下落算法中,排序顺序至关重要——必须先处理底部的猫咪再处理顶部的,否则会出现“穿透“现象。Array.sort() 配合比较函数实现自定义排序。 本文以 GameEngine.updateCats() 中的 sort((a,b) > b.y - a.y) 为锚点,讲解排序在…

2026/10/11 20:38:29 阅读更多 →
私有化IM部署:能用只是开始,全栈信创适配等五道坎决定能否管好

私有化IM部署:能用只是开始,全栈信创适配等五道坎决定能否管好

私有化IM部署热背后:“能用”只是开始,全栈信创适配等五道坎决定能否“管好” 当越来越多的组织把即时通讯从SaaS拉回内网,私有化部署似乎成了解决数据焦虑的“标准答案”。但一个新的管理盲区正在浮现:许多团队以为只要聊天功能在…

2026/10/13 1:13:07 阅读更多 →
2026 年程序员找工作,为什么 AI 写出的代码反而成了简历的“硬伤”?

2026 年程序员找工作,为什么 AI 写出的代码反而成了简历的“硬伤”?

聊《一份看似完整的程序员就业方案,为什么投递时没效果?》之前,先说一句实在的:别急着背概念,先看它在真实项目里到底解决什么问题。摘要摘要:2026 年,AI 编程工具已不是新鲜事,但能…

2026/9/28 14:09:36 阅读更多 →

最新新闻

微服务服务发现从原理到落地实战指南

微服务服务发现从原理到落地实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 1:15:59 阅读更多 →
STM32F429 移植 LiteOS 实战:CubeMX 创建 HAL 库工程指南

STM32F429 移植 LiteOS 实战:CubeMX 创建 HAL 库工程指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 1:15:59 阅读更多 →
秒杀系统实战:从限流到库存扣减的高并发方案

秒杀系统实战:从限流到库存扣减的高并发方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 1:15:59 阅读更多 →
Nginx域名访问后台管理系统实战:从IP端口到HTTPS反向代理

Nginx域名访问后台管理系统实战:从IP端口到HTTPS反向代理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 1:15:59 阅读更多 →
嵌入式老兵转机器人:STM32、电机控制、Linux驱动三条高薪路线怎么选

嵌入式老兵转机器人:STM32、电机控制、Linux驱动三条高薪路线怎么选

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 1:15:59 阅读更多 →
模拟IC设计经验法则:RC估算、增益带宽与运放稳定性速查

模拟IC设计经验法则:RC估算、增益带宽与运放稳定性速查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 1:14:59 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →