Hibernate equals and proxy

后端 未结 4 1828
忘了有多久
忘了有多久 2020-12-29 09:12

I have one BaseEntity which abstracts id and version property. this class also implements hashcode and equals based on PK (id) property.

BaseEntity{

    Lo         


        
相关标签:
4条回答
  • 2020-12-29 09:51

    To be able to lazy-load the a.b association, Hibernate sets the b field in a to a proxy. The proxy is an instance of a class that extends B, but is not B. So, your equals() method will always fail when comparing a non-proxy B instance to a proxy B instance, because it compares the classes of both objects:

    if (getClass() != obj.getClass())
        return false;
    

    In the case of Hibernate entities, you should replace this with

    if (!(obj instanceof B)) {
        return false;
    }
    

    Also, note that

    • Hibernate recommends not implementing equals() and hashCode() by using the ID, but rather by using a natural identifier. Implementing it with IDs can cause problems because entities don't have an ID until they're saved and the ID is generated
    • When using entity inheritance, the problem is even worse. Suppose B is a superclass of two sub-entities B1 and B2. Hiberante can't know which type (B1 or B2) is a.b before loading it. So a.b will be initialized to a proxy which is a subclass of B, but is not a subclass of B1 or B2. So the hashCode() and equals() methods should be implemented in B, but not be overridden in B1 and B2. Two B instances should be considered equal if they're instances of B, and have the same identifier.
    0 讨论(0)
  • 2020-12-29 09:51

    You can also make it works this way, usefull if you don't know wich instance is B (may happen if your equals is in a superclass)

    if (HibernateProxyHelper.getClassWithoutInitializingProxy(this) != HibernateProxyHelper.getClassWithoutInitializingProxy(obj)) 
        return false
    
    0 讨论(0)
  • 2020-12-29 09:52

    I use Hibernate.getClass for many years and I never noticed a problem:

    @Override    
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (Hibernate.getClass(this) != Hibernate.getClass(obj)) {
            return false;
        }
    
        ... check for values
    
        return true;
    }
    
    0 讨论(0)
  • 2020-12-29 10:10

    This is mostly an effect of standard Java inheritance.

    a1.getB().equals(b1) uses Object.equals() (except if you've overridden equals() in your class), which only returns true if a1.getB() and b1 are the same instance. I don't know what you've done exactly (your code formatting is broken), but it looks like you've loaded a again in a different session, so you get a new instance for a and a.getB(), and consequently Object.equals() returns false.

    a1.getB().getId().equals(b1.getId()) uses Long.equals(), which returns true if the long values are the same (even for different instances of the Long object), and these values are obviously the same.

    0 讨论(0)
提交回复
热议问题