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
- Move @ColumnDefault off the multi-column property
- Apply the default to the specific single-column leaf (target the embeddable field that maps one column)
- 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
- Treat @ColumnDefault as a leaf-level, single-column annotation
- Put defaults inside the embeddable on the exact field they affect
- Smoke-test mappings in CI so misplaced annotations fail early
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
- '@GeneratedColumn' may only be applied to single-column mapp
- '@Check' may only be applied to single-column mappings but '
- Property '${path}' specifies ${columnCount} '@AttributeOverr
- '@AttributeAccessor' annotation must specify a 'strategy'
- Write expression in '@ColumnTransformer' for property '${pro
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/4b2e2fec1e5e39de.
Report an issue: GitHub.