{"record":{"id":"d5041a22cf7b3263","repo":"Tencent/APIJSON","slug":"list-join-join-onlist-0-getkey-nul","errorCode":null,"errorMessage":"服务器内部错误，List<Join> 中 Join.onList[0].getKey() = null！","messagePattern":"服务器内部错误，List<Join> 中 Join\\.onList\\[0\\]\\.getKey\\(\\) = null！","errorType":"exception","errorClass":"NullPointerException","httpStatus":500,"severity":"critical","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java","lineNumber":786,"sourceCode":"\t\t\t\tSQLConfig<T, M, L> cc = join.getCacheConfig(); //这里用config改了getSQL后再还原很麻烦，所以提前给一个config2更好\r\n\t\t\t\tif (cc == null) {\r\n\t\t\t\t\tif (Log.DEBUG) {\r\n\t\t\t\t\t\tthrow new NullPointerException(\"服务器内部错误, executeAppJoin cc == null ! 导致不能缓存 @ APP JOIN 的副表数据！\");\r\n\t\t\t\t\t}\r\n\t\t\t\t\tcontinue;\r\n\t\t\t\t}\r\n\r\n\t\t\t\tSQLConfig<T, M, L> jc = join.getJoinConfig();\r\n\r\n\t\t\t\tList<On> onList = join.getOnList();\r\n\t\t\t\tOn on = onList == null || onList.isEmpty() ? null : onList.get(0);  // APP JOIN 应该有且只有一个 ON 条件\r\n\t\t\t\tString originKey = on == null ? null : on.getOriginKey();\r\n\t\t\t\tif (originKey == null) {\r\n\t\t\t\t\tthrow new NullPointerException(\"服务器内部错误，List<Join> 中 Join.onList[0\" + (on == null ? \"] = null！\" : \".getOriginKey() = null！\"));\r\n\t\t\t\t}\r\n\t\t\t\tString key = on.getKey();\r\n\t\t\t\tif (key == null) {\r\n\t\t\t\t\tthrow new NullPointerException(\"服务器内部错误，List<Join> 中 Join.onList[0\" + (on == null ? \"] = null！\" : \".getKey() = null！\"));\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// 取出 \"id@\": \"@/User/userId\" 中所有 userId 的值\r\n\t\t\t\tList<Object> targetValueList = new ArrayList<>();\r\n\r\n\t\t\t\tfor (int i = 0; i < resultList.size(); i++) {\r\n\t\t\t\t\tM mainTable = resultList.get(i);\r\n\t\t\t\t\tObject targetValue = mainTable == null ? null : mainTable.get(on.getTargetKey());\r\n\r\n\t\t\t\t\tif (targetValue != null && targetValueList.contains(targetValue) == false) {\r\n\t\t\t\t\t\ttargetValueList.add(targetValue);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\r\n\t\t\t\tif (targetValueList.isEmpty() && config.isExplain() == false) {\r\n\t\t\t\t\tthrow new NotExistException(\"targetValueList.isEmpty() && config.isExplain() == false\");\r\n\t\t\t\t}\r\n\r","sourceCodeStart":768,"sourceCodeEnd":804,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractSQLExecutor.java#L768-L804","documentation":"Internal NullPointerException from executeAppJoin: the first On has an originKey but getKey() returns null. The key names the main-table column to pull target values from (e.g. userId in \"userId@/User/id\"); a null key makes the subsequent mainTable.get(on.getTargetKey()) / mapping logic impossible, so execution aborts.","triggerScenarios":"On objects built without setKey, or a joiner string whose key portion before '@' is empty (\"/User/id\" as the relation segment).","commonSituations":"Programmatic On construction; malformed joiner strings missing the leading column name; extension code copying On objects field-by-field and skipping key.","solutions":["Provide the full relation including the local column: \"MyTable/userId@/User/id\".","When constructing On in code, always setKey with the local column name.","Validate joiner strings with a regex like [A-Za-z0-9_$]+/[^/@]+@/[^/@]+/[^/@]+ before use."],"exampleFix":"// before\n\"join\": {\"/User/id\": \"@ APP\"}\n// after\n\"join\": {\"Comment/userId@/User/id\": \"@ APP\"}","handlingStrategy":"try-catch","validationCode":"if (on != null && on.getKey() == null) throw new IllegalStateException('APP JOIN On missing local key');","typeGuard":null,"tryCatchPattern":"catch (NullPointerException npe) { if (String(npe.getMessage()).contains(\"Join.onList[0].getKey() = null\")) { throw new BadRequestException('join key must include local column before @'); } }","preventionTips":["Include the local column name before '@' in every join relation.","Reject join keys starting with '/' client-side."],"tags":["apijson","app-join","internal-error","null-pointer"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}