{"record":{"id":"4920bb92c13a562e","repo":"hibernate/hibernate-orm","slug":"property-qualify-getentityname-prop-getna","errorCode":null,"errorMessage":"Property '\" + qualify( getEntityName(), prop.getName() ) + \"' maps to \" + actualColumns + \" columns but \" + requiredColumns + \" columns are required (type '\" + type.getName() + \"' spans \" + requiredColumns + \" columns)","messagePattern":"Property '\" \\+ qualify\\( getEntityName\\(\\), prop\\.getName\\(\\) \\) \\+ \"' maps to \" \\+ actualColumns \\+ \" columns but \" \\+ requiredColumns \\+ \" columns are required \\(type '\" \\+ type\\.getName\\(\\) \\+ \"' spans \" \\+ requiredColumns \\+ \" columns\\)","errorType":"exception","errorClass":"MappingException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java","lineNumber":702,"sourceCode":"\t\t\t|| getSuperMappedSuperclass() != null && getSuperMappedSuperclass().isPropertyDefinedInHierarchy( name )\n\t\t\t|| getSuperclass() != null && getSuperclass().isPropertyDefinedInHierarchy( name );\n\t}\n\n\tpublic OptimisticLockStyle getOptimisticLockStyle() {\n\t\treturn optimisticLockStyle;\n\t}\n\n\tpublic void setOptimisticLockStyle(OptimisticLockStyle optimisticLockStyle) {\n\t\tthis.optimisticLockStyle = optimisticLockStyle;\n\t}\n\n\tpublic void validate(Metadata mapping) throws MappingException {\n\t\tfor ( var prop : getProperties() ) {\n\t\t\tif ( !prop.isValid( mapping ) ) {\n\t\t\t\tfinal var type = prop.getType();\n\t\t\t\tfinal int actualColumns = prop.getColumnSpan();\n\t\t\t\tfinal int requiredColumns = type.getColumnSpan( mapping );\n\t\t\t\tthrow new MappingException(\n\t\t\t\t\t\t\"Property '\" + qualify( getEntityName(), prop.getName() )\n\t\t\t\t\t\t\t\t+ \"' maps to \" + actualColumns + \" columns but \" + requiredColumns\n\t\t\t\t\t\t\t\t+ \" columns are required (type '\" + type.getName()\n\t\t\t\t\t\t\t\t+ \"' spans \" + requiredColumns + \" columns)\"\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tcheckPropertyDuplication();\n\t\tcheckColumnDuplication();\n\t}\n\n\tprivate void checkPropertyDuplication() throws MappingException {\n\t\tfinal HashSet<String> names = new HashSet<>();\n\t\tfor ( var property : getProperties() ) {\n\t\t\tif ( !names.add( property.getName() ) ) {\n\t\t\t\tthrow new MappingException( \"Duplicate property mapping of \" + property.getName() + \" found in \" + getEntityName() );\n\t\t\t}\n\t\t}","sourceCodeStart":684,"sourceCodeEnd":720,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java#L684-L720","documentation":"Mapping validation failed because the number of columns a property maps to does not match the number its Hibernate type requires. PersistentClass.validate() calls Property.isValid(); when invalid, it compares prop.getColumnSpan() (columns actually mapped) with type.getColumnSpan(mapping) (columns the type spans) and reports both numbers. The property's column mapping and its resolved type disagree.","triggerScenarios":"A custom UserType or composite type that spans 2 columns while only one @Column is declared (or the reverse); @Columns with the wrong element count; an @Embedded whose override set lost or added columns; a formula used where the type requires real columns.","commonSituations":"Upgrading Hibernate so the resolved type for a Java type changes its column span; composite-user-type migration in 6.x; hand-written hbm with stale column lists after type changes.","solutions":["Read the two numbers in the message and add or remove @Column/@Columns entries so the mapped count equals the required count","For custom types, check the type's declared column span against the mapped columns","For embeddables, verify @AttributeOverride lists cover exactly the component's columns","After a Hibernate upgrade, re-check types whose span semantics changed"],"exampleFix":"// before\n@Columns(columns = @Column(name = \"v1\"))\nprivate MonetaryAmount amount; // MonetaryAmount spans 2 columns\n\n// after\n@Columns(columns = {@Column(name = \"v1\"), @Column(name = \"v2\")})\nprivate MonetaryAmount amount;","handlingStrategy":"validation","validationCode":"for (PersistentClass pc : metadata.getEntityBindings()) {\n    for (Property p : pc.getProperties()) {\n        int mapped = p.getColumnSpan();\n        int required = p.getType().getColumnSpan((Mapping) metadata);\n        if (mapped != required) {\n            // report entity and property before the SessionFactory build fails\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"try { metadata.buildSessionFactory(); }\ncatch (MappingException e) {\n    if (e.getMessage().contains(\"columns are required\")) {\n        // read the two counts from the message and align the @Columns list\n    }\n    throw e;\n}","preventionTips":["Document the column span of custom types next to their registration","Run a mapping-validation test after every Hibernate upgrade","Prefer standard types; wrap multi-column types in a well-tested AggregateJdbcType"],"tags":["hibernate","orm","mapping-validation","columns","type-mapping"],"backgroundTag":"column-count-mismatch","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}