hibernate/hibernate-orm · error · AnnotationException
'@Check' may only be applied to single-column mappings but '
Error message
'@Check' may only be applied to single-column mappings but '${name}' maps to ${length} columns (use a table-level '@Check') What it means
AnnotatedColumn.applyColumnCheckConstraint turns a property-level @Check into a column-level CHECK constraint, which can only be attached to a single column. If the annotated member maps a number of columns other than 1, binding fails with this AnnotationException, and the message itself points you to a table-level '@Check'.
Source
Thrown at hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java:990
}
}
void applyCheckConstraint(PropertyData inferredData, int length) {
final var memberDetails = inferredData.getAttributeMember();
if ( memberDetails != null ) {
// if there are multiple annotations, they're not overrideable
final var checksAnn = memberDetails.getDirectAnnotationUsage( Checks.class );
if ( checksAnn != null ) {
final var checkAnns = checksAnn.value();
for ( var checkAnn : checkAnns ) {
addCheckConstraint( nullIfBlank( checkAnn.name() ), checkAnn.constraints() );
}
}
else {
final var checkAnn = getOverridableAnnotation( memberDetails, Check.class, getBuildingContext() );
if ( checkAnn != null ) {
if ( length != 1 ) {
throw new AnnotationException("'@Check' may only be applied to single-column mappings but '"
+ memberDetails.getName() + "' maps to " + length + " columns (use a table-level '@Check')" );
}
addCheckConstraint( nullIfBlank( checkAnn.name() ), checkAnn.constraints() );
}
}
}
else {
BOOT_LOGGER.couldNotPerformCheckLookup();
}
}
//must only be called after all setters are defined and before binding
private void extractDataFromPropertyData(
PropertyHolder propertyHolder,
PropertyData inferredData,
ModelsContext context) {
if ( inferredData != null ) {
final var memberDetails = inferredData.getAttributeMember();View on GitHub (pinned to fad1729dce)
Solutions
- Remove the property-level @Check and declare a table-level check instead: @Table(checks = @Check(...)) on the entity (or class-level @Check)
- Alternatively apply @Check to the exact single-column field inside the embeddable
- Verify the constraint expression references only columns of that one property if kept at column level
Example fix
// before: column-level @Check on a multi-column property
@Entity
public class Booking {
@Check(name = "valid_range", constraints = "start_date <= end_date") // 2 columns -> error
@Embedded
private DateRange range;
}
// after: table-level check
@Entity
@Table(checks = @Check(name = "valid_range", constraints = "start_date <= end_date"))
public class Booking {
@Embedded
private DateRange range; // maps start_date / end_date
} Defensive patterns
Strategy: validation
Validate before calling
// Before boot: property-level @Check must not sit on embedded/multi-column attributes
static boolean propertyChecksAreSingleColumn(Class<?> entity) {
for (Field f : entity.getDeclaredFields()) {
if (f.isAnnotationPresent(Check.class) && !f.isAnnotationPresent(Checks.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
- Use table-level @Check for any constraint spanning multiple columns
- Use property-level @Check only for single-column domains (e.g. 'price > 0')
- Add annotation-placement rules to code review checklists for composite mappings
When it happens
Trigger: A property-level @org.hibernate.annotations.Check on an @Embedded attribute, a composite basic type (@Columns), or other multi-column mapping during metadata binding.
Common situations: Annotating embedded/composite attributes with @Check; teams familiar with table-level checks applying the annotation at field level; refactoring a single-column field into a composite while leaving @Check behind.
Related errors
- '@GeneratedColumn' may only be applied to single-column mapp
- '@ColumnDefault' may only be applied to single-column mappin
- Multiple check constraints not supported for aggregate colum
- Property '${path}' specifies ${columnCount} '@AttributeOverr
- '@AttributeAccessor' annotation must specify a 'strategy'
AI-assisted analysis of hibernate/hibernate-orm@fad1729dce (2026-08-22).
Data as JSON: /api/errors/b7f8648871aa9811.
Report an issue: GitHub.