hibernate/hibernate-orm · error · AnnotationException

Table '${table}' has no column named '${column}' matching th

Error message

Table '${table}' has no column named '${column}' matching the column specified in '@Index'

What it means

While binding a natural-id unique key (and index-like column references), Hibernate resolves each specified column name to a logical column on the target table. If the logical name matches no mapped column of that table, the mapping cannot be built and AnnotationException is thrown at bootstrap. Note the lookup uses the logical column name, so physical naming strategies and @Column renames affect what must be written in the annotation.

Source

Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/NaturalIdBinder.java:78

			for ( Selectable selectable : property.getValue().getSelectables() ) {
				if ( selectable instanceof org.hibernate.mapping.Column column) {
					uniqueKey.addColumn( tableColumn( column, table, collector ) );
				}
			}
		}
		else {
			for ( var column : columns.getColumns() ) {
				uniqueKey.addColumn( tableColumn( column.getMappingColumn(), table, collector ) );
			}
		}
	}

	private static org.hibernate.mapping.Column tableColumn(
			org.hibernate.mapping.Column column, Table table, InFlightMetadataCollector collector) {
		final String columnName = collector.getLogicalColumnName( table, column.getQuotedName() );
		final var tableColumn = table.getColumn( collector, columnName );
		if ( tableColumn == null ) {
			throw new AnnotationException(
					"Table '" + table.getName() + "' has no column named '" + columnName
							+ "' matching the column specified in '@Index'"
			);
		}
		return tableColumn;
	}

	private static class NaturalIdNameSource implements ImplicitUniqueKeyNameSource {
		private final Table table;
		private final MetadataBuildingContext context;

		NaturalIdNameSource(Table table, MetadataBuildingContext context) {
			this.table = table;
			this.context = context;
		}

		@Override
		public Identifier getTableName() {

View on GitHub (pinned to fad1729dce)

Solutions

  1. Compare the names in the index/unique-key annotation against the actual @Column(name=...) values (or default property names) of the entity and fix the typo.
  2. Remember the lookup uses LOGICAL column names: reference the name from the mapping (e.g. @Column(name="user_email") => "user_email"), not a strategy-transformed physical name.
  3. If a naming strategy transforms names, either align the annotation values with the strategy output or make the mapping names explicit with @Column so both sides agree.
  4. Enable DEBUG logging for org.hibernate.boot.model to see the logical name Hibernate resolved for each column.

Example fix

// before: column was renamed in the mapping but not in the index
@Column(name = "user_email")
String email;
@TableIndex(name = "ix_email", columnNames = "email")  // no such logical column

// after
@TableIndex(name = "ix_email", columnNames = "user_email")
Defensive patterns

Strategy: validation

Validate before calling

// Before startup, cross-check index/natural-id column names against mapped @Column names
Map<String, Set<String>> tableColumns = readMappedColumnsPerTable(annotatedClasses); // from @Table + @Column
for (Class<?> entity : annotatedClasses) {
    TableIndex idx = entity.getAnnotation(TableIndex.class);
    if (idx != null) {
        Set<String> mapped = tableColumns.getOrDefault(tableOf(entity), Set.of());
        for (String c : idx.columnNames()) {
            if (!mapped.contains(c)) throw new IllegalStateException("Index column '" + c + "' not mapped on " + entity.getName());
        }
    }
}

Try / catch

try {
    SessionFactory sf = cfg.buildSessionFactory();
} catch (AnnotationException e) {
    // mapping errors: report and fail startup; retrying cannot help
    throw new IllegalStateException("Mapping invalid: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: A @NaturalId / index column specification referencing a column name that does not exist among the entity's mapped columns: misspelled name in columnNames, a @Column(name=...) rename that the index entry was not updated to follow, quoting differences, or an implicit naming strategy that produces a different logical name than the one written in the annotation.

Common situations: Renaming a column in the entity mapping but not in the accompanying index/unique-key annotation; switching ImplicitNamingStrategy or PhysicalNamingStrategy (e.g. Spring's CamelCaseToUnderscoresNamingStrategy) after annotations were written; hand-written column names that assume the DB schema names rather than logical mapping names; migrations between Hibernate versions with changed default naming.

Related errors


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