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
- 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.
- 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.
- 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.
- 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
- Update index/natural-id column strings whenever a @Column name changes
- Reference logical mapping names, not naming-strategy-transformed physical names
- Add a startup smoke test that builds the SessionFactory in CI to catch mapping drift
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
- Property '${property}' belongs to an entity subclass and may
- AttributeConverter class [%s] registered multiple times
- Duplicate named query '%s'
- Duplicate named stored procedure '{}'
- Duplicate SQL result set mapping '{}'
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/0988bcfdf79031a6.
Report an issue: GitHub.