Forwarding Emmanuel's responses, which reduces the scope of what I thought was buggy behavior.
There are still some weird cases though. I'll hone in on those in a separate thread. ---------- Forwarded message ---------- From: Emmanuel Bernard <ebern...@redhat.com> Date: Wed, Sep 12, 2018 at 5:54 AM Subject: Re: Fwd: @OneToOne with @PrimaryKeyJoinColumn(s) vs @MapsId without value element To: Gail Badner <gbad...@redhat.com> Cc: Guillaume Smet <gs...@redhat.com> On Wed 18-09-12 0:29, Gail Badner wrote: > Hibernate treats @OneToOne @PKJC differently from @OneToOne MapsId (without > a value element). I believe some of the differences are expected, but I > suspect some of the differences are bugs. In some cases, I'm not sure which > is treated correctly. > All my comments are caveat by me not having looked at this for years. Note that these are different features. @PKJC says that we don't create a new FK column, we reuse the id one. @MapsId says, copy the value of the id from that property and pretend it's an id generator. > I've also found that the following mapping has some problems: > > @OneToOne > @MapsId > @JoinColumn(name = "FK") > private Employee employee; > > Sometimes Hibernate treats that mapping like @OneToOne @PKJC; other cases > it treats that mapping like @OneToOne @MapsId. > > I'm in the process of documenting the differences in a Google document so > it can all be sorted out. > > As a start, it would help a lot if you could address the questions in this > email. > > I've gotten very familiar with the related code, so once I have the > answers, I'll know how to fix them. > > Thanks, > Gail > > ---------- Forwarded message ---------- > From: Gail Badner <gbad...@redhat.com> > Date: Sat, Sep 1, 2018 at 12:21 AM > Subject: Re: @OneToOne with @PrimaryKeyJoinColumn(s) vs @MapsId without > value element > To: hibernate-dev <hibernate-dev@lists.jboss.org> > > > FYI, I am taking PTO Tuesday, 9/4. I hope to be able to move forward on > this when I return on 9/5. > > I see some differences. Some may be expected, but I think there are some > bugs. > > For example, suppose we have the following entities: > > @Entity > public class Parent { > @Id > private Long id; > } > > @Entity > public class ChildPKJC { > @Id > private Long id; > > @OneToOne // note that cascade-persist is not enabled > @PrimaryKeyJoinColumn > private Parent parent; > } > > public class ChildMapsId { > @Id > private Long id; > > @OneToOne // note that cascade-persist is not enabled > @MapsId > private Parent parent; > } > > ------------------------------------------------------------ > ------------------------------------------------------------ > ------------------- > > When persisting ChildPKJC: > > 1) the application must initialize ChildPKJC#id before persisting the > entity [1]; otherwise, the following exception is thrown: > javax.persistence.PersistenceException: > org.hibernate.id.IdentifierGenerationException: > ids for this class must be manually assigned before calling save(): > Sounds fine. > 2) if ChildPKJC#parent is new with an assigned ID, and ChildPKJC#id is > assigned parent's ID, the ChildPKJC Entity is persisted with the parent's > ID, but parent is not persisted. > Sounds fine. > When persisting ChildMapsId: > > 1) Hibernate automatically initializes ChildMapsId#id to parent.id [2] > Yes that's the expected behavior. > 2) if ChildMapsId#parent is new, parent is automatically cascade-persisted > (even though CascadeStyle.PERSIST is not mapped), then the ChildMapsId > entity > is persisted. > So that is not expressed in the spec but it might be that disabling that is making things too complex for the Hibernate ORM engine. I would not sweat too much on it. > Are these expected difference? (My guess is yes) > > ------------------------------------------------------------ > ------------------------------------------------------------ > ------------------- > > Foreign key generation: > > If ChildPKJC#parent is optional there is no foreign key generated from > ChildPKJC > referencing Parent. > Sounds fine. [3] If ChildPKJC#parent is not optional, a foreign key > is generated > Sounds fine. > For ChildMapsId, a foreign key is generated from ChildPKJC referencing > Parent, even if ChildMapsId#parent is optional. > ChildMapsId cannot be optional as it is the generator of the id. So we ignore that the user has marked it optional. > Is this a bug? My guess is that it is. > Not to me, see above. > Adding the following mapping to ChildMapsId#parent works to disable foreign > key generation: > @JoinColumn(foreignKey = @ForeignKey(value = ConstraintMode.NO_CONSTRAINT)) > (can be used as a workaround) > > ------------------------------------------------------------ > ------------------------------------------------------------ > ------------------- > > Loading an existing ChildPKJC/ChildMapsId with an optional Parent > association by ID, when there is no Parent entity with the same ID (IIUC, > this is the only way that ChildPKJC#parent or ChildMapsId#parent can be > optional [3]): > > For ChildPKJC, the loaded ChildPKJC entity will have a null parent. There > is no need to add @NotFound(IGNORE) to ChildPKJC#parent. > That's a bit weird but if the parent is not optional, why isn't there a parent in the database with the proper id. Feels like a data incoherence problem. If people have that,t hey must make it optional. > If ChildPKJC#parent is optional, it is always eagerly loaded. > > This makes sense, since we cannot create a proxy if there is the > possibility of a null Parent entity. > > For ChildMapsId, the loaded value will be null because > ObjectNotFoundException > will be thrown when Hibernate tries to load the Parent entity. > Right, this is a data incoherence, the parent must not be null. Adding @NotFound(IGNORE) to ChildMapsId#parent will result in ChildMapsId > entity being loaded with a null parent association. > > Is this expected? If so, then ChildMapsId#parent cannot be optional by > default (without @NotFound(IGNORE). > Again I don't think parent being null is valid for @MapsId > I think it would make more sense if the ChildMapsId entity is loaded with a > null parent association, consistent with what happens for ChildPKJC. If we > go that route, then ChildMapsId#parent will always have to be loaded > eagerly. > > ------------------------------------------------------------ > ------------------------------------------------------------ > ------------------- > > Please let me know your thoughts on this. > > [1] this requirement is documented in Example 178. Derived identifier > @PrimaryKeyJoinColumn with a note that says: "Unlike @MapsId, the > application developer is responsible for ensuring that the identifier and > the many-to-one (or one-to-one) association are in sync as you can see in > the PersonDetails#setPerson method." > > [2] Section 2.4.1 Primary Keys Corresponding to Derived Identities of the > spec has this footnote: > [12] If the application does not set the primary key attribute > corresponding to the relationship, the value of that attribute may not be > available until after the entity has been flushed to the database. > > [3] Section 11.1.44 PrimaryKeyJoinColumn Annotation has a footnote: > [121]It is not expected that a database foreign key be defined for the > OneToOne mapping, as the OneToOne relationship may be defined as > “optional=true”. > > > On Fri, Aug 31, 2018 at 1:29 PM, Gail Badner <gbad...@redhat.com> wrote: > > The fix for HHH-12436 involves correcting the foreign key direction for >> "real" one-to-one associations. I've been looking into the ramifications >> of >> this change because I'm concerned that applications can rely on the old >> (incorrect) foreign key direction. >> >> In the process I've found that Hibernate treats: >> >> @OneToOne >> @PrimaryKeyJoinColumn >> private Employee employee; >> >> differently from: >> >> @OneToOne >> @MapsId >> private Employee employee; >> >> I believe they should be treated consistently. You can see my reasoning >> below. [1] >> >> Before going into details about how they are treated differently, I'd like >> to get confirmation, in case I am missing some subtlety. >> >> Could someone please confirm this? >> >> Regards, >> Gail >> >> ------------------------------------------------------------ >> --------------------------------- >> [1] >> >> In 2.4.1.3 Examples of Derived Identities, Example 4(b) uses MapsId >> without the value element as follows: >> >> @MapsId >> @JoinColumn(name="FK") >> @OneToOne Person patient; >> >> This example has the following footnote: >> "[15] Note that the use of PrimaryKeyJoinColumn instead of MapsId would >> result in the same mapping in this example. Use of MapsId >> is preferred for the mapping of derived identities." >> >> The description has a footnote that says that using PrimaryKeyJoinColumn >> instead of MapsId would result in the same mapping. >> >> In 11.1.45 PrimaryKeyJoinColumns Annotation, Example 2 uses >> @PrimaryKeyJoinColumns as follows: >> >> @OneToOne >> @PrimaryKeyJoinColumns({ >> @PrimaryKeyJoinColumn(name="ID", >> referencedColumnName="EMP_ID"), >> @PrimaryKeyJoinColumn(name="NAME", >> referencedColumnName="EMP_NAME")}) >> EmployeeInfo info; >> >> This example has the following footnote: >> "[123]Note that the derived identity mechanisms decribed in section >> 2.4.1.1 is now preferred to the use of PrimaryKeyJoinColumn for >> this case." >> >> >> _______________________________________________ hibernate-dev mailing list hibernate-dev@lists.jboss.org https://lists.jboss.org/mailman/listinfo/hibernate-dev