Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

  • extend the equals implementation to take into account the proxy case. Cons: maintainability toll, hardwired dependency to Hibernate proxy infrastructure, hacky, entity or domain models should be agnostic to the ORM being used i.e. since they might be reused in different contexts where there is no need for ORM e.g. Swing UI.
  • check whether it is a proxy before invoking equals. Cons: not always possible, i.e., dealing with Collections and implicit invocations of equals, e.g., Map.
  • Refrain from using lazy loading. Cons: not reasonable nor efficient in all use-cases.

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);
    }
like image 768
SkyWalker Avatar asked Dec 24 '12 18:12

SkyWalker


People also ask

What is Hibernate proxy object?

Hibernate proxy is a reference of an object that might not actually exists. In other words it's a fake object.

Which method returns proxy object in Hibernate?

Explanation: load() method returns proxy object.

What is proxy pattern in Hibernate?

Proxy pattern is used when you do not want to expose the actual object, the reason can be. You want to hide the complexities of actual object. For example if the under laying object is handling data over network then you may want to create proxy object which will handle network related issues gracefully.

What is JPA proxy?

The JPA lazy loading mechanism can either be implemented using Proxies or Bytecode Enhancement so that calls to lazy associations can be intercepted and relationships initialized prior to returning the result back to the caller.


1 Answers

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())

like image 114
Willem de Wit Avatar answered Nov 15 '22 19:11

Willem de Wit