08-条件拼接规则固定值与动态参数元数据引擎最核心的一段逻辑EA05的条件元数据怎么变成WHERE子句。固定值始终拼接、动态参数空则跳过、括号支持嵌套分组、like运算符走方言模糊——这些规则决定了每一条查询的行为。这篇逐行拆appendJoinAndWhere并用真实元数据走一遍完整拼接。文章目录08-条件拼接规则固定值与动态参数一、一段50行的方法管着所有查询的WHERE二、固定值分支元数据就是SQL片段三、动态参数分支三态空判定与跳过四、运算符与占位符的类型转换五、括号分组EAE016/EAE017六、一次完整拼接实录七、这套规则的边界源码browise-metadata/src/main/java/com/browise/ea/core/engine/SqlBuilder.javaL414-447一、一段50行的方法管着所有查询的WHEREprivateListStringappendJoinAndWhere(StringBuildersql,ListEaConditionMetaconditions,MapString,Stringparams){ListStringparamValuesnewArrayList();if(conditionsnull)returnparamValues;for(EaConditionMetac:conditions){if(c.getConditionType()1){// ──────── 固定值分支 ────────sql.append(\t ).append(c.getJoinOperator()).append( );if(c.isHasOpenParen())sql.append( ( );sql.append(c.getConditionValue());// 直接拼字面SQL片段if(c.isHasCloseParen())sql.append( ) );}else{// ──────── 动态参数分支 ────────StringparamNamec.getConditionValue();Stringvalparams.get(paramName);if(valnull||val.isEmpty()||null.equals(val))continue;// 三态空全跳过sql.append(\t ).append(c.getJoinOperator()).append( );if(c.isHasOpenParen())sql.append( ( );Stringcolumn有别名?alias.column:column;Stringopc.getOperatorSymbol();sql.append(column).append( ).append(op).append( );sql.append(typeConvertWhereParam(c.getDataType(),op));// 占位符paramValues.add(val);if(c.isHasCloseParen())sql.append( ) );}}returnparamValues;}两个分支的行为差异全部源自一个字段EAE013条件类型。二、固定值分支元数据就是SQL片段conditionType1 时conditionValue的内容原样拼进SQLEA05配置: EAE0131, EAE015AND, EAE014u.STATUS 1 拼出: AND u.STATUS 1这不是漏洞吗——值的来源是EA05元数据表只有管理员能配置。这就是CODE_REVIEW修完后的边界查询接口的固定值信任元数据管理员权限改删接口的固定值参数化多一层保险。固定值的典型用途场景配置效果只查在职u.STATUS1界面上永远看不到停保人员逻辑删除过滤u.DELETED0软删数据对查询不可见多表关联u.UNIT_IDd.UNIT_ID隐式JOIN的关联条件替代EA04权限收窄u.ORG_CODE LIKE 4601%只看本市数据粗粒度多表关联条件也走固定值——这是wiserise时代EA04表间关联在browise的简化归宿关联就是一种固定WHERE。三、动态参数分支三态空判定与跳过Stringvalparams.get(paramName);if(valnull||val.isEmpty()||null.equals(val))continue;三种空全部跳过不是冗余——每一种都来自真实的调用方空的形态来源null前端没传这个参数Map.get返回null前端传了但输入框空着nullJSON序列化把null变成了字符串null——某些前端HTTP库的老毛病跳过而不是报错——查询条件是可选的。用户没填姓名筛选就不该有这个WHERE。这也是零代码CommonQuery能工作的基础界面渲染了8个查询条件用户只填2个另外6个条件静默消失。四、运算符与占位符的类型转换sql.append(column).append( ).append(op).append( );sql.append(typeConvertWhereParam(c.getDataType(),op));paramValues.add(val);typeConvertWhereParam 按运算符优先、类型其次两级决策占位符形态privateStringtypeConvertWhereParam(StringdataType,Stringoperator){if(like.equals(operator))returndialect.whereLikeParam();// Oracle: %||?||% MySQL: CONCAT(%,?,%)if(2.equals(dataType))returndialect.whereDateParam();// Oracle: to_date(?,yyyy-mm-dd)if(3.equals(dataType))returndialect.whereNumberParam();// 通常就是?if(4.equals(dataType))returndialect.whereDateTimeParam();// to_date(?,yyyy-mm-dd hh24:mi:ss)return?;}like的占位符不是一个孤零零的?——Oracle的模糊匹配必须在SQL层做字符串拼接%||?||%。参数只传张SQL层负责包%。日期参数同理——前端传2026-01-01字符串Oracle的DATE列不能直接等值比较必须 to_date 包一层。参数值始终是纯字符串类型转换全部发生在占位符表达式里——这是字符串参数方言占位符协作的核心约定。EaConditionMeta.getOperatorSymbol() 的运算符映射EAE012符号说明01等值02like模糊走whereLikeParam03/04 / 范围05/06 / 闭区间07 / !排除08-11in类预留扩展五、括号分组EAE016/EAE017if(c.isHasOpenParen())sql.append( ( );// ...本条件...if(c.isHasCloseParen())sql.append( ) );配置状态有效 且 (姓名含张 或 单位XX局)行列运算符连接符左括号右括号值1u.STATUSAND--固定 ‘1’2u.PSN_NAMElikeAND1-动态 psnName3u.UNIT_IDOR-1动态 unitId拼出WHERE11ANDu.STATUS1AND(u.PSN_NAMELIKE%||?||%ORu.UNIT_ID?)注意括号只包住单个条件的前后——跨多条件的括号组要靠连续多行的开闭括号组合出来。这是wiserise传下来的模型够用但配置时容易错配——设计器界面上会可视化预览来防错。六、一次完整拼接实录元数据配置承接第05篇的示例EA05三行EAE001SYS_USER_s: 行1: u.STATUS AND 固定 1 行2: u.PSN_NAME like AND 动态 psnName 行3: u.UNIT_ID AND 动态 unitId调用query(SYS_USER_s, {psnName:张})unitId没传第一行固定AND u.STATUS 1第二行动态有值AND u.PSN_NAME LIKE %||?||% -- params: [张]第三行动态nullcontinue跳过什么都不拼最终BuiltSqlselectu.PSN_ID,u.PSN_NAME,u.PSN_SEXfromSYS_USER uwhere11ANDu.STATUS1ANDu.PSN_NAMELIKE%||?||%-- params: [张]-- 再由dialect.paginate包ROWNUM追加minRow/maxRow两个参数七、这套规则的边界它不是通用查询构建器。刻意不支持子查询条件in后面跟select——走rawSql通道OR连接的顶层条件组首个条件不能是OR——where 11 AND...的起点写死了AND链函数条件UPPER(name) like ?——列名只支持裸列这些限制换来的是条件元数据模型足够简单一行一个条件管理界面可以表格化配置普通业务人员能维护。复杂需求有rawSql逃生舱——两个通道覆盖全部场景中间没有灰色地带。✅ 亮点50行appendJoinAndWhere逐行拆解——固定值/动态参数两分支的行为差异、三态空判定的来源、like与日期的方言占位符、括号分组配置法最后用完整示例走一遍拼接实录。适合实现动态查询的参考。扩展方向第09篇拆8方言体系、第11篇拆rawSql通道的#{}替换。