{"record":{"id":"31ffab836d35b5e3","repo":"hibernate/hibernate-orm","slug":"column-mappings-for-property-propertyname-mix","errorCode":null,"errorMessage":"Column mappings for property '${propertyName}' mix nullable with 'not null'","messagePattern":"Column mappings for property '(.+?)' mix nullable with 'not null'","errorType":"exception","errorClass":"AnnotationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumns.java","lineNumber":164,"sourceCode":"\t\treturn holder.getTable();\n\t}\n\n\tpublic void setTable(Table table) {\n\t\tthis.table = table;\n\t}\n\n\tpublic void addColumn(AnnotatedColumn child) {\n\t\tcolumns.add( child );\n\t}\n\n\tpublic void checkPropertyConsistency() {\n\t\tif ( columns.size() > 1 ) {\n\t\t\tfor ( int currentIndex = 1; currentIndex < columns.size(); currentIndex++ ) {\n\t\t\t\tfinal AnnotatedColumn current = columns.get( currentIndex );\n\t\t\t\tfinal AnnotatedColumn previous = columns.get( currentIndex - 1 );\n\t\t\t\tif ( !current.isFormula() && !previous.isFormula() ) {\n\t\t\t\t\tif ( current.isNullable() != previous.isNullable() ) {\n\t\t\t\t\t\tthrow new AnnotationException(\n\t\t\t\t\t\t\t\t\"Column mappings for property '\" + propertyName + \"' mix nullable with 'not null'\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif ( current.isInsertable() != previous.isInsertable() ) {\n\t\t\t\t\t\tthrow new AnnotationException(\n\t\t\t\t\t\t\t\t\"Column mappings for property '\" + propertyName + \"' mix insertable with 'insertable=false'\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif ( current.isUpdatable() != previous.isUpdatable() ) {\n\t\t\t\t\t\tthrow new AnnotationException(\n\t\t\t\t\t\t\t\t\"Column mappings for property '\" + propertyName + \"' mix updatable with 'updatable=false'\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif ( !current.getExplicitTableName().equals( previous.getExplicitTableName() ) ) {\n\t\t\t\t\t\tthrow new AnnotationException(\n\t\t\t\t\t\t\t\t\"Column mappings for property '\" + propertyName + \"' mix distinct secondary tables\"\n\t\t\t\t\t\t);\n\t\t\t\t\t}","sourceCodeStart":146,"sourceCodeEnd":182,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumns.java#L146-L182","documentation":"AnnotatedColumns.checkPropertyConsistency compares each pair of consecutive non-formula columns that map one property; if one column is nullable and the next is not, the mapping is self-contradictory and binding throws this AnnotationException. The same pass also rejects mixed insertable/updatable flags and distinct secondary tables.","triggerScenarios":"A single property mapped by multiple @Column/@AttributeOverride entries whose nullable settings differ - e.g. @Columns({@Column(nullable = false), @Column(nullable = true)}) or an @AttributeOverrides block where one overridden column is required and another is optional.","commonSituations":"Copying override blocks between properties and tweaking only one entry; composite types where one part was made NOT NULL for a legacy schema while the other stayed optional; partial edits after schema hardening.","solutions":["Make nullable identical across all columns of the property (usually all false when part of a key)","If parts genuinely differ, split the mapping into separate properties so each has a consistent nullability","Re-run the SessionFactory build to surface the next consistency check (insertable/updatable, table) if any"],"exampleFix":"// before: contradictory nullability within one property\n@Type(MoneyType.class)\n@Columns({\n    @Column(name = \"amount\", nullable = false),\n    @Column(name = \"currency\", nullable = true)    // mixes with false -> error\n})\nprivate Money price;\n\n// after: consistent nullability\n@Type(MoneyType.class)\n@Columns({\n    @Column(name = \"amount\", nullable = false),\n    @Column(name = \"currency\", nullable = false)\n})\nprivate Money price;","handlingStrategy":"validation","validationCode":"// Before boot: all columns of one multi-column property must agree on nullable\nstatic boolean columnGroupsConsistent(Class<?> entity) {\n    for (Field f : entity.getDeclaredFields()) {\n        Columns cols = f.getAnnotation(Columns.class);\n        if (cols == null || cols.value().length < 2) continue;\n        boolean first = cols.value()[0].nullable();\n        for (Column c : cols.value()) {\n            if (c.nullable() != first) 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":["Edit all entries of a @Columns/@AttributeOverrides block together","Prefer splitting genuinely-different-nullability data into separate properties","Add a consistency check to mapping tests"],"tags":["hibernate","jpa","multi-column","nullable","consistency","mapping"],"backgroundTag":"inconsistent-column-mapping-flags","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}