{"record":{"id":"cce0db4f0f250aa3","repo":"mybatis/mybatis-3","slug":"mappedstatementid-batch-index-index-faile","errorCode":null,"errorMessage":"${mappedStatementId} (batch index #${index}) failed. ${priorCount} prior sub executor(s) completed successfully, but will be rolled back.","messagePattern":"(.+?) \\(batch index #(.+?)\\) failed\\. (.+?) prior sub executor\\(s\\) completed successfully, but will be rolled back\\.","errorType":"exception","errorClass":"BatchExecutorException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/apache/ibatis/executor/BatchExecutor.java","lineNumber":147,"sourceCode":"          if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) {\n            Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator;\n            jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);\n          } else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { // issue #141\n            for (Object parameter : parameterObjects) {\n              keyGenerator.processAfter(this, ms, stmt, parameter);\n            }\n          }\n          // Close statement to close cursor #1109\n          closeStatement(stmt);\n        } catch (BatchUpdateException e) {\n          StringBuilder message = new StringBuilder();\n          message.append(batchResult.getMappedStatement().getId()).append(\" (batch index #\").append(i + 1).append(\")\")\n              .append(\" failed.\");\n          if (i > 0) {\n            message.append(\" \").append(i)\n                .append(\" prior sub executor(s) completed successfully, but will be rolled back.\");\n          }\n          throw new BatchExecutorException(message.toString(), e, results, batchResult);\n        }\n        results.add(batchResult);\n      }\n      return results;\n    } finally {\n      for (Statement stmt : statementList) {\n        closeStatement(stmt);\n      }\n      currentSql = null;\n      statementList.clear();\n      batchResultList.clear();\n    }\n  }\n\n}\n","sourceCodeStart":129,"sourceCodeEnd":163,"githubUrl":"https://github.com/mybatis/mybatis-3/blob/008069adb1b089579b5dcba87ee591908b263274/src/main/java/org/apache/ibatis/executor/BatchExecutor.java#L129-L163","documentation":"BatchExecutor.doFlushStatements() executes each batched Statement via executeBatch(). If the driver raises BatchUpdateException, MyBatis builds a message '<statementId> (batch index #N) failed. M prior sub executor(s) completed successfully, but will be rolled back.' and throws BatchExecutorException carrying the BatchUpdateException, the list of successful BatchResults so far, and the failing BatchResult. Batch index is 1-based; 'prior sub executor(s)' counts earlier statements in the same flush.","triggerScenarios":"ExecutorType.BATCH session with multiple statements/flushes; one batch member violates a constraint (duplicate key, FK, NOT NULL, data too long) or times out; driver rejects a batched statement; mixing incompatible statements in one batch; calling flushStatements()/commit when one of the accumulated batches is already broken.","commonSituations":"Bulk import scripts where a single bad row fails the whole batch; duplicate-key on upsert-less inserts; batching heterogeneous statements where a later one fails, rolling back earlier successful ones (surprising full-batch rollback); MySQL/Oracle driver differences in BatchUpdateException reporting; exceeding max_allowed_packet with huge batches.","solutions":["Read the BatchExecutorException: getBatchUpdateException() holds the driver error (SQLState/error code) identifying the failing row; the message names the mapped statement and 1-based batch index","Fix the data/constraint issue it points to (unique key conflict, null in NOT NULL column, oversized values)","Batch smaller and flush periodically so failures affect fewer rows and are easier to locate","Use ON DUPLICATE KEY UPDATE / MERGE / ON CONFLICT upserts to tolerate duplicates","For per-row error isolation, fall back to non-batched execution or partition the batch to identify the offending row"],"exampleFix":"// before\ntry (SqlSession s = factory.openSession(ExecutorType.BATCH, false)) {\n  UserMapper m = s.getMapper(UserMapper.class);\n  users.forEach(m::insert);\n  s.flushStatements(); // one bad row -> BatchExecutorException, all rolled back\n  s.commit();\n}\n\n// after: flush in chunks and use upsert to tolerate conflicts\ntry (SqlSession s = factory.openSession(ExecutorType.BATCH, false)) {\n  UserMapper m = s.getMapper(UserMapper.class);\n  int i = 0;\n  for (User u : users) {\n    m.upsert(u); // INSERT ... ON DUPLICATE KEY UPDATE\n    if (++i % 500 == 0) s.flushStatements();\n  }\n  s.flushStatements();\n  s.commit();\n}","handlingStrategy":"try-catch","validationCode":"// Pre-validate data likely to violate constraints before batching:\nSet<String> existing = new HashSet<>(mapper.findKeys(keys));\nList<User> insertable = users.stream()\n    .filter(u -> !existing.contains(u.key()))\n    .collect(Collectors.toList());","typeGuard":null,"tryCatchPattern":"try {\n  sqlSession.flushStatements();\n} catch (BatchExecutorException bee) {\n  int failingIndex = bee.getBatchUpdateException() != null ? -1 : -1;\n  // bee.getMessage() names statement + 1-based batch index;\n  // bee.getBatchUpdateException() carries SQLState/vendor code for the bad row;\n  // earlier successful statements are rolled back — session is unusable, open a new one.\n  log.error(\"Batch failed at index {}: {}\", bee.getMessage(),\n      bee.getBatchUpdateException() == null ? \"\" : bee.getBatchUpdateException().getMessage());\n  throw bee;\n}","preventionTips":["Flush every N rows so failures are cheap and localized","Clean/dedupe data before import; prefer upsert statements for idempotent loads","Log the mapped statement id and batch index from BatchExecutorException to pinpoint the row"],"tags":["mybatis","batch","sql-error","rollback","constraint-violation"],"backgroundTag":null,"analyzedSha":"008069adb1b089579b5dcba87ee591908b263274","analyzedAt":"2026-08-14T13:07:10.264Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}