Comments below...
On Sun, Sep 2, 2018 at 11:17 PM, Vlad Mihalcea <mihalcea.vlad(a)gmail.com>
wrote:
Here are my comments:
1. I think the @MapsId behavior is the right one even if we don't enable
cascading on the child-side.
Although theoretically, we should not do that, in reality, if we disable
this behavior, some apps will break.
I don't know the history of why cascade-persist is forced. I see 2 jiras
related to this (HHH-4858, HHH-4887).
Emmanuel, can you provide some information about why this was added for
@MapsId, but not @PrimaryKeyJoinColumn? I don't see anything in the spec
about this. Am I missing something?
I agree that removing cascade-persist from @MapsId now would cause
applications to break.
If this is Hibernate-specific behavior then shouldn't this functionality be
enabled/disabled via a property ( e.g.,
hibernate.jpa.compliance.cascade_persist_id_associations)
and JpaCompliance? The default would be "enabled".
We could document the behavior so that, when used @MapdId or PKJC,
Hibernate is allowed to cascade the parent if it's transient.
Do we really want to add this functionality with @PKJC at this point, since
@MapsId is preferred, and cascade=CascadeType.PERSIST can easily be added
to @OneToOne.
2. According to the JPA spec, the @OneToOne optional attribute is
defined
like this:
(Optional) Whether the association is optional.
> If set to false then a non-null relationship must
> always exist.
I don't think that `optional` should be related to the FK generation. For
me, `optional` is more like adding NOT NULL to the FK column.
I think in this particular case, it is important.
With the following mapping:
@Entity
public class ChildPKJC {
@Id
private Long id;
@OneToOne // note that cascade-persist is not enabled
@PrimaryKeyJoinColumn
private Parent parent;
}
If ChildPKJC#parent is null, that means that ChildPKJC#id has a non-null
value that does not correspond to a Parent ID. If this happens and there is
a foreign key constraint, a constraint violation would occur.
It seems to me that the spec supports this use case with @PKJC, and
annotating with @NotFound(IGNORE) should not be necessary.
Emmanuel, do you agree?
Should using @MapsId instead of @PKJC work the same way?
3. This reminds me of this Jira issue:
https://hibernate.atlassian.net/browse/HHH-12770
I guess it makes sense to have what you proposed:
Is this expected? If so, then ChildMapsId#parent cannot be optional by
> default (without @NotFound(IGNORE).
As for these questions:
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():
This is a consequence of the current behavior. I've added this phrase to
explain what happens.
If we change the behavior, we should update the doc too.
Related to:
[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.
They say "may not", meaning that setting the ID prior to flushing is not
going to break the spec.
For @MapsId, the spec seems to say that, if the application doesn't assign
the ID attributes corresponding to a relationship, the persistence provider
will assign ID attributes during flush (at the latest). I find the
documentation on this topic to be confusing, so maybe I'm missing something.
I don't see anything in the spec that this same behavior is expected for
@PKJC.
Emmanuel, please confirm.
For sequence identifiers, we always get the ID generated prior to the
flush anyway.
And for:
[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”.
I don't think that makes any sense at all. The real one-to-one database
table relationship is supposed to use a FK.
Now, without MapsId or PKJC, we actually have a "one-to-many" table
relationship with a UNIQUE constraint.
Even so, the FK is always mandatory. I have no idea why the JPA spec says
that.
Please see what I wrote above about this.
Thanks,
Gail
Vlad
On Sat, Sep 1, 2018 at 10:33 AM, Gail Badner <gbadner(a)redhat.com> wrote:
> FWIW, I've already spent a lot of time looking into the possible bugs I've
> described. I have a good idea about how to fix each one, so there's no
> need
> to research these. At this point, I'm just trying to get confirmation of
> whether they really are bugs.
>
> On Sat, Sep 1, 2018 at 12:21 AM, Gail Badner <gbadner(a)redhat.com> wrote:
>
> > 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.IdentifierGen
> erationException:
> > ids for this class must be manually assigned before calling save():
> >
> > 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.
> >
> > When persisting ChildMapsId:
> >
> > 1) Hibernate automatically initializes ChildMapsId#id to parent.id [2]
> >
> > 2) if ChildMapsId#parent is new, parent is automatically
> > cascade-persisted (even though CascadeStyle.PERSIST is not mapped), then
> > the ChildMapsId entity is persisted.
> >
> > 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. [3] If ChildPKJC#parent is not optional, a foreign
> > key is generated
> >
> > For ChildMapsId, a foreign key is generated from ChildPKJC referencing
> > Parent, even if ChildMapsId#parent is optional.
> >
> > Is this a bug? My guess is that it is.
> >
> > 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.
> >
> > 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.
> > 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).
> >
> > 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 <gbadner(a)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(a)lists.jboss.org
>
https://lists.jboss.org/mailman/listinfo/hibernate-dev
>