{"record":{"id":"4f1f277d9e2f92f1","repo":"Tencent/APIJSON","slug":"join-value-value-key-key-on-getkey","errorCode":null,"errorMessage":"join:value 中 value 的 key@ 中 key 值 ${on.getKey()} 不合法！必须满足英文单词变量名格式！","messagePattern":"join:value 中 value 的 key@ 中 key 值 (.+?) 不合法！必须满足英文单词变量名格式！","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractParser.java","lineNumber":1859,"sourceCode":"\t\t\t\t}\n\n\t\t\t\t//对引用的JSONObject添加条件\n\t\t\t\tMap<String, Object> targetObj;\n\t\t\t\ttry {\n\t\t\t\t\ttargetObj = JSON.get(request, targetTableKey);\n\t\t\t\t}\n\t\t\t\tcatch (Exception e2) {\n\t\t\t\t\tthrow new IllegalArgumentException(e.getKey() + \":'/targetTable/targetKey' 中路径对应的 '\" + targetTableKey + \"':value 中 value 类型不合法！必须是 {} 这种 Map<String, Object> 格式！\" + e2.getMessage());\n\t\t\t\t}\n\n\t\t\t\tif (targetObj == null) {\n\t\t\t\t\tthrow new IllegalArgumentException(e.getKey() + \":'/targetTable/targetKey' 中路径对应的对象 '\" + targetTableKey + \"':{} 不存在或值为 null ！必须是 {} 这种 Map<String, Object> 格式！\");\n\t\t\t\t}\n\n\t\t\t\tJoin.On on = new Join.On();\n\t\t\t\ton.setKeyAndType(j.getJoinType(), j.getTable(), originKey);\n\t\t\t\tif (StringUtil.isName(on.getKey()) == false) {\n\t\t\t\t\tthrow new IllegalArgumentException(apijson.JSONRequest.KEY_JOIN + \":value 中 value 的 key@ 中 key 值 \" + on.getKey() + \" 不合法！必须满足英文单词变量名格式！\");\n\t\t\t\t}\n\n\t\t\t\ton.setOriginKey(originKey);\n\t\t\t\ton.setOriginValue((String) refEntry.getValue());\n\t\t\t\ton.setTargetTableKey(targetTableKey);\n\t\t\t\ton.setTargetTable(targetTable);\n\t\t\t\ton.setTargetAlias(targetAlias);\n\t\t\t\ton.setTargetKey(targetKey);\n\n\t\t\t\tonList.add(on);\n\t\t\t}\n\n\t\t\tj.setOnList(onList);\n\n\t\t\tjoinList.add(j);\n\t\t\t//\t\t\tonList.add(table + \".\" + key + \" = \" + targetTable + \".\" + targetKey); // ON User.id = Moment.userId\n\n\t\t\t// 保证和 SQLExcecutor 缓存的 Config 里 where 顺序一致，生成的 SQL 也就一致 <<<<<<<<<","sourceCodeStart":1841,"sourceCodeEnd":1877,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractParser.java#L1841-L1877","documentation":"Join.On.setKeyAndType strips the join-type prefix from the ON originKey to obtain the bare key; that key must satisfy StringUtil.isName. This IllegalArgumentException fires when the resulting key is not a valid identifier — e.g. the originKey carried type markers or characters that do not reduce to a clean name.","triggerScenarios":"Origin keys like 'to@id@' (embedded '@'), 'key1|key2@' (combine-style operator inside), 'user id@' (space) on the joined table, after setKeyAndType leaves an invalid residue.","commonSituations":"Reusing @combine condition syntax ('|', '&') inside join ON keys; keys assembled from user input with punctuation; double '@' suffixes from template concatenation.","solutions":["Use a plain identifier for the ON key inside the joined table object: 'toId@': '/User/id'","Do not embed combine operators or extra '@' characters in join ON keys"],"exampleFix":"// before\n{ 'join': '@/Comment', 'Comment': { 'to@id@': '/User/id' } }\n// after\n{ 'join': '@/Comment', 'Comment': { 'toId@': '/User/id' } }","handlingStrategy":"validation","validationCode":"const NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;\nfunction checkOnOriginKeys(req, joinValue) {\n  const paths = typeof joinValue === 'string' ? [joinValue] : Object.keys(joinValue);\n  for (const p of paths) {\n    const rest = p.substring(p.indexOf('/') + 1);\n    const tblKey = rest.includes('/') ? rest.substring(0, rest.lastIndexOf('/')) : rest;\n    const tbl = req[tblKey.split(':')[0]];\n    for (const k of Object.keys(tbl || {})) {\n      if (k.endsWith('@') && !NAME_RE.test(k.slice(0, -1))) {\n        return `ON key '${k}' in ${tblKey} reduces to an invalid identifier`;\n      }\n    }\n  }\n  return null;\n}","typeGuard":"const isCleanOnKey = (k) => /^[A-Za-z_][A-Za-z0-9_]*@$/.test(k);","tryCatchPattern":"Catch IllegalArgumentException with 'key@ 中 key 值'; rename the ON key to a plain identifier.","preventionTips":["Never embed '@', '|', '&', spaces, or '()' in join ON key names","Keep @combine syntax out of join ON keys — it belongs on top-level condition keys"],"tags":["apijson","join","identifier","on-condition"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}