[hibernate-dev] Entity implementation of equals()

Steve Ebersole steve at hibernate.org
Wed Aug 28 08:05:16 EDT 2019


There are 2 different discussions here:

1) How to implement `#equals` to account for HibernateProxy
2) How to implement `#equals` in a MappedSuperclass

These things are at odds here.

With regards to (1), as discussed above, you have to allow subclasses in
the equality comparison *if* you want to use proxy-based laziness.  Is
there a specific discussion about that?  Well there is, or used to be, an
entire section in the documentation about implementing `#equals` properly.
I do not remember whether it mentions this "allow subclasses"
specifically.  However it is common sense given the documented way that
proxy-based laziness works.

The real problem here is that the MappedSuperclass is defined "above" the
root entities in the hierarchy and the user is trying to define the
comparison there.  Allowing subclass matching there means that any entity
type would equal any other entity types, even entities in a different
entity hierarchy.  E.g. say Customer and Order are both root entities and
extend a common MappedSuperclass named BaseEntity - if BaseEntity defines
`#equals` to allow subclasses to match then a Customer would equal an Order
which is clearly wrong.

As far as using getters in the `#equals` impl to account for laziness... I
do not know if it is specifically documented somewhere, but again that's
just common sense.

Note that these in particular could be handled via the new "bytecode proxy"
feature. There would be no HibernateProxy involved and access to getters
*or fields* would trigger the load. Just a thought



On Tue, Aug 27, 2019, 11:18 PM Gail Badner <gbadner at redhat.com> wrote:

> Please see below...
>
> On Tue, Aug 27, 2019 at 8:00 PM Jan-Willem Gmelig Meyling <
> jan-willem at youngmediaexperts.nl> wrote:
>
>> I tend to use this.getClass().isInstance(o) and this.getClass().cast(o)
>> which works even in a mapped super class on most occasions. (Assuming that
>> the proxy delegates equals to a concrete target).
>>
>
> Your suggestion worked to get past the class comparison.
>
> There is still an issue though when comparing to a HibernateProxy.
>
> This returns false: bID.equals( that.bID).
>
> This returns true: bID.equals( that.getbID() ).
>
> I've never noticed this before.
>
> Steve, is there some requirement that getters have be used to compare
> properties when an entity can be lazy?
>
>
>>
>> Jan-Willem
>>
>>
>> > Op 27 aug. 2019 om 22:29 heeft Steve Ebersole <steve at hibernate.org>
>> het volgende geschreven:
>> >
>> > Generally speaking an `#equals` method would not allow subclasses to
>> > match.  For entity mappings specifically I this it is generally
>> considered
>> > kosher to allow subclass matching in `#equals`.  Interestingly, when
>> > generating `#equals` overrides through IntelliJ it even asks
>> specifically
>> > whether subclasses should be allowed to match.  In fact it mentions (or
>> > used to mention) Hibernate specifically wrt proxies in the dialog.
>> >
>> > And it's actually required (as you found) if you want to use Hibernate's
>> > proxy-based lazy loading.
>>
>
> It makes sense, but I don't remember reading it. Is this documented
> somewhere?
>
> HHH-13590 specifically has to do with an entity's #equals(Object o) with o
> being a HibernateProxy.
>
> The fix unproxies the HibernateProxy, so that the argument to
> #equals(Object o) is the target (not a HibernateProxy).
>
> Thanks,
> Gail
>
> >
>> > However, this is a case with a MappedSuperclass and specifically a
>> > MappedSupperclass that is I guess shared amongst multiple root entities.
>> > So here the allowance of subclasses is not really feasible and relying
>> on
>> > any kind of equality checking defined on the MappedSuperclass is not
>> going
>> > to work
>> >
>> >> On Tue, Aug 27, 2019 at 6:49 PM Gail Badner <gbadner at redhat.com>
>> wrote:
>> >>
>> >> Hi,
>> >>
>> >> I'm looking into the impact of HHH-13590.
>> >>
>> >> In the test for HHH-13590, I see that the mapped superclass entity
>> defines
>> >> equals() as:
>> >>
>> >> @Override
>> >> public boolean equals(Object o) {
>> >>   if (this == o) return true;
>> >>   if (o == null || getClass() != o.getClass()) return false;
>> >>
>> >>   ...
>> >>
>> >> }
>> >>
>> >> Due to the bug:
>> >> * this is a Project instance;
>> >> * o is a HibernateProxy with target == this.
>> >>
>> >> Because the getClass() != o.getClass(), false is returned, and that
>> >> ultimately causes the test to fail.
>> >>
>> >> The fix for HHH-13590 results in comparing a Project instance with
>> itself
>> >> (not the proxy).
>> >>
>> >> I see that the documentation has a similar implementation of equals()
>> in
>> >> "Example 111. Natural Id equals/hashCode" of [1].
>> >>
>> >> In general, is it OK for equals() to require both instances to be of
>> the
>> >> same class?
>> >>
>> >> Thanks,
>> >> Gail
>> >>
>> >> [1]
>> >>
>> >>
>> https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#mapping-model-pojo-equalshashcode
>> >> _______________________________________________
>> >> hibernate-dev mailing list
>> >> hibernate-dev at lists.jboss.org
>> >> https://lists.jboss.org/mailman/listinfo/hibernate-dev
>> >>
>> > _______________________________________________
>> > hibernate-dev mailing list
>> > hibernate-dev at lists.jboss.org
>> > https://lists.jboss.org/mailman/listinfo/hibernate-dev
>>
>


More information about the hibernate-dev mailing list