hibernate/hibernate-orm · error · AnnotationException

Property '${path}' specifies ${columnCount} '@AttributeOverr

Error message

Property '${path}' specifies ${columnCount} '@AttributeOverride's but the overridden property has ${overriddenColumnCount} columns (every column must have exactly one '@AttributeOverride')

What it means

When @AttributeOverride is applied to a property, AnnotatedColumn.overrideColumns compares the number of supplied jakarta.persistence.Column entries with the number of columns the overridden property actually maps. A multi-column mapping (composite basic types, embeddables mapping several columns) needs exactly one @Column override per mapped column; any other count throws this AnnotationException. (A code comment notes PersistentClass.validate() often fires first with a vaguer message.)

Source

Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java:768

						actualColumns,
						fractionalSeconds
				);
			}
		}
	}

	private static jakarta.persistence.Column[] overrideColumns(
			jakarta.persistence.Column[] columns,
			PropertyHolder propertyHolder,
			PropertyData inferredData ) {
		final String path = getPath( propertyHolder, inferredData );
		final var overriddenCols = propertyHolder.getOverriddenColumn( path );
		if ( overriddenCols != null ) {
			//check for overridden first
			if ( columns != null && overriddenCols.length != columns.length ) {
				//TODO: unfortunately, we never actually see this nice error message, since
				//      PersistentClass.validate() gets called first and produces a worse message
				throw new AnnotationException( "Property '" + path
						+ "' specifies " + columns.length
						+ " '@AttributeOverride's but the overridden property has " + overriddenCols.length
						+ " columns (every column must have exactly one '@AttributeOverride')" );
			}
			if ( BOOT_LOGGER.isTraceEnabled() ) {
				BOOT_LOGGER.columnMappingOverridden( inferredData.getPropertyName() );
			}
			return isEmpty( overriddenCols ) ? null : overriddenCols;
		}
		else {
			return columns;
		}
	}

	private static AnnotatedColumns buildExplicitColumns(
//			Comment comment,
			PropertyHolder propertyHolder,
			PropertyData inferredData,

View on GitHub (pinned to fad1729dce)

Solutions

  1. Count the columns the overridden property maps and supply exactly that many @Column entries in @AttributeOverrides
  2. If you only meant to rename one column of a composite, still override every column of that composite
  3. For overriding associations, use @AssociationOverride instead of @AttributeOverride
  4. If counts look right, check that a different validation error is not masking this one - fix the first mapping error reported

Example fix

// before: two-column property overridden once
@Embeddable
public class Money {                       // maps amount + currency -> 2 columns
    BigDecimal amount;
    @Column(length = 3) String currency;
}

@Entity
public class Payment {
    @Embedded
    @AttributeOverrides({@AttributeOverride(name = "amount", column = @Column(name = "pay_amount"))})
    private Money money;                   // 1 override for 2 columns -> error
}

// after: every column overridden exactly once
@Embedded
@AttributeOverrides({
    @AttributeOverride(name = "amount", column = @Column(name = "pay_amount")),
    @AttributeOverride(name = "currency", column = @Column(name = "pay_currency"))
})
private Money money;
Defensive patterns

Strategy: validation

Validate before calling

// Before boot: known multi-column embeddables need an override per column
// Money maps 2 columns -> require both 'amount' and 'currency' overrides
static boolean overridesComplete(Class<?> entity, Map<String, Integer> columnsByPath) {
    for (Field f : entity.getDeclaredFields()) {
        AttributeOverrides o = f.getAnnotation(AttributeOverrides.class);
        if (o == null) continue;
        long distinct = Arrays.stream(o.value())
                .map(AttributeOverride::name).distinct().count();
        Integer expected = columnsByPath.get(f.getName());
        if (expected != null && distinct != expected) return false;
    }
    return true;
}

Try / catch

try {
    final SessionFactory sf = new MetadataSources(standardServiceRegistry)
            .addAnnotatedClass(MyEntity.class)
            .buildMetadata()
            .buildSessionFactory();
} catch (AnnotationException | MappingException e) {
    throw new IllegalStateException("Invalid ORM mapping, aborting startup: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: Overriding a property that maps N>1 columns with fewer/more @AttributeOverride entries - e.g. overriding only one column of a two-column user type or of a nested embeddable, or listing overrides for a single-column property twice.

Common situations: Applying @AttributeOverride to embeddables containing multi-column fields (monetary amounts, durations, custom composite types); copying override blocks between embeddables with different column counts; adding a field to an embeddable without adding its override.

Related errors


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