hibernate/hibernate-orm · error · AnnotationException

'@ColumnDefault' may only be applied to single-column mappin

Error message

'@ColumnDefault' may only be applied to single-column mappings but '${name}' maps to ${length} columns

What it means

AnnotatedColumn.applyColumnDefault applies the DDL default from @ColumnDefault when binding a column. The annotation describes a DEFAULT clause for exactly one column, so when the annotated property maps to a different number of columns than 1 (a composite/embedded or multi-column basic type), binding fails with this AnnotationException naming the member and the actual column count.

Source

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

	private static String getColumnName(Database database, jakarta.persistence.Column column) {
		final String name = column.name();
		return name.isBlank()
				? null
				: database.getJdbcEnvironment().getIdentifierHelper().toIdentifier( name ).render();
	}

	void applyColumnDefault(PropertyData inferredData, int length) {
		final var memberDetails = inferredData.getAttributeMember();
		if ( memberDetails != null ) {
			final var columnDefault = getOverridableAnnotation(
					memberDetails,
					ColumnDefault.class,
					getBuildingContext()
			);
			if ( columnDefault != null ) {
				if ( length != 1 ) {
					throw new AnnotationException( "'@ColumnDefault' may only be applied to single-column mappings but '"
							+ memberDetails.getName() + "' maps to " + length + " columns" );
				}
				setDefaultValue( columnDefault.value() );
			}
		}
		else {
			BOOT_LOGGER.couldNotPerformColumnDefaultLookup();
		}
	}

	void applyGeneratedAs(PropertyData inferredData, int length) {
		final var memberDetails = inferredData.getAttributeMember();
		if ( memberDetails != null ) {
			final var generatedColumn = getOverridableAnnotation(
					memberDetails,
					GeneratedColumn.class,
					getBuildingContext()
			);

View on GitHub (pinned to fad1729dce)

Solutions

  1. Move @ColumnDefault off the multi-column property
  2. Apply the default to the specific single-column leaf (target the embeddable field that maps one column)
  3. If a default is needed on several columns, define them per-column inside the embeddable or via column definition SQL

Example fix

// before: default on a multi-column embedded property
@Embeddable
public class Money { BigDecimal amount; String currency; }   // 2 columns

@Entity
public class Payment {
    @ColumnDefault("0")        // 1 default for 2 columns -> error
    @Embedded
    private Money money;
}

// after: default on the single-column leaf
@Embeddable
public class Money {
    @ColumnDefault("0")
    private BigDecimal amount;
    private String currency;
}
Defensive patterns

Strategy: validation

Validate before calling

// Before boot: @ColumnDefault must not sit on embedded/multi-column attributes
static boolean columnDefaultsAreSingleColumn(Class<?> entity) {
    for (Field f : entity.getDeclaredFields()) {
        if (f.isAnnotationPresent(ColumnDefault.class)
                && (f.isAnnotationPresent(Embedded.class) || f.isAnnotationPresent(Columns.class))) {
            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: Placing @ColumnDefault on a property whose mapping spans multiple columns - an @Embedded attribute, a multi-column user type (@Type with @Columns, e.g. monetary amount+currency), or an aggregate - during metadata binding.

Common situations: Adding @ColumnDefault to an embedded attribute for convenience; annotating composite custom types ported from single-column predecessors; bulk-adding default annotations with tooling that ignores column counts.

Related errors


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