{"record":{"id":"b7f8648871aa9811","repo":"hibernate/hibernate-orm","slug":"check-may-only-be-applied-to-single-column-mapp","errorCode":null,"errorMessage":"'@Check' may only be applied to single-column mappings but '${name}' maps to ${length} columns (use a table-level '@Check')","messagePattern":"'@Check' may only be applied to single-column mappings but '(.+?)' maps to (.+?) columns \\(use a table-level '@Check'\\)","errorType":"exception","errorClass":"AnnotationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java","lineNumber":990,"sourceCode":"\t\t}\n\t}\n\n\tvoid applyCheckConstraint(PropertyData inferredData, int length) {\n\t\tfinal var memberDetails = inferredData.getAttributeMember();\n\t\tif ( memberDetails != null ) {\n\t\t\t// if there are multiple annotations, they're not overrideable\n\t\t\tfinal var checksAnn = memberDetails.getDirectAnnotationUsage( Checks.class );\n\t\t\tif ( checksAnn != null ) {\n\t\t\t\tfinal var checkAnns = checksAnn.value();\n\t\t\t\tfor ( var checkAnn : checkAnns ) {\n\t\t\t\t\taddCheckConstraint( nullIfBlank( checkAnn.name() ), checkAnn.constraints() );\n\t\t\t\t}\n\t\t\t}\n\t\t\telse {\n\t\t\t\tfinal var checkAnn = getOverridableAnnotation( memberDetails, Check.class, getBuildingContext() );\n\t\t\t\tif ( checkAnn != null ) {\n\t\t\t\t\tif ( length != 1 ) {\n\t\t\t\t\t\tthrow new AnnotationException(\"'@Check' may only be applied to single-column mappings but '\"\n\t\t\t\t\t\t\t\t+ memberDetails.getName() + \"' maps to \" + length + \" columns (use a table-level '@Check')\" );\n\t\t\t\t\t}\n\t\t\t\t\taddCheckConstraint( nullIfBlank( checkAnn.name() ), checkAnn.constraints() );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tBOOT_LOGGER.couldNotPerformCheckLookup();\n\t\t}\n\t}\n\n\t//must only be called after all setters are defined and before binding\n\tprivate void extractDataFromPropertyData(\n\t\t\tPropertyHolder propertyHolder,\n\t\t\tPropertyData inferredData,\n\t\t\tModelsContext context) {\n\t\tif ( inferredData != null ) {\n\t\t\tfinal var memberDetails = inferredData.getAttributeMember();","sourceCodeStart":972,"sourceCodeEnd":1008,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java#L972-L1008","documentation":"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'.","triggerScenarios":"A property-level @org.hibernate.annotations.Check on an @Embedded attribute, a composite basic type (@Columns), or other multi-column mapping during metadata binding.","commonSituations":"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.","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"],"exampleFix":"// before: column-level @Check on a multi-column property\n@Entity\npublic class Booking {\n    @Check(name = \"valid_range\", constraints = \"start_date <= end_date\")  // 2 columns -> error\n    @Embedded\n    private DateRange range;\n}\n\n// after: table-level check\n@Entity\n@Table(checks = @Check(name = \"valid_range\", constraints = \"start_date <= end_date\"))\npublic class Booking {\n    @Embedded\n    private DateRange range;   // maps start_date / end_date\n}","handlingStrategy":"validation","validationCode":"// Before boot: property-level @Check must not sit on embedded/multi-column attributes\nstatic boolean propertyChecksAreSingleColumn(Class<?> entity) {\n    for (Field f : entity.getDeclaredFields()) {\n        if (f.isAnnotationPresent(Check.class) && !f.isAnnotationPresent(Checks.class)\n                && (f.isAnnotationPresent(Embedded.class) || f.isAnnotationPresent(Columns.class))) {\n            return false;\n        }\n    }\n    return true;\n}","typeGuard":null,"tryCatchPattern":"try {\n    final SessionFactory sf = new MetadataSources(standardServiceRegistry)\n            .addAnnotatedClass(MyEntity.class)\n            .buildMetadata()\n            .buildSessionFactory();\n} catch (AnnotationException | MappingException e) {\n    throw new IllegalStateException(\"Invalid ORM mapping, aborting startup: \" + e.getMessage(), e);\n}","preventionTips":["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"],"tags":["hibernate","check-constraint","embeddable","multi-column","annotation","mapping","ddl"],"backgroundTag":"single-column-annotation-misuse","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}