{"record":{"id":"cb62222724c44168","repo":"hibernate/hibernate-orm","slug":"association-in-entity-is-annotated-map","errorCode":null,"errorMessage":"Association '{}' in entity '{}' is annotated '@MapsId' but refers to a property '{}' which has an explicit column mapping","messagePattern":"Association '(.+?)' in entity '(.+?)' is annotated '@MapsId' but refers to a property '(.+?)' which has an explicit column mapping","errorType":"exception","errorClass":"AnnotationException","httpStatus":null,"severity":"error","filePath":"hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedJoinColumn.java","lineNumber":394,"sourceCode":"\t\t\t\tmappingColumn != null && mappingColumn.isUnique(),\n\t\t\t\tfalse\n\t\t);\n\t}\n\n\tprivate String defaultColumnName(int columnIndex, PersistentClass referencedEntity, String logicalReferencedColumn) {\n\t\tfinal var parent = getParent();\n\t\tif ( parent.hasMapsId() ) {\n\t\t\t// infer the join column of the association\n\t\t\t// from the name of the mapped primary key\n\t\t\t// column (this is not required by the JPA\n\t\t\t// spec) and is arguably backwards, given\n\t\t\t// the name of the @MapsId annotation, but\n\t\t\t// it's better than just having two different\n\t\t\t// column names which disagree\n\t\t\tfinal var column = parent.resolveMapsId().getValue().getColumns().get( columnIndex );\n//\t\t\treturn column.getQuotedName();\n\t\t\tif ( column.isExplicit() ) {\n\t\t\t\tthrow new AnnotationException( \"Association '\" + parent.getPropertyName()\n\t\t\t\t\t\t+ \"' in entity '\" + parent.getPropertyHolder().getEntityName()\n\t\t\t\t\t\t+ \"' is annotated '@MapsId' but refers to a property '\"\n\t\t\t\t\t\t+ parent.getMapsId() + \"' which has an explicit column mapping\" );\n\t\t\t}\n\t\t}\n//\t\telse {\n\t\t\treturn parent.buildDefaultColumnName( referencedEntity, logicalReferencedColumn );\n//\t\t}\n\t}\n\n\tprivate String defaultAnyKeyColumnName() {\n\t\tfinal var context = getBuildingContext();\n\t\tfinal var buildingOptions = context.getBuildingOptions();\n\t\tfinal Identifier logicalColumnName =\n\t\t\t\tbuildingOptions.getImplicitNamingStrategy()\n\t\t\t\t\t\t.determineAnyKeyColumnName( new ImplicitAnyKeyColumnNameSource() {\n\t\t\t\t\t\t\tprivate final AttributePath attributePath =\n\t\t\t\t\t\t\t\t\tAttributePath.parse( getParent().getPropertyName() );","sourceCodeStart":376,"sourceCodeEnd":412,"githubUrl":"https://github.com/hibernate/hibernate-orm/blob/fad1729dce015f908198d57a8d80274a30f905a5/hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedJoinColumn.java#L376-L412","documentation":"'@MapsId' tells Hibernate to derive the association's join column from the mapped identifier property's column. That derivation only works when the id property uses the default/implicit column name; if the named id property has an explicit @Column mapping, the derived join-column name would silently disagree with it, so Hibernate (as a non-spec convenience it controls strictly) throws instead.","triggerScenarios":"'@Id @Column(name = \"cust_id\") Long id;' together with '@OneToOne @MapsId Customer customer;' on the same entity; @MapsId pointing at an @EmbeddedId attribute that declares an explicit @Column. Detected in AnnotatedJoinColumn when parent.hasMapsId() and the resolved mapped column isExplicit().","commonSituations":"Shared-primary-key (@OneToOne derived-id) patterns where the id column was renamed explicitly; retrofitting @MapsId onto an existing entity that already had a custom id column name; following a tutorial that adds both @Column on the id and @MapsId on the association.","solutions":["Remove the explicit @Column from the identifier property named in the message and let the association's @JoinColumn define the shared column name.","Or keep the explicit @Column and drop @MapsId, mapping the FK manually: '@Column(name=\"cust_id\", insertable=false, updatable=false)' on the id plus a plain @OneToOne with @JoinColumn.","Make sure only ONE side names the column — either the id property or the association's @JoinColumn, never both."],"exampleFix":"// before\n@Entity\nclass Person {\n    @Id\n    @Column(name = \"cust_id\") // explicit -> conflicts with @MapsId derivation\n    Long id;\n\n    @OneToOne(fetch = FetchType.LAZY)\n    @MapsId\n    Customer customer;\n}\n\n// after\n@Entity\nclass Person {\n    @Id\n    Long id; // implicit name; derived from the join column\n\n    @OneToOne(fetch = FetchType.LAZY)\n    @MapsId\n    @JoinColumn(name = \"cust_id\")\n    Customer customer;\n}","handlingStrategy":"validation","validationCode":"// Guard: an explicitly mapped id property must not be @MapsId-derived\nfor (Field f : cls.getDeclaredFields()) {\n    if (f.isAnnotationPresent(ManyToOne.class) || f.isAnnotationPresent(OneToOne.class)) {\n        MapsId mapsId = f.getAnnotation(MapsId.class);\n        if (mapsId != null && !mapsId.value().isEmpty()) {\n            Field idField = cls.getDeclaredField(mapsId.value());\n            if (idField != null && idField.getAnnotation(Column.class) != null\n                    && idField.getAnnotation(Column.class).name() != null\n                    && !idField.getAnnotation(Column.class).name().isEmpty()) {\n                throw new IllegalStateException(\"Explicit @Column on id conflicts with @MapsId on \" + f.getName());\n            }\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    Metadata md = sources.buildMetadata();\n} catch (AnnotationException e) {\n    // 'explicit column mapping' -> remove @Column from the id property or drop @MapsId\n    throw newConfigurationException(\"Derived-id column conflict\", e);\n}","preventionTips":["With @MapsId, name the shared column in exactly one place: the association's @JoinColumn.","Never copy a tutorial that shows @Column on the id AND @MapsId on the association.","Cover every derived-id entity with a schema-generation test (drop/create) in CI."],"tags":["hibernate","jpa","mapsid","derived-identifier","orm-mapping"],"backgroundTag":"mapsid-misconfiguration","analyzedSha":"fad1729dce015f908198d57a8d80274a30f905a5","analyzedAt":"2026-08-22T04:13:57.527Z","schemaVersion":2},"datasetVersion":"2026-08-22T09:17:25.309Z"}