08-条件拼接规则

08-条件拼接规则:固定值与动态参数

元数据引擎最核心的一段逻辑:EA05的条件元数据怎么变成WHERE子句。固定值始终拼接、动态参数空则跳过、括号支持嵌套分组、like运算符走方言模糊------这些规则决定了每一条查询的行为。这篇逐行拆appendJoinAndWhere,并用真实元数据走一遍完整拼接。

文章目录

源码:browise-metadata/src/main/java/com/browise/ea/core/engine/SqlBuilder.java L414-447


一、一段50行的方法,管着所有查询的WHERE

java 复制代码
private List<String> appendJoinAndWhere(StringBuilder sql,
        List<EaConditionMeta> conditions, Map<String, String> params) {
    List<String> paramValues = new ArrayList<>();
    if (conditions == null) return paramValues;

    for (EaConditionMeta c : 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 {
            // ──────── 动态参数分支 ────────
            String paramName = c.getConditionValue();
            String val = params.get(paramName);
            if (val == null || val.isEmpty() || "null".equals(val))
                continue;                             // 三态空全跳过
            sql.append("\t ").append(c.getJoinOperator()).append(" ");
            if (c.isHasOpenParen()) sql.append(" ( ");
            String column = 有别名 ? alias.column : column;
            String op = c.getOperatorSymbol();
            sql.append(column).append(" ").append(op).append(" ");
            sql.append(typeConvertWhereParam(c.getDataType(), op));  // 占位符
            paramValues.add(val);
            if (c.isHasCloseParen()) sql.append(" ) ");
        }
    }
    return paramValues;
}

两个分支的行为差异全部源自一个字段:EAE013条件类型


二、固定值分支:元数据就是SQL片段

conditionType==1 时,conditionValue 的内容原样拼进SQL

复制代码
EA05配置: EAE013=1, EAE015=AND, EAE014="u.STATUS = '1'"
拼出:     AND u.STATUS = '1'

这不是漏洞吗?------值的来源是EA05元数据表,只有管理员能配置。这就是CODE_REVIEW修完后的边界:查询接口的固定值信任元数据(管理员权限),改删接口的固定值参数化(多一层保险)

固定值的典型用途:

场景 配置 效果
只查在职 u.STATUS='1' 界面上永远看不到停保人员
逻辑删除过滤 u.DELETED='0' 软删数据对查询不可见
多表关联 u.UNIT_ID=d.UNIT_ID 隐式JOIN的关联条件(替代EA04)
权限收窄 u.ORG_CODE LIKE '4601%' 只看本市数据(粗粒度)

多表关联条件也走固定值------这是wiserise时代EA04(表间关联)在browise的简化归宿:关联就是一种固定WHERE。


三、动态参数分支:三态空判定与跳过

java 复制代码
String val = params.get(paramName);
if (val == null || val.isEmpty() || "null".equals(val)) continue;

三种空全部跳过不是冗余------每一种都来自真实的调用方:

空的形态 来源
null 前端没传这个参数(Map.get返回null)
"" 前端传了但输入框空着
"null" JSON序列化把null变成了字符串"null"------某些前端HTTP库的老毛病

跳过而不是报错------查询条件是可选的。用户没填姓名筛选,就不该有这个WHERE。这也是"零代码CommonQuery"能工作的基础:界面渲染了8个查询条件,用户只填2个,另外6个条件静默消失。


四、运算符与占位符的类型转换

java 复制代码
sql.append(column).append(" ").append(op).append(" ");
sql.append(typeConvertWhereParam(c.getDataType(), op));
paramValues.add(val);

typeConvertWhereParam 按"运算符优先、类型其次"两级决策占位符形态:

