{"record":{"id":"74119d31513cd565","repo":"hibernate/hibernate-orm","slug":"column-mappings-for-property-propertyname-mix-74119d","errorCode":null,"errorMessage":"Column mappings for property '${propertyName}' mix updatable with 'updatable=false'","messagePattern":"Column mappings for property '(.+?)' mix updatable with 'updatable=false'","errorType":"exception","errorClass":"AnnotationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumns.java","lineNumber":174,"sourceCode":"\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}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n","sourceCodeStart":156,"sourceCodeEnd":188,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumns.java#L156-L188","documentation":"Hibernate throws this at bootstrap while binding a property that maps to multiple columns (e.g. via @Columns, @JoinColumn + @Column, or overridden mappings). Every column mapping belonging to one property must agree on nullability, insertability, updatability, and secondary table; here two of them disagree on the 'updatable' flag, so Hibernate cannot decide what the UPDATE statements should include.","triggerScenarios":"A property carries two column mappings where one is 'updatable=true' (the default) and the other is '@Column(..., updatable=false)'. Typical shapes: an @AttributeOverride/@AssociationOverride in a subclass that flips updatable on one column of a multi-column property; a @Column plus a @JoinColumn (or @JoinColumns) on the same association with mismatched flags; the same property mapped twice through an embedded and an override.","commonSituations":"Copying a read-only pattern ('insertable=false, updatable=false') onto only the first of several @Column entries of a property; overriding an embedded or mapped-superclass mapping in a subclass without repeating all flags; mixing JPA annotations with legacy XML hbm mappings where one side sets updatable=false.","solutions":["Find the property named in the message and set the SAME 'updatable' value on every @Column/@JoinColumn that maps it (default is true).","Check @AttributeOverride / @AssociationOverride declarations in subclasses and embeddings for flags that differ from the parent mapping; make them consistent.","If one column really must be read-only while others are writable, split them into two properties (one with updatable=false) instead of one property with mixed flags."],"exampleFix":"// before\n@Entity\nclass Person {\n    @Columns({\n        @Column(name = \"first_name\"),\n        @Column(name = \"last_name\", updatable = false) // mixed flags\n    })\n    private Name name;\n}\n\n// after\n@Entity\nclass Person {\n    @Columns({\n        @Column(name = \"first_name\"),\n        @Column(name = \"last_name\") // consistent: both updatable\n    })\n    private Name name;\n}","handlingStrategy":"validation","validationCode":"// Startup self-check: fail with a clear report before Hibernate binds\nfor (Class<?> cls : persistenceUnitClasses) {\n    for (Field f : cls.getDeclaredFields()) {\n        Column[] cols = f.getAnnotationsByType(Column.class);\n        Set<Boolean> upd = Arrays.stream(cols).map(Column::updatable).collect(toSet());\n        if (cols.length > 1 && upd.size() > 1) {\n            throw new IllegalStateException(cls.getSimpleName() + \".\" + f.getName()\n                + \" mixes updatable flags across \" + cols.length + \" columns\");\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    Metadata metadata = metadataBuilder.build(); // binding happens here\n} catch (AnnotationException e) {\n    // message names the property: \"Column mappings for property 'x' mix updatable...\"\n    throw newConfigurationException(\"Inconsistent column flags\", e);\n}","preventionTips":["Whenever a property maps multiple columns, always specify ALL of nullable/insertable/updatable/table explicitly on every column.","Review @AttributeOverride/@AssociationOverride values in subclasses whenever flags change on the parent mapping.","Add a metadata smoke test that builds a SessionFactory for all entities so these errors surface in CI, not production."],"tags":["hibernate","jpa","orm-mapping","annotation-conflict","boot-time"],"backgroundTag":"orm-column-mapping-conflict","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}