-
Type:
Improvement
-
Resolution: Unresolved
-
Priority:
Unknown
-
None
-
Affects Version/s: None
-
Component/s: Model
-
None
-
None
-
None
-
None
-
None
-
None
-
None
-
None
Summary
The boot guard forbidDerivedIdentity in MongoAdditionalMappingContributor detects derived identity by finding an entity-level ToOne whose column name(s) overlap the identifier's column names. That predicate also rejects a plain, writable association whose join column happens to be named the same as the id column, for example:
@Entity class ItemA { @Id int id; @ManyToOne @JoinColumn(name = "id") ItemB itemB; }
That mapping works in MongoDB. The guard runs before setIdentifierColumnName, so it compares pre-rename SQL column names. After the rename the identifier's column is _id and the association's column is still id; they are distinct Column instances, both fields are written on insert, and a $lookup on localField: "id" resolves correctly. This shape was exercised by a passing integration test before the guard was added.
Consider narrowing the predicate so it rejects only an association that does not own its columns.
Proposed discriminator
Measured on Hibernate ORM 7.4.5 by dumping the mapping model for each shape at boot:
| mapping | column-name overlap with id | association column insertable/updatable |
|---|---|---|
| plain @ManyToOne @JoinColumn(name = "id") | yes | true / true |
| @MapsId with @EmbeddedId | yes | false / false |
| @MapsId with a simple @Id | yes | false / false |
| @JoinColumn(name = "id", insertable = false, updatable = false) | yes | false / false |
| @OneToOne @PrimaryKeyJoinColumn | association has no columns | n/a |
In every shape the association's Column is a distinct instance from the identifier's and is not registered in the table, so instance identity does not discriminate. Column insertability does: when the association does not own its columns, nothing ever writes that field, so after the identifier is renamed to _id the field never exists and a $lookup on it silently matches nothing. That is the case that must keep failing at boot. When the association owns its column, the two mappings resolve to separate BSON fields and translation is correct.
Second gap in the same predicate
@OneToOne @PrimaryKeyJoinColumn yields a ToOne with zero columns, so the overlap predicate never fires on it. Whether that shape translates correctly or breaks silently has not been determined.
Context
HIBERNATE-207 added the guard. The over-broad predicate was found when the guard broke the many-to-one join test added by HIBERNATE-223, whose entity used @JoinColumn(name = "id"); that test was fixed by renaming its join column, so nothing is currently blocked.
- related to
-
HIBERNATE-237 Support derived identity (@MapsId) in a composite primary key
-
- Needs Triage
-