java 复制代码
private String typeConvertWhereParam(String dataType, String operator) {
    if ("like".equals(operator))
        return dialect.whereLikeParam();        // Oracle: '%'||?||'%'  MySQL: CONCAT('%',?,'%')
    if ("2".equals(dataType))
        return dialect.whereDateParam();       // Oracle: to_date(?,'yyyy-mm-dd')
    if ("3".equals(dataType))
        return dialect.whereNumberParam();     // 通常就是?
    if ("4".equals(dataType))
        return dialect.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 = 等值
02 like 模糊(走whereLikeParam)
03/04 > / < 范围
05/06 >= / <= 闭区间
07 <> / != 排除
08-11 in类 预留扩展

五、括号分组:EAE016/EAE017

java 复制代码
if (c.isHasOpenParen())  sql.append(" ( ");
// ...本条件...
if (c.isHasCloseParen()) sql.append(" ) ");

配置"状态有效 且 (姓名含张 或 单位=XX局)":

运算符 连接符 左括号 右括号
1 u.STATUS = AND - - 固定 '1'
2 u.PSN_NAME like AND 1 - 动态 psnName
3 u.UNIT_ID = OR - 1 动态 unitId

拼出:

sql 复制代码
WHERE 1=1
  AND u.STATUS = '1'
  AND  ( u.PSN_NAME LIKE '%'||?||'%'
   OR u.UNIT_ID = ? )

注意括号只包住单个条件的前后------跨多条件的括号组要靠连续多行的开闭括号组合出来。这是wiserise传下来的模型,够用但配置时容易错配------设计器界面上会可视化预览来防错。


六、一次完整拼接实录

元数据配置(承接第05篇的示例):

复制代码
EA05三行(EAE001=SYS_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: ["张"]

第三行(动态,null)

复制代码
(continue跳过,什么都不拼)

最终BuiltSql

sql 复制代码
select
  u.PSN_ID, u.PSN_NAME, u.PSN_SEX
from
  SYS_USER u
where 1=1
  AND u.STATUS = '1'
  AND u.PSN_NAME LIKE '%'||?||'%'
-- params: ["张"]
-- 再由dialect.paginate包ROWNUM,追加minRow/maxRow两个参数

七、这套规则的边界

它不是通用查询构建器。 刻意不支持:

  • 子查询条件(in后面跟select)------走rawSql通道
  • OR连接的顶层条件组(首个条件不能是OR)------where 1=1 AND...的起点写死了AND链
  • 函数条件(UPPER(name) like ?)------列名只支持裸列

这些限制换来的是:条件元数据模型足够简单(一行一个条件),管理界面可以表格化配置,普通业务人员能维护。复杂需求有rawSql逃生舱------两个通道覆盖全部场景,中间没有灰色地带。


✅ 亮点:50行appendJoinAndWhere逐行拆解------固定值/动态参数两分支的行为差异、三态空判定的来源、like与日期的方言占位符、括号分组配置法,最后用完整示例走一遍拼接实录。适合实现动态查询的参考。扩展方向:第09篇拆8方言体系、第11篇拆rawSql通道的#{}替换。

相关推荐
2601_9548118243 分钟前
AI通识课跨设备联调难题:协议兼容层设计与教学联动架构优化
人工智能·python·架构
Scott9999HH44 分钟前
2026 年企业级生成式搜索引擎底层架构演进与生成式引擎优化系统推荐:从多路向量同步到全域自动化 GEO 中台的落地实战
搜索引擎·架构·自动化
码匠许师傅1 小时前
【C++ 面试真题】32. 聊聊 C++ 的原子变量与无锁编程
java·c++·面试
不会就选b1 小时前
Linux之线程池(一)
java·开发语言
zhangjw341 小时前
第43篇:微服务网关:Spring Cloud Gateway,统一接口入口
微服务·云原生·架构
未来之窗软件服务1 小时前
Web 技能的自主测试系统开好处发架构思维-东方仙盟
大数据·前端·架构·仙盟创梦ide·东方仙盟
todoitbo1 小时前
向量数据库不该成为新孤岛:KingbaseES 多模融合架构如何减少数据搬运
数据库·架构·国产数据库·kes
IT毕设实战小研1 小时前
基于大数据的二手车数据分析与预测
java·大数据·后端·爬虫·python·算法·课程设计
吠品1 小时前
OkHttp 多文件上传的一个实现
java·服务器·前端