hibernate/hibernate-orm · error · SchemaManagementException

Index mismatch - `%s` on table `%s`

Error message

Index mismatch - `%s` on table `%s`

What it means

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.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaValidator.java:231

			var indicesMatch = true;
			assert index.getSelectables().size() == index.getColumnSpan();
			if ( index.getColumnSpan() != indexInformation.getIndexedColumns().size() ) {
				indicesMatch = false;
			}
			else {
				for ( int i = 0; i < index.getSelectables().size(); i++ ) {
					final Selectable column = index.getSelectables().get( i );
					final ColumnInformation columnInfo = indexInformation.getIndexedColumns().get( i );
					if ( !column.getText().equals( columnInfo.getColumnIdentifier().getText() ) ) {
						indicesMatch = false;
						break;
					}
				}
			}

			if ( !indicesMatch ) {
				throw new SchemaManagementException(
						String.format(
								ROOT,
								"Index mismatch - `%s` on table `%s`",
								name.render( dialect ),
								tableInformation.getName().render()
						)
				);
			}
		} );
	}

	private void validateUniqueKeys(
			Table table,
			TableInformation tableInformation,
			Metadata metadata,
			ExecutionOptions options,
			Dialect dialect) {
		var validationType = ConstraintValidationType.interpret( UNIQUE_KEY_VALIDATION, options.getConfigurationValues() );

View on GitHub (pinned to fad1729dce)

Solutions

  1. Recreate the index with exactly the columns and order the mapping declares: DROP INDEX <name>; CREATE INDEX <name> ON <table> (col1, col2);
  2. Or update @Index(columnList=...) to match the real index definition.
  3. If the difference is intentional, drop the mapped declaration or disable index validation (hibernate.tooling.schema.index_validation=NONE).

Example fix

-- before: existing index covers (customer_id) but the mapping declares (customer_id, created_at)
DROP INDEX orders_customer_idx;

-- after: recreate to match the mapping exactly, order included
CREATE INDEX orders_customer_idx ON orders (customer_id, created_at);
Defensive patterns

Strategy: validation

Validate before calling

// Before validate, compare declared column lists (order included) against JDBC index metadata
// expected: "customer_id,created_at" from @Index(columnList)
try (Connection c = dataSource.getConnection();
     ResultSet rs = c.getMetaData().getIndexInfo(null, null, "orders", false, false)) {
    Map<String, List<String>> byIndex = new HashMap<>();
    while (rs.next()) {
        String n = rs.getString("INDEX_NAME");
        if (n != null) byIndex.computeIfAbsent(n, k -> new ArrayList<>()).add(rs.getString("COLUMN_NAME"));
    }
    List<String> actual = byIndex.get("orders_customer_idx");
    if (!List.of("customer_id", "created_at").equals(actual)) {
        throw new IllegalStateException("orders_customer_idx column list/order mismatch: " + actual);
    }
}

Try / catch

try {
    new SchemaValidator().validate(metadata, serviceRegistry);
} catch (SchemaManagementException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("Index mismatch")) {
        // recreate the index with exactly the mapped columns/order, or update columnList to reality
    }
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22). Data as JSON: /api/errors/4d6ab80d106e5485. Report an issue: GitHub.