{"record":{"id":"857391a77967c1c9","repo":"hibernate/hibernate-orm","slug":"struct-s-is-defined-by-multiple-components-s-w","errorCode":null,"errorMessage":"Struct [%s] is defined by multiple components %s with different number of mappings %d and %d","messagePattern":"Struct \\[(.+?)\\] is defined by multiple components (.+?) with different number of mappings (.+?) and (.+?)","errorType":"exception","errorClass":"MappingException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/boot/model/internal/AggregateComponentSecondPass.java","lineNumber":394,"sourceCode":"\n\t}\n\n\tprivate static void ensureParentInitialized(\n\t\t\tInFlightMetadataCollector metadataCollector,\n\t\t\tAggregateColumn aggregateColumn) {\n\t\tdo {\n\t\t\t// Trigger resolving of the value so that the column gets properly filled\n\t\t\taggregateColumn.getValue().getType();\n\t\t\t// Make sure this state is initialized\n\t\t\taggregateColumn.getSqlTypeCode( metadataCollector );\n\t\t\taggregateColumn.getSqlType( metadataCollector );\n\t\t\taggregateColumn = aggregateColumn.getComponent().getParentAggregateColumn();\n\t\t} while ( aggregateColumn != null );\n\t}\n\n\tprivate void validateEqual(UserDefinedObjectType udt1, UserDefinedObjectType udt2) {\n\t\tif ( udt1.getColumnSpan() != udt2.getColumnSpan() ) {\n\t\t\tthrow new MappingException(\n\t\t\t\t\tString.format(\n\t\t\t\t\t\t\t\"Struct [%s] is defined by multiple components %s with different number of mappings %d and %d\",\n\t\t\t\t\t\t\tudt1.getName(),\n\t\t\t\t\t\t\tfindComponentClasses(),\n\t\t\t\t\t\t\tudt1.getColumnSpan(),\n\t\t\t\t\t\t\tudt2.getColumnSpan()\n\n\t\t\t\t\t)\n\t\t\t);\n\t\t}\n\t\tfinal List<Column> missingColumns = new ArrayList<>();\n\t\tfor ( Column column1 : udt1.getColumns() ) {\n\t\t\tfinal Column column2 = udt2.getColumn( column1 );\n\t\t\tif ( column2 == null ) {\n\t\t\t\tmissingColumns.add( column1 );\n\t\t\t}\n\t\t\telse if ( !column1.getSqlType().equals( column2.getSqlType() ) ) {\n\t\t\t\tthrow new MappingException(","sourceCodeStart":376,"sourceCodeEnd":412,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AggregateComponentSecondPass.java#L376-L412","documentation":"When multiple components map to the same database struct name, Hibernate enforces that they describe the identical type. validateEqual first compares column counts; if two definitions of the same struct map different numbers of columns, this MappingException reports the struct name, the involved component classes, and both counts.","triggerScenarios":"Two (or more) embeddable classes, or two mappings of embeddables, carry the same @Struct(name = ...) but declare different numbers of persistent attributes/columns - e.g. one Address struct with 3 fields and another with 4.","commonSituations":"Copy-pasting an embeddable and adding a field to only one copy; different teams mapping the same database UDT with diverging Java models; a stale duplicate embeddable left behind after refactoring.","solutions":["Make the column sets identical across all components sharing the struct name","If the types genuinely differ, give one of them a different struct name","Delete the stale/duplicate embeddable mapping","After fixing, ensure only one canonical Java class represents each database UDT"],"exampleFix":"// before: same struct name, different column counts\n@Embeddable @Struct(name = \"address\")\npublic class AddressV1 { String street; String city; }              // 2 columns\n\n@Embeddable @Struct(name = \"address\")\npublic class AddressV2 { String street; String city; String zip; }  // 3 columns -> error\n\n// after: one canonical definition (or distinct struct names)\n@Embeddable @Struct(name = \"address\")\npublic class Address { String street; String city; String zip; }   // single source of truth","handlingStrategy":"validation","validationCode":"// Before boot: embeddables sharing a struct name must map the same number of columns\nstatic Map<String, List<Class<?>>> structFieldCounts(List<Class<?>> embeddables) {\n    Map<String, List<Class<?>>> byStruct = new HashMap<>();\n    for (Class<?> c : embeddables) {\n        Struct s = c.getAnnotation(Struct.class);\n        if (s != null) byStruct.computeIfAbsent(s.name(), k -> new ArrayList<>()).add(c);\n    }\n    return byStruct; // assert each list's members all map equal column counts\n}","typeGuard":null,"tryCatchPattern":"try {\n    sessionFactory = metadata.buildSessionFactory();\n} catch (MappingException e) {\n    if (e.getMessage().contains(\"different number of mappings\")) {\n        throw new IllegalStateException(\"Two embeddables share a struct name but map different columns\", e);\n    }\n    throw e;\n}","preventionTips":["One Java class per database struct type - no parallel variants","Search the codebase for @Struct(name = \"x\") duplicates whenever you add or edit one","Cover struct mappings with a boot smoke test in CI"],"tags":["hibernate","struct","duplicate-definition","mapping","udt"],"backgroundTag":"struct-definition-conflict","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}