SkyWalker
SkyWalker

Reputation: 14307

Hibernate javassist proxies and `Object#equals`

When providing #equals implementation for a UDT in Java one of the conditions is that the passed argument object must be an instance of the current class otherwise we fail-fast return false see Effective Java (EJ2). However, while using Hibernate 4 we can end up with javassist proxy instances due to lazy loading where this #equals condition will fail. What would be the best choice to overcome this? The few choices I can think of are:

UPDATE

Reviewing EJ2 again I believe that the following will work for all scenarios (Type-Type, Type-Proxy, Proxy-Type and Proxy-Proxy) but as pointed out in one of the comments below it may loop forever if the Type is compared to a totally different type e.g. Person.equals(Employee) and both use the same equals EJ2 criteria.

    if (this.getClass() != anObject.getClass())
    {
        return anObject.equals(this);
    }

Upvotes: 10

Views: 4451

Answers (4)

Lars
Lars

Reputation: 1361

The answer by DHansen above is close, but for me (using Hibernate) this solved the issue:

if (!Hibernate.getClass(this).equals(Hibernate.getClass(obj))) { return false; } as suggested by Dr. Hans-Peter Störr

Also it is important to always use 'getters' as suggested by Willem de Wit above.

Upvotes: 1

DHansen
DHansen

Reputation: 345

I don't have reputation to comment the Willem de Wit answer. Than I need to post a new answer.

To solve the issue of djechelon, you should replace this line:

if (!getClass().isAssignableFrom(obj.getClass()))

for

if ( !obj.getClass().isAssignableFrom(getClass()) && !getClass().isAssignableFrom(obj.getClass()) )

Then you will assure that the equals will work for all scenarios (Type-Type, Type-Proxy, Proxy-Type and Proxy-Proxy).

I don't have reputation to vote up your answer too. I'm so miserable!

Upvotes: 10

Willem de Wit
Willem de Wit

Reputation: 8732

I stumbled on the same problem. The way I fixed is was to change the .equals-method.

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (!getClass().isAssignableFrom(obj.getClass()))
        return false;
    AbstractEntity other = (AbstractEntity) obj;
    if (getId() == null) {
        if (other.getId() != null)
            return false;
    } else if (!getId().equals(other.getId()))
        return false;
    return true;

The trick is to not compare the classes to be the same but use the isAssignableFrom-method. The other trick is to not use direct properties (other.id), but use the get-method (other.getId())

Upvotes: 11

Doron Manor
Doron Manor

Reputation: 596

You can do two things: 1. Change the equals to use instanceof instead of class equality. The type of the proxy is not equal to the type of the entity, but rather extends the type of the entity.

  1. Unwrap the proxy to get the entity itself (there are several hibernate tools that helps you do that)

Upvotes: 1

Related Questions