{"record":{"id":"4d6ab80d106e5485","repo":"hibernate/hibernate-orm","slug":"index-mismatch-s-on-table-s","errorCode":null,"errorMessage":"Index mismatch - `%s` on table `%s`","messagePattern":"Index mismatch - `(.+?)` on table `(.+?)`","errorType":"exception","errorClass":"SchemaManagementException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaValidator.java","lineNumber":231,"sourceCode":"\n\t\t\tvar indicesMatch = true;\n\t\t\tassert index.getSelectables().size() == index.getColumnSpan();\n\t\t\tif ( index.getColumnSpan() != indexInformation.getIndexedColumns().size() ) {\n\t\t\t\tindicesMatch = false;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tfor ( int i = 0; i < index.getSelectables().size(); i++ ) {\n\t\t\t\t\tfinal Selectable column = index.getSelectables().get( i );\n\t\t\t\t\tfinal ColumnInformation columnInfo = indexInformation.getIndexedColumns().get( i );\n\t\t\t\t\tif ( !column.getText().equals( columnInfo.getColumnIdentifier().getText() ) ) {\n\t\t\t\t\t\tindicesMatch = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif ( !indicesMatch ) {\n\t\t\t\tthrow new SchemaManagementException(\n\t\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\tROOT,\n\t\t\t\t\t\t\t\t\"Index mismatch - `%s` on table `%s`\",\n\t\t\t\t\t\t\t\tname.render( dialect ),\n\t\t\t\t\t\t\t\ttableInformation.getName().render()\n\t\t\t\t\t\t)\n\t\t\t\t);\n\t\t\t}\n\t\t} );\n\t}\n\n\tprivate void validateUniqueKeys(\n\t\t\tTable table,\n\t\t\tTableInformation tableInformation,\n\t\t\tMetadata metadata,\n\t\t\tExecutionOptions options,\n\t\t\tDialect dialect) {\n\t\tvar validationType = ConstraintValidationType.interpret( UNIQUE_KEY_VALIDATION, options.getConfigurationValues() );","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaValidator.java#L213-L249","documentation":"Index validation found an index with the declared name, but its column list disagrees with the mapping: a different column count, or the i-th indexed column's identifier differs from the i-th selectable of the mapped index (the comparison is position-sensitive, in both directions). Hibernate requires the mapped and physical index to cover the same columns in the same order.","triggerScenarios":"validate with hibernate.tooling.schema.index_validation=NAMED/ALL where an existing index covers different columns or a different order than @Index(columnList = \"a, b\") declares: the migration created the index on (b, a); a DBA later replaced a single-column index with a composite one; a column was renamed in the DB but the mapping kept the old name.","commonSituations":"Hand-tuned production indexes diverging from entity declarations; performance work adding leading columns to indexes without updating mappings; index definitions copied between MySQL and PostgreSQL exports with reordered columns.","solutions":["Recreate the index with exactly the columns and order the mapping declares: DROP INDEX <name>; CREATE INDEX <name> ON <table> (col1, col2);","Or update @Index(columnList=...) to match the real index definition.","If the difference is intentional, drop the mapped declaration or disable index validation (hibernate.tooling.schema.index_validation=NONE)."],"exampleFix":"-- before: existing index covers (customer_id) but the mapping declares (customer_id, created_at)\nDROP INDEX orders_customer_idx;\n\n-- after: recreate to match the mapping exactly, order included\nCREATE INDEX orders_customer_idx ON orders (customer_id, created_at);","handlingStrategy":"validation","validationCode":"// Before validate, compare declared column lists (order included) against JDBC index metadata\n// expected: \"customer_id,created_at\" from @Index(columnList)\ntry (Connection c = dataSource.getConnection();\n     ResultSet rs = c.getMetaData().getIndexInfo(null, null, \"orders\", false, false)) {\n    Map<String, List<String>> byIndex = new HashMap<>();\n    while (rs.next()) {\n        String n = rs.getString(\"INDEX_NAME\");\n        if (n != null) byIndex.computeIfAbsent(n, k -> new ArrayList<>()).add(rs.getString(\"COLUMN_NAME\"));\n    }\n    List<String> actual = byIndex.get(\"orders_customer_idx\");\n    if (!List.of(\"customer_id\", \"created_at\").equals(actual)) {\n        throw new IllegalStateException(\"orders_customer_idx column list/order mismatch: \" + actual);\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    new SchemaValidator().validate(metadata, serviceRegistry);\n} catch (SchemaManagementException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Index mismatch\")) {\n        // recreate the index with exactly the mapped columns/order, or update columnList to reality\n    }\n    throw e;\n}","preventionTips":["Treat index column order as part of the API: never reorder columns without a migration and mapping update","When DBAs tune indexes, sync the @Index declaration in the same change set","Keep index validation enabled in CI so divergence is caught at build time"],"tags":["hibernate","schema-validation","index","hbm2ddl","ddl-migration"],"backgroundTag":"index-mismatch","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}