{"record":{"id":"cc00a0aed490b135","repo":"mybatis/mybatis-3","slug":"too-many-keys-are-generated-there-are-only-d-tar","errorCode":null,"errorMessage":"Too many keys are generated. There are only %d target object(s). A 'Map' passed as the sole parameter is treated as a single target object, so keys generated by a multi-row insert cannot be assigned to a collection nested in the 'Map'. Pass the collection itself as the sole parameter, or declare the parameters with @Param and prefix 'keyProperty' with the parameter name (e.g. 'list.id').","messagePattern":"Too many keys are generated\\. There are only (.+?) target object\\(s\\)\\. A 'Map' passed as the sole parameter is treated as a single target object, so keys generated by a multi-row insert cannot be assigned to a collection nested in the 'Map'\\. Pass the collection itself as the sole parameter, or declare the parameters with @Param and prefix 'keyProperty' with the parameter name \\(e\\.g\\. 'list\\.id'\\)\\.","errorType":"exception","errorClass":"ExecutorException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java","lineNumber":126,"sourceCode":"      // Single param without @Param\n      assignKeysToParam(configuration, rs, rsmd, keyProperties, parameter);\n    }\n  }\n\n  private void assignKeysToParam(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd,\n      String[] keyProperties, Object parameter) throws SQLException {\n    Collection<?> params = collectionize(parameter);\n    if (params.isEmpty()) {\n      return;\n    }\n    List<KeyAssigner> assignerList = new ArrayList<>();\n    for (int i = 0; i < keyProperties.length; i++) {\n      assignerList.add(new KeyAssigner(configuration, rsmd, i + 1, null, keyProperties[i]));\n    }\n    Iterator<?> iterator = params.iterator();\n    while (rs.next()) {\n      if (!iterator.hasNext()) {\n        throw new ExecutorException(\n            String.format(parameter instanceof Map ? MSG_TOO_MANY_KEYS_FOR_MAP : MSG_TOO_MANY_KEYS, params.size()));\n      }\n      Object param = iterator.next();\n      assignerList.forEach(x -> x.assign(rs, param));\n    }\n  }\n\n  private void assignKeysToParamMapList(Configuration configuration, ResultSet rs, ResultSetMetaData rsmd,\n      String[] keyProperties, ArrayList<ParamMap<?>> paramMapList) throws SQLException {\n    Iterator<ParamMap<?>> iterator = paramMapList.iterator();\n    List<KeyAssigner> assignerList = new ArrayList<>();\n    long counter = 0;\n    while (rs.next()) {\n      if (!iterator.hasNext()) {\n        throw new ExecutorException(String.format(MSG_TOO_MANY_KEYS, counter));\n      }\n      ParamMap<?> paramMap = iterator.next();\n      if (assignerList.isEmpty()) {","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/mybatis/mybatis-3/blob/008069adb1b089579b5dcba87ee591908b263274/src/main/java/org/apache/ibatis/executor/keygen/Jdbc3KeyGenerator.java#L108-L144","documentation":"During a multi-row insert (single insert statement with a collection parameter), Jdbc3KeyGenerator iterates the generated-keys ResultSet side by side with the parameter objects. When a java.util.Map is the sole parameter, MyBatis treats the whole Map as ONE target object, so if the insert generated several keys (one per row) there are more keys than targets and this ExecutorException is thrown, explaining that a Map cannot receive per-row keys.","triggerScenarios":"A multi-row INSERT ... VALUES (row1),(row2) with useGeneratedKeys where the single parameter is a Map (e.g. @Param map or a plain HashMap) that merely CONTAINS the collection (e.g. map.put(\"list\", items)) instead of the collection being the parameter itself.","commonSituations":"Wrapping a list in a Map to pass extra data (map.put(\"items\", list)) and still expecting each generated key to land inside map.items[i].id.","solutions":["Pass the collection itself as the sole parameter: List<User> insert(List<User> users)","Or declare parameters with @Param and prefix keyProperty with the parameter name: keyProperty=\"list.id\"","Do not use a bare Map as the sole parameter of a multi-row insert that needs generated keys assigned"],"exampleFix":"// before\nMap<String,Object> m = Map.of(\"list\", users);\nmapper.insert(m); // with keyProperty=\"id\" -> exception\n\n// after\nint insert(@Param(\"list\") List<User> users); // keyProperty=\"list.id\"","handlingStrategy":"validation","validationCode":"// Guard before a multi-row insert with generated keys\nif (params instanceof Map && !(params instanceof List)) {\n  throw new IllegalArgumentException(\"Pass the collection itself or use @Param + 'keyProperty=list.id'\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never use a bare Map as the sole parameter for multi-row inserts needing generated keys","Standardize on @Param(\"list\") + keyProperty=\"list.id\" for batch inserts"],"tags":["generated-keys","batch-insert","map-parameter"],"backgroundTag":null,"analyzedSha":"008069adb1b089579b5dcba87ee591908b263274","analyzedAt":"2026-08-14T13:07:10.264Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}