{"record":{"id":"274f2e4a7ed89aa4","repo":"MyCATApache/Mycat-Server","slug":"joinkey-not-provided-tc-getjoinkey-inserts","errorCode":null,"errorMessage":"joinKey not provided :${tc.getJoinKey()},${insertStmt}","messagePattern":"joinKey not provided :(.+?),(.+?)","errorType":"exception","errorClass":"java.sql.SQLNonTransientException","httpStatus":null,"severity":"error","filePath":"src/main/java/io/mycat/route/util/RouterUtil.java","lineNumber":1931,"sourceCode":"\t\t\tMySqlInsertStatement insertStmt = (MySqlInsertStatement) stmt;\r\n\t\t\tString tableName = insertStmt.getTableName().getSimpleName().toUpperCase();\r\n\t\t\tfinal TableConfig tc = schema.getTables().get(tableName);\r\n\r\n\t\t\tif (null != tc && tc.isChildTable()) {\r\n\t\t\t\terFlag = true;\r\n\r\n\t\t\t\tString sql = insertStmt.toString();\r\n\r\n\t\t\t\tfinal RouteResultset rrs = new RouteResultset(sql, ServerParse.INSERT);\r\n\t\t\t\tString joinKey = tc.getJoinKey();\r\n\t\t\t\t//因为是Insert语句，用MySqlInsertStatement进行parse\r\n//\t\t\t\tMySqlInsertStatement insertStmt = (MySqlInsertStatement) (new MySqlStatementParser(origSQL)).parseInsert();\r\n\t\t\t\t//判断条件完整性，取得解析后语句列中的joinkey列的index\r\n\t\t\t\tint joinKeyIndex = getJoinKeyIndex(insertStmt.getColumns(), joinKey);\r\n\t\t\t\tif (joinKeyIndex == -1) {\r\n\t\t\t\t\tString inf = \"joinKey not provided :\" + tc.getJoinKey() + \",\" + insertStmt;\r\n\t\t\t\t\tLOGGER.warn(inf);\r\n\t\t\t\t\tthrow new SQLNonTransientException(inf);\r\n\t\t\t\t}\r\n\t\t\t\t//子表不支持批量插入\r\n\t\t\t\tif (isMultiInsert(insertStmt)) {\r\n\t\t\t\t\tString msg = \"ChildTable multi insert not provided\";\r\n\t\t\t\t\tLOGGER.warn(msg);\r\n\t\t\t\t\tthrow new SQLNonTransientException(msg);\r\n\t\t\t\t}\r\n\t\t\t\t//取得joinkey的值\r\n\t\t\t\tString joinKeyVal = insertStmt.getValues().getValues().get(joinKeyIndex).toString();\r\n\t\t\t\t//解决bug #938，当关联字段的值为char类型时，去掉前后\"'\"\r\n\t\t\t\tString realVal = joinKeyVal;\r\n\t\t\t\tif (joinKeyVal.startsWith(\"'\") && joinKeyVal.endsWith(\"'\") && joinKeyVal.length() > 2) {\r\n\t\t\t\t\trealVal = joinKeyVal.substring(1, joinKeyVal.length() - 1);\r\n\t\t\t\t}\r\n\r\n\t\t\t\t// try to route by ER parent partion key\r\n\t\t\t\t//如果是二级子表（父表不再有父表）,并且分片字段正好是joinkey字段，调用routeByERParentKey\r\n\t\t\t\tRouteResultset theRrs = RouterUtil.routeByERParentKey(sc, schema, ServerParse.INSERT, sql, rrs, tc, realVal);\r","sourceCodeStart":1913,"sourceCodeEnd":1949,"githubUrl":"https://github.com/MyCATApache/Mycat-Server/blob/65f8d8beb752f935752f2a0eec0ab017facab9ef/src/main/java/io/mycat/route/util/RouterUtil.java#L1913-L1949","documentation":"When inserting into a child (ER) table, Mycat requires the joinKey column (the FK referencing the parent's partition column) to be present in the INSERT column list, because routing needs its value. getJoinKeyIndex returns -1 when the parsed insert's columns don't include joinKey, and Mycat throws SQLNonTransientException naming the missing joinKey and the statement.","triggerScenarios":"INSERT into a childTable where tc.getJoinKey() is defined but the insert's column list omits that column (value supplied by trigger/default/ORM, or column name cased differently).","commonSituations":"ORMs (Hibernate/JPA) omitting columns with default values; relying on DB triggers or defaults to fill the FK; column-name case mismatch between SQL and schema.xml joinKey; explicit column lists missing the FK in bulk-load scripts.","solutions":["Add the joinKey column and its value explicitly to the INSERT column list","Verify joinKey in schema.xml matches the actual column name exactly (case included)","Configure the ORM to always include the FK column in inserts (no 'dynamic-insert' omission for that column)","If the column genuinely isn't needed, remove the childTable/parentKey/joinKey ER config so the table routes as a normal sharded table"],"exampleFix":"// before\nINSERT INTO order_detail(item) VALUES ('x');\n// after\nINSERT INTO order_detail(order_id, item) VALUES (42, 'x');","handlingStrategy":"validation","validationCode":"// Ensure the joinKey column is in the insert column list before executing\nSet<String> cols = insertColumns.stream().map(String::toLowerCase).collect(toSet());\nif (!cols.contains(joinKey.toLowerCase())) throw new IllegalStateException(\"INSERT must include joinKey \" + joinKey);","typeGuard":null,"tryCatchPattern":"catch (SQLNonTransientException e) { if (e.getMessage().startsWith(\"joinKey not provided\")) { /* add joinKey column and retry */ } throw e; }","preventionTips":["Always list child-table columns explicitly, including the FK/joinKey","Disable dynamic-insert omission for joinKey columns in the ORM","Keep joinKey case in SQL identical to schema.xml"],"tags":["er-table","insert","join-key","child-table","missing-column"],"backgroundTag":"missing-required-argument","analyzedSha":"65f8d8beb752f935752f2a0eec0ab017facab9ef","analyzedAt":"2026-09-11T00:12:21.696Z","contentChangedAt":"2026-09-11T00:12:21.696Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}