hibernate/hibernate-orm · error · MappingException

Index {} already exists

Error message

Index {} already exists

What it means

Table.addIndex(Index) rejects registering a second index with the same name on one table. Index names must be unique per table (and in most databases per schema), so when the mapping layer or programmatic API tries to add an index whose name already exists in the table's index map, Hibernate throws this MappingException with the duplicated name.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/mapping/Table.java:469

			return index;
		}
		else {
			final var newIndex = new Index();
			newIndex.setName( indexName );
			newIndex.setTable( this );
			indexes.put( indexName, newIndex );
			return newIndex;
		}
	}

	public Index getIndex(String indexName) {
		return indexes.get( indexName );
	}

	public Index addIndex(Index index) {
		final var current =  indexes.get( index.getName() );
		if ( current != null ) {
			throw new MappingException( "Index " + index.getName() + " already exists" );
		}
		indexes.put( index.getName(), index );
		return index;
	}

	public UniqueKey addUniqueKey(UniqueKey uniqueKey) {
		final var current = uniqueKeys.get( uniqueKey.getName() );
		if ( current != null ) {
			throw new MappingException( "UniqueKey " + uniqueKey.getName() + " already exists" );
		}
		uniqueKeys.put( uniqueKey.getName(), uniqueKey );
		return uniqueKey;
	}

	/**
	 * Mark the given column unique and assign a name to the unique key.
	 * <p>
	 * This method does not add a {@link UniqueKey} to the table itself!

View on GitHub (pinned to fad1729dce)

Solutions

  1. Search all entities and hbm files mapping the table named in the message for the index name, and make each @Index name unique (e.g. prefix with table name: idx_orders_custid).
  2. If the duplication is intentional (same columns), keep exactly one declaration and delete the redundant one.
  3. In programmatic models, guard with table.getIndex(name) != null before table.addIndex(index).
  4. Adopt a naming convention (table_column_purpose) so hand-written index names cannot collide.

Example fix

// before
@Table(name = "orders", indexes = @Index(name = "idx_cust", columnList = "customer_id"))
public class Order { ... }
@Index(name = "idx_cust", columnList = "customer_email") // duplicate name, same table
private String email;

// after
@Index(name = "idx_orders_email", columnList = "customer_email")
private String email;
Defensive patterns

Strategy: validation

Validate before calling

// programmatic mapping: check before add
if (table.getIndex("idx_orders_cust") != null) {
    // reuse existing index or pick another name
} else {
    table.addIndex(newIndex);
}

Try / catch

try {
    metadata.buildMetadata().validate();
} catch (MappingException e) {
    if (e.getMessage().matches("Index .+ already exists")) {
        // duplicate @Index/<index> name on one table — dedupe names
    }
    throw e;
}

Prevention

When it happens

Trigger: Two @Index(name = "idx_x", columnList = "...") annotations resolving to the same table with the same name; hbm.xml <index name="x"/> appearing twice for one table; @Table(indexes = ...) plus an entity-level @Index with the same name; programmatic mapping calling table.addIndex(...) twice with equal names.

Common situations: Copy-pasted index annotations across entities mapped to one table (e.g. @Inheritance SINGLE_TABLE subclasses each adding indexes on the shared table); combining @Table(indexes=...) with a second redundant annotation after refactoring; XML and annotation sources merged for one table; schema-generation pipelines that union indexes from several mapping files.

Related errors


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