Wrapper 条件构造
Wrapper 把链式条件 API 编译成 SQL 片段和参数映射,调用方写的是领域条件,注入 SQL 时仍使用预编译参数而不是字符串拼接值。
先给答案:Wrapper 把链式条件 API 编译成 SQL 片段和参数映射,调用方写的是领域条件,注入 SQL 时…
Section titled “先给答案:Wrapper 把链式条件 API 编译成 SQL 片段和参数映射,调用方写的是领域条件,注入 SQL 时…”Wrapper 把链式条件 API 编译成 SQL 片段和参数映射,调用方写的是领域条件,注入 SQL 时仍使用预编译参数而不是字符串拼接值。 理解Wrapper 条件构造时,要先确认入口与状态归属,再跟踪控制流或数据流的推进顺序,最后落到对外可观察的结果。
主要失效边界集中在“Wrapper 跨线程复用、in 传空集合、apply 直接拼接”这些场景。它们破坏的是容量、顺序、并发或生命周期前提;排查时应先确认状态是否仍由正确对象持有,再核对推进条件和清理路径。
主要失效边界集中在“Wrapper 跨线程复用、in 传空集合、apply 直接拼接”这些场景。它们破坏的是容量、顺序、并发或生命周期前提;排查时应先确认状态是否仍由正确对象持有,再核对推进条件和清理路径。
eq("status", 1).like("name", "A") │ ▼MergeSegments + paramNameValuePairs │ ▼WHERE status = #{ew.paramNameValuePairs.MPGENVAL1}AbstractWrapper 保存实体、表达式片段、参数序列和参数值映射;addCondition 等方法只增加 SQL 结构和绑定参数:见 AbstractWrapper.java:50-125、AbstractWrapper.java:150-250、AbstractWrapper.java:300-420、AbstractWrapper.java:603-680。
Lambda wrapper 通过 SFunction 解析实体 getter,再借助 LambdaUtils 获得列名,避免手写字符串列名:见 LambdaQueryWrapper.java:30-100、AbstractLambdaWrapper.java:40-150、LambdaUtils.java:100-180。
条件最终由 MergeSegments#getSqlSegment 合并,并由 SqlScriptUtils#convertIf 等工具嵌入动态 SQL:见 MergeSegments.java:30-92、NormalSegmentList.java:25-110、SqlScriptUtils.java:35-105。
为什么参数值不能直接拼进 SQL
Section titled “为什么参数值不能直接拼进 SQL”替代方案:把 eq("name", value) 直接拼成 name = 'value'。
为什么不行:有注入风险,且无法复用 PreparedStatement;字符串、日期和特殊字符转义也会复杂化。
证据:Wrapper 将值放入 paramNameValuePairs,SQL 只生成参数占位符。
| 场景 | 现象 | 原因 | 规避 |
|---|---|---|---|
| Wrapper 跨线程复用 | 参数互相污染 | 内部 segment 和 map 可变 | 每次请求新建 |
in 传空集合 |
SQL 不合法或语义变化 | 条件生成需要特殊处理 | 先判断集合是否为空 |
apply 直接拼接 |
注入风险 | 绕过参数绑定 | 只用于受控模板并使用占位符 |
链式 DSL 的关键不是 fluent API,而是把结构和数据分离:结构进入 AST/片段,数据进入参数表。
面试锚点
- Wrapper 如何防止参数值直接拼 SQL?
- Lambda wrapper 如何得到列名?
- Wrapper 为什么不应该跨线程共享